From 0d4e5cbdd5c68a52cbdb1cf22b2924c56d12492b Mon Sep 17 00:00:00 2001 From: Zamralik Date: Thu, 15 Jan 2026 03:58:03 +0100 Subject: [PATCH 01/71] chore: fork initialization --- .gitignore | 258 ++++++++++++++++- README.md | 30 +- jest.config.js | 12 - package.json | 61 ++-- pnpm-lock.yaml | 352 +++++++++++++++++++++++ tsconfig.json | 75 +++-- vitruvius-labs-lua-engine.code-workspace | 64 +++++ 7 files changed, 783 insertions(+), 69 deletions(-) delete mode 100644 jest.config.js create mode 100644 pnpm-lock.yaml create mode 100644 vitruvius-labs-lua-engine.code-workspace diff --git a/.gitignore b/.gitignore index b8ffe08..975245a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,259 @@ +# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudiocode,git,node +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,visualstudiocode,git,node + +### Custom ## +build/ +reports/ +tmp/ +*.crt +*.key + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories node_modules/ -package-lock.json +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets +!.vscode/cspell.json + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk +# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudiocode,git,node diff --git a/README.md b/README.md index e728be6..6909189 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # TSLua -A pure TypeScript reimplementation of the Lua programming language. +A pure TypeScript implementation of a Lua programming language interpreter. This implementation is designed to be as close as possible to the original C implementation, while providing seamless TypeScript/JavaScript interoperability. All 'native' functions are implemented in TypeScript and objects are represented using standard JavaScript objects. Meaning garbage collection is left as the responsibility of the JavaScript engine. @@ -10,10 +10,10 @@ To create a Lua runtime environment, simply instantiate the `Engine` object with Global state is maintained for the whole lifetime of the `Engine` object. Lua variables can be queried by name using the `global` method. `define` and `define_table` can also be used to interact with the global object. An example is shown below: ```ts - const engine = lua.Engine('a = 1 + 2') + const engine = lua.Engine("a = 1 + 2") engine.run() - const a = engine.global('a')?.number + const a = engine.global("a")?.number console.log(a) // --> 3 ``` @@ -86,10 +86,10 @@ The following *keywords* are reserved and cannot be used as names: ```lua and break do else elseif - end false for function goto - if in local nil not - or repeat return then true - until while + end false for function global + goto if in local nil + not or repeat return then + true until while ``` Lua is a case-sensitive language: `and` is a reserved word, but `And`and `AND` are two different, valid names. As a convention, programs should avoid creating names that start with an underscore followed by one or more uppercase letters (such as `_VERSION`). @@ -132,7 +132,7 @@ Examples of valid integer constants are ```lua 3 - 345 + 345 0xff 0xBEBADA ``` @@ -140,7 +140,7 @@ Examples of valid integer constants are Examples of valid float constants are ```lua - 3.0 + 3.0 3.1416 314.16e-2 0.31416E1 @@ -589,9 +589,9 @@ As usual, you can use parentheses to change the precedences of an expression. Th Table constructors are expressions that create tables. Every time a constructor is evaluated, a new table is created. A constructor can be used to create an empty table or to create a table and initialize some of its fields. The general syntax for constructors is ```lua - tableconstructor ::= ‘{’ [fieldlist] ‘}’ - fieldlist ::= field {fieldsep field} [fieldsep] - field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp + tableconstructor ::= ‘{’ [fieldlist] ‘}’ + fieldlist ::= field {fieldsep field} [fieldsep] + field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp fieldsep ::= ‘,’ | ‘;’ ``` @@ -1131,11 +1131,11 @@ A *character class* is used to represent a set of characters. The following comb - **`%x`:** represents all hexadecimal digits. - **`%x`:** (where *x* is any non-alphanumeric character) represents the character *x*. This is the standard way to escape the magic characters. Any non-alphanumeric character (including all punctuation characters, even the non-magical) can be preceded by a '`%`' to represent itself in a pattern. - **`[set]`:** represents the class which is the union of all characters in *set*. A range of characters can be specified by separating the end characters of the range, in ascending order, with a '`-`'. All classes `%`*x* described above can also be used as components in *set*. All other characters in *set* represent themselves. For example, `[%w_]` (or `[_%w]`) represents all alphanumeric characters plus the underscore, `[0-7]` represents the octal digits, and `[0-7%l%-]` represents the octal digits plus the lowercase letters plus the '`-`' character. - + You can put a closing square bracket in a set by positioning it as the first character in the set. You can put a hyphen in a set by positioning it as the first or the last character in the set. (You can also use an escape for both cases.) - + The interaction between ranges and classes is not defined. Therefore, patterns like `[%a-z]` or `[a-%%]` have no meaning. - + - **`[^set]`:** represents the complement of *set*, where *set* is interpreted as above. For all classes represented by single letters (`%a`, `%c`, etc.), the corresponding uppercase letter represents the complement of the class. For instance, `%S` represents all non-space characters. diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 463894e..0000000 --- a/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -} - diff --git a/package.json b/package.json index 4dc3838..34d0f92 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,46 @@ { - "name": "tslua", - "version": "1.0.0", - "description": "", - "scripts": { - "test": "jest" - }, - "files": [ - "index.ts" - ], - "author": "", - "license": "ISC", - "devDependencies": { - "@types/jest": "^29.5.3", - "jest": "^29.6.1", - "ts-jest": "^29.1.1", - "typescript": "^5.1.6" - } + "$schema": "https://json.schemastore.org/package.json", + "name": "lua-engine", + "version": "1.0.0", + "description": "A TypeScript implementation of a Lua interpreter", + "license": "ISC", + "author": { + "name": "VitruviusLabs" + }, + "contributors": [ + "Nicolas \"SmashingQuasar\" Lebacq ", + "Benjamin Blum ", + "Ben Jilks " + ], + "keywords": [ + "lua" + ], + "homepage": "https://github.com/VitruviusLabs/lua-engine#readme", + "bugs": "https://github.com/VitruviusLabs/lua-engine/issues", + "repository": { + "type": "git", + "url": "https://github.com/VitruviusLabs/lua-engine.git" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "engines": { + "node": ">=18.0.0" + }, + "type": "module", + "files": [ + "src/index.mts" + ], + "scripts": { + "ts:check": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@vitruvius-labs/ts-predicate": "^7.0.0" + }, + "devDependencies": { + "@types/node": "^22.14.1", + "tsx": "^4.19.3", + "typescript": "^5.8.3" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..dacb11e --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,352 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@vitruvius-labs/ts-predicate': + specifier: ^7.0.0 + version: 7.0.0 + devDependencies: + '@types/node': + specifier: ^22.14.1 + version: 22.19.6 + tsx: + specifier: ^4.19.3 + version: 4.21.0 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@types/node@22.19.6': + resolution: {integrity: sha512-qm+G8HuG6hOHQigsi7VGuLjUVu6TtBo/F05zvX04Mw2uCg9Dv0Qxy3Qw7j41SidlTcl5D/5yg0SEZqOB+EqZnQ==} + + '@vitruvius-labs/ts-predicate@7.0.0': + resolution: {integrity: sha512-lnWz0W2BeI/LZh6AGrOhC+SrQ74IeG2hUuT8BUXN9Q0nNj1cZ63e3a/uFd5/guqAqowQaNyHx4mP61CW2DybjQ==} + engines: {node: '>=18.0.0'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + +snapshots: + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@types/node@22.19.6': + dependencies: + undici-types: 6.21.0 + + '@vitruvius-labs/ts-predicate@7.0.0': {} + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + resolve-pkg-maps@1.0.0: {} + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} diff --git a/tsconfig.json b/tsconfig.json index eae2e17..87df1a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,53 @@ { - "compilerOptions": - { - "module": "commonjs", - "esModuleInterop": true, - "moduleResolution": "node", - "target": "es2017", - - "noImplicitAny": true, - "removeComments": true, - "allowUnreachableCode": false, - "strictNullChecks": true, - "incremental": true - }, - - "lib": - [ - "es2017" - ], - - "files": - [ - "index.ts" - ] + "$schema": "https://json.schemastore.org/tsconfig", + "compileOnSave": false, + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "pretty": true, + "declaration": false, + "importHelpers": true, + "newLine": "LF", + "preserveConstEnums": true, + "sourceMap": false, + "allowJs": false, + "checkJs": false, + "noStrictGenericChecks": false, + "allowSyntheticDefaultImports": false, + "forceConsistentCasingInFileNames": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noErrorTruncation": true, + "target": "ESNext", + "lib": [ + "ESNext" + ], + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "useUnknownInCatchVariables": true + }, + "include": [ + "./src/index.mts" + ], + "exclude": [ + "./node_modules" + ] } - diff --git a/vitruvius-labs-lua-engine.code-workspace b/vitruvius-labs-lua-engine.code-workspace new file mode 100644 index 0000000..63a4bb5 --- /dev/null +++ b/vitruvius-labs-lua-engine.code-workspace @@ -0,0 +1,64 @@ +{ + "folders": [ + { + "name": "pnpm-workspace", + "path": "." + }, + ], + "settings": { + // Formatting + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "editor.detectIndentation": false, + "editor.indentSize": "tabSize", + "editor.insertSpaces": false, + // Formatting overrides + "[yaml]": { + "editor.insertSpaces": true + }, + "[shellscript]": { + "editor.insertSpaces": true + }, + // Linters & prettier extensions settings + "eslint.validate": [ + "javascript", + "typescript" + ], + "eslint.useFlatConfig": true, + "eslint.options": { + "overrideConfigFile": "./eslint.config.mjs" + }, + "eslint.workingDirectories": [ + "./packages/architectura", + "./packages/aws-s3", + "./packages/aws-signature-v4", + "./packages/aws-sqs", + "./packages/functional", + "./packages/mockingbird", + "./packages/testing-ground", + "./packages/toolbox", + "./packages/ts-predicate" + ], + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.fixAll.tslint": "never" + }, + "eslint.format.enable": true, + // Code spell checker extension settings + "cSpell.enabled": true, + // UX + "explorer.fileNesting.enabled": true, + "search.exclude": { + "**/dist": true, + "**/lib": true, + "**/.yarn": true + }, + // TypeScript + "js/ts.implicitProjectConfig.target": "ESNext", + "typescript.tsdk": "./node_modules/typescript/bin", + "typescript.tsserver.maxTsServerMemory": 4096, + "typescript.preferences.quoteStyle": "double", + "prettier.enable": false, + } +} From 36ad869db441c75c5a2954cace7108404ade11ef Mon Sep 17 00:00:00 2001 From: Zamralik Date: Thu, 15 Jan 2026 04:00:22 +0100 Subject: [PATCH 02/71] chore: change extensions from .ts to .mts --- src/{ast.ts => ast.mts} | 0 src/{compiler.ts => compiler.mts} | 23 ++++++++------- src/{engine.ts => engine.mts} | 29 ++++++++++--------- index.ts => src/index.mts | 0 src/{lexer.ts => lexer.mts} | 0 src/{lib.ts => lib.mts} | 17 ++++++------ src/{opcode.ts => opcode.mts} | 5 ++-- src/{optimizer.ts => optimizer.mts} | 0 src/{parser.ts => parser.mts} | 43 ++++++++++++++--------------- src/{runtime.ts => runtime.mts} | 3 +- 10 files changed, 57 insertions(+), 63 deletions(-) rename src/{ast.ts => ast.mts} (100%) rename src/{compiler.ts => compiler.mts} (99%) rename src/{engine.ts => engine.mts} (97%) rename index.ts => src/index.mts (100%) rename src/{lexer.ts => lexer.mts} (100%) rename src/{lib.ts => lib.mts} (99%) rename src/{opcode.ts => opcode.mts} (97%) rename src/{optimizer.ts => optimizer.mts} (100%) rename src/{parser.ts => parser.mts} (98%) rename src/{runtime.ts => runtime.mts} (96%) diff --git a/src/ast.ts b/src/ast.mts similarity index 100% rename from src/ast.ts rename to src/ast.mts diff --git a/src/compiler.ts b/src/compiler.mts similarity index 99% rename from src/compiler.ts rename to src/compiler.mts index c8883ab..a420900 100644 --- a/src/compiler.ts +++ b/src/compiler.mts @@ -6,14 +6,14 @@ import type { Chunk, Expression, Value } from './ast' import type { IfBlock, While, For, NumericFor, Repeat, Do } from './ast' -import type { Op, Program } from './opcode' -import type { Token } from './lexer' +import type { Op, Program } from './opcode.mts' +import type { Token } from './lexer.mts' import type { Assignment, Local, Return } from './ast' import { StatementKind, ExpressionKind, ValueKind } from './ast' -import { OpCode } from './opcode' -import { DataType } from './runtime' -import { make_boolean, make_number, make_string, nil } from './runtime' +import { OpCode } from './opcode.mts' +import { DataType } from './runtime.mts' +import { make_boolean, make_number, make_string, nil } from './runtime.mts' function compile_function(chunk: Chunk, token: Token, parameters: Token[], functions: Op[][]): number { @@ -87,7 +87,7 @@ function compile_value(value: Value | undefined, functions: Op[][]): Op[] debug: debug, }] } - + default: throw new Error() } @@ -114,7 +114,7 @@ function compile_call(func: Expression | undefined, { if (func == undefined || args == undefined) throw new Error() - + const debug = func.token.debug const ops: Op[] = [] for (const arg of args) @@ -131,7 +131,7 @@ function compile_index(target: Expression | undefined, { if (target == undefined || index == undefined) throw new Error() - + const ops: Op[] = [] ops.push(...compile_expression(index, functions)) ops.push(...compile_expression(target, functions)) @@ -230,7 +230,7 @@ function compile_assignment(assignment: Assignment | undefined, functions: Op[][ { if (assignment == undefined) throw new Error() - + const ops: Op[] = [] const debug = assignment.token.debug ops.push({ code: OpCode.StartStackChange, debug: debug }) @@ -247,13 +247,13 @@ function compile_assignment(assignment: Assignment | undefined, functions: Op[][ { if (lhs.value?.kind != ValueKind.Variable) throw new Error() - + const identifier = make_string(lhs.value?.identifier ?? '') if (assignment.local) ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }) ops.push({ code: OpCode.Store, arg: identifier, debug: debug }) break - } + } case ExpressionKind.Index: { @@ -683,4 +683,3 @@ export function compile(chunk: Chunk, extend?: Op[]): Program start: start, } } - diff --git a/src/engine.ts b/src/engine.mts similarity index 97% rename from src/engine.ts rename to src/engine.mts index 00774e5..6a5d996 100644 --- a/src/engine.ts +++ b/src/engine.mts @@ -4,16 +4,16 @@ * SPDX-License-Identifier: BSD-2-Clause */ -import type { Op } from './opcode' -import type { NativeFunction, Variable } from './runtime' +import type { Op } from './opcode.mts' +import type { NativeFunction, Variable } from './runtime.mts' -import { OpCode, op_code_name } from './opcode' -import { DataType, nil, make_number, make_boolean, make_string } from './runtime' -import { TokenStream } from './lexer' -import { parse } from './parser' -import { compile } from './compiler' -import { optimize_chunk } from './optimizer' -import * as std from './lib' +import { OpCode, op_code_name } from './opcode.mts' +import { DataType, nil, make_number, make_boolean, make_string } from './runtime.mts' +import { TokenStream } from './lexer.mts' +import { parse } from './parser.mts' +import { compile } from './compiler.mts' +import { optimize_chunk } from './optimizer.mts' +import * as std from './lib.mts' function index(val: Variable | undefined): string | number | undefined { @@ -206,7 +206,7 @@ export class Engine this.stack.push(arg) this.stack.push(make_number(args.length)) this.locals_stack.push(new Map()) - + const result = this.run() const return_values = this.stack this.stack = old_stack @@ -313,7 +313,7 @@ export class Engine } case OpCode.Dup: - { + { const count = arg?.number ?? 1 const items = this.stack.splice(this.stack.length - count, count) this.stack.push(...items, ...items) @@ -321,7 +321,7 @@ export class Engine } case OpCode.Swap: - { + { const x = this.stack.splice(this.stack.length - 2, 1) this.stack.push(...x) break @@ -407,7 +407,7 @@ export class Engine const [x, y] = [this.stack.pop(), this.stack.pop()] this.stack.push(make_boolean(is_true(x) && is_true(y))) break - } + } case OpCode.Or: { @@ -609,7 +609,7 @@ export class Engine if (this.ip >= this.program.length) return - const op = this.program[this.ip++] + const op = this.program[this.ip++] if (options?.trace || options?.trace_instructions) { const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' @@ -625,4 +625,3 @@ export class Engine } } - diff --git a/index.ts b/src/index.mts similarity index 100% rename from index.ts rename to src/index.mts diff --git a/src/lexer.ts b/src/lexer.mts similarity index 100% rename from src/lexer.ts rename to src/lexer.mts diff --git a/src/lib.ts b/src/lib.mts similarity index 99% rename from src/lib.ts rename to src/lib.mts index 65a0a67..3032a48 100644 --- a/src/lib.ts +++ b/src/lib.mts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ -import type { Variable } from './runtime' -import { Engine } from './engine' -import { DataType, make_boolean, make_number, make_string, nil } from './runtime' +import type { Variable } from './runtime.mts' +import { Engine } from './engine.mts' +import { DataType, make_boolean, make_number, make_string, nil } from './runtime.mts' let rand = xoroshiro([0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100)))) @@ -34,7 +34,7 @@ export function variable_to_string(variable: Variable, tables_done: Variable[] = case DataType.String: return variable.string ?? '' case DataType.Function: return `` case DataType.NativeFunction: return `` - case DataType.Table: + case DataType.Table: { if (tables_done.includes(variable)) return '...' @@ -113,7 +113,7 @@ function next(_: Engine, { table }: Variable, index?: Variable): Variable[] let next_key: number | string | undefined = keys.next().value while (next_key != undefined && next_key != index.number && next_key != index.string) next_key = keys.next().value - + next_key = keys.next().value if (next_key == undefined) return [nil] @@ -639,13 +639,13 @@ function math_randomseed(_: Engine, x?: Variable, y?: Variable): Variable[] seed[0] = BigInt(x.number & 0xFFFFFFFF) seed[1] = BigInt((x.number << 32) & 0xFFFFFFFF) } - + if (y?.number != undefined) { seed[2] = BigInt(y.number & 0xFFFFFFFF) seed[3] = BigInt((y.number << 32) & 0xFFFFFFFF) } - + rand = xoroshiro(seed) return [nil] } @@ -766,7 +766,7 @@ export function std_lib(): Map ['tointeger', { data_type: DataType.NativeFunction, native_function: math_tointeger }], ['type', { data_type: DataType.NativeFunction, native_function: math_type }], ['ult', { data_type: DataType.NativeFunction, native_function: math_ult }], - + ['pi', { data_type: DataType.Number, number: Math.PI }], ['maxinteger', { data_type: DataType.Number, number: 0xFFFFFFFF }], ['mininteger', { data_type: DataType.Number, number: -(0xFFFFFFFF - 1) }], @@ -777,4 +777,3 @@ export function std_lib(): Map global.set('_G', { data_type: DataType.Table, table: global }) return global } - diff --git a/src/opcode.ts b/src/opcode.mts similarity index 97% rename from src/opcode.ts rename to src/opcode.mts index 6ade1a3..ddb9b8c 100644 --- a/src/opcode.ts +++ b/src/opcode.mts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ -import type { Variable } from './runtime' -import type { Debug } from './lexer' +import type { Variable } from './runtime.mts' +import type { Debug } from './lexer.mts' export enum OpCode { Load, @@ -137,4 +137,3 @@ export interface Program { code: Op[], start: number, } - diff --git a/src/optimizer.ts b/src/optimizer.mts similarity index 100% rename from src/optimizer.ts rename to src/optimizer.mts diff --git a/src/parser.ts b/src/parser.mts similarity index 98% rename from src/parser.ts rename to src/parser.mts index aa01cb9..66e13cc 100644 --- a/src/parser.ts +++ b/src/parser.mts @@ -6,10 +6,10 @@ import type { Chunk, IfElseBlock } from './ast' import type { Expression, Statement, Value } from './ast' -import type { Token } from './lexer' +import type { Token } from './lexer.mts' import { ExpressionKind, StatementKind, ValueKind } from './ast' -import { TokenKind, TokenStream, token_kind_to_string } from './lexer' +import { TokenKind, TokenStream, token_kind_to_string } from './lexer.mts' const UNARY = [ TokenKind.Not, @@ -42,7 +42,7 @@ function expect(stream: TokenStream, kind: TokenKind): Token | Error const token = stream.peek() if (token.kind != kind) { - return error(token, + return error(token, `expected '${ token_kind_to_string(kind) }', ` + `got '${ token_kind_to_string(token.kind) }' instead`) } @@ -157,20 +157,20 @@ function parse_value(stream: TokenStream): Value | Error const token = stream.peek() switch (token.kind) { - case TokenKind.NumberLiteral: - return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } + case TokenKind.NumberLiteral: + return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } case TokenKind.BooleanLiteral: - return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data == 'true' } + return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data == 'true' } case TokenKind.StringLiteral: - return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } + return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } case TokenKind.NilLiteral: - return { kind: ValueKind.NilLiteral, token: stream.next() } + return { kind: ValueKind.NilLiteral, token: stream.next() } case TokenKind.Identifier: return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data } - + case TokenKind.SquiglyOpen: return parse_table(stream) - case TokenKind.Function: + case TokenKind.Function: return parse_function_value(stream.next(), stream) default: @@ -253,7 +253,7 @@ function parse_call(func: Expression, stream: TokenStream): Expression | Error break } - const close_brace = expect(stream, TokenKind.CloseBrace) + const close_brace = expect(stream, TokenKind.CloseBrace) if (close_brace instanceof Error) return close_brace @@ -330,7 +330,7 @@ function parse_access_expression(expression: Expression, stream: TokenStream): E case TokenKind.OpenSquare: return parse_index(expression, stream) - + case TokenKind.Dot: return parse_dot(expression, stream) @@ -421,7 +421,7 @@ function parse_local_statement(local: Token, values: Expression[]): Statement | names.push(value.token) } - return { + return { kind: StatementKind.Local, local: { token: local, @@ -536,7 +536,7 @@ function parse_if(stream: TokenStream): Statement | Error const then = expect(stream, TokenKind.Then) if (then instanceof Error) return then - + const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End) if (body instanceof Error) return body @@ -548,7 +548,7 @@ function parse_if(stream: TokenStream): Statement | Error const condition = parse_expression(stream) if (condition instanceof Error) return condition - + const then = expect(stream, TokenKind.Then) if (then instanceof Error) return then @@ -571,7 +571,7 @@ function parse_if(stream: TokenStream): Statement | Error return chunk else_body = chunk } - + const end = expect(stream, TokenKind.End) if (end instanceof Error) return end @@ -601,7 +601,7 @@ function parse_while(stream: TokenStream): Statement | Error const do_token = expect(stream, TokenKind.Do) if (do_token instanceof Error) return do_token - + const body = parse(stream, TokenKind.End) if (body instanceof Error) return body @@ -679,7 +679,7 @@ function parse_for(stream: TokenStream): Statement | Error if (consume(stream, TokenKind.Assign)) return parse_numeric_for(items[0], stream) - + const in_token = expect(stream, TokenKind.In) if (in_token instanceof Error) return in_token @@ -725,7 +725,7 @@ function parse_repeat(stream: TokenStream): Statement | Error const condition = parse_expression(stream) if (condition instanceof Error) return condition - + return { kind: StatementKind.Repeat, repeat: { @@ -741,7 +741,7 @@ function parse_do(stream: TokenStream): Statement | Error const do_token = expect(stream, TokenKind.Do) if (do_token instanceof Error) return do_token - + const body = parse(stream, TokenKind.End) if (body instanceof Error) return body @@ -863,7 +863,7 @@ function parse_function(stream: TokenStream): Statement | Error if (consume(stream, TokenKind.Dot)) return parse_local_function(name, stream) - + const function_value = parse_function_value(name, stream) if (function_value instanceof Error) return function_value @@ -950,4 +950,3 @@ export function parse(stream: TokenStream, ...end_tokens: TokenKind[]): Chunk | return chunk } - diff --git a/src/runtime.ts b/src/runtime.mts similarity index 96% rename from src/runtime.ts rename to src/runtime.mts index 1cb104b..2476e8a 100644 --- a/src/runtime.ts +++ b/src/runtime.mts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -import { Engine } from './engine' +import { Engine } from './engine.mts' export enum DataType { Nil, @@ -45,4 +45,3 @@ export function make_string(string: string): Variable { return { data_type: DataType.String, string: string } } - From c1e5039ce267e6a0b0ca0a36c93570409b7e9dbb Mon Sep 17 00:00:00 2001 From: Zamralik Date: Thu, 15 Jan 2026 04:19:19 +0100 Subject: [PATCH 03/71] chore: convert indentation to tabs --- src/ast.mts | 231 ++++--- src/compiler.mts | 1122 ++++++++++++++++----------------- src/engine.mts | 1178 +++++++++++++++++------------------ src/index.mts | 17 +- src/lexer.mts | 1279 +++++++++++++++++++------------------- src/lib.mts | 928 ++++++++++++++-------------- src/opcode.mts | 220 +++---- src/optimizer.mts | 555 +++++++++-------- src/parser.mts | 1502 ++++++++++++++++++++++----------------------- src/runtime.mts | 38 +- 10 files changed, 3533 insertions(+), 3537 deletions(-) diff --git a/src/ast.mts b/src/ast.mts index d6d16e0..6386bdb 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -7,173 +7,172 @@ import type { Token } from './lexer' export enum ValueKind { - NilLiteral, - NumberLiteral, - BooleanLiteral, - StringLiteral, - TableLiteral, - Function, - Variable, + NilLiteral, + NumberLiteral, + BooleanLiteral, + StringLiteral, + TableLiteral, + Function, + Variable, } export interface LuaFunction { - parameters: Token[], - body: Chunk, + parameters: Token[], + body: Chunk, } export interface Value { - kind: ValueKind, - token: Token, + kind: ValueKind, + token: Token, - number?: number, - boolean?: boolean, - string?: string, - table?: Map, - function?: LuaFunction, - identifier?: string, + number?: number, + boolean?: boolean, + string?: string, + table?: Map, + function?: LuaFunction, + identifier?: string, } export enum ExpressionKind { - Value, - Call, - Index, - - Addition, - Subtract, - Multiplication, - Division, - FloorDivision, - Modulo, - Exponent, - Concat, - - BitAnd, - BitOr, - BitXOr, - BitNot, - BitShiftLeft, - BitShiftRight, - - Equals, - NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, - And, - Or, - - Not, - Negate, - Length, + Value, + Call, + Index, + + Addition, + Subtract, + Multiplication, + Division, + FloorDivision, + Modulo, + Exponent, + Concat, + + BitAnd, + BitOr, + BitXOr, + BitNot, + BitShiftLeft, + BitShiftRight, + + Equals, + NotEquals, + LessThen, + LessThenEquals, + GreaterThen, + GreaterThenEquals, + And, + Or, + + Not, + Negate, + Length, } export interface Expression { - kind: ExpressionKind, - token: Token, + kind: ExpressionKind, + token: Token, - lhs?: Expression, - rhs?: Expression, - value?: Value, - expression?: Expression, - index?: Expression, - arguments?: Expression[], + lhs?: Expression, + rhs?: Expression, + value?: Value, + expression?: Expression, + index?: Expression, + arguments?: Expression[], } export interface Assignment { - local: boolean, - lhs: Expression[], - rhs: Expression[], - token: Token, + local: boolean, + lhs: Expression[], + rhs: Expression[], + token: Token, } export interface Local { - names: Token[], - token: Token, + names: Token[], + token: Token, } export interface IfElseBlock { - body: Chunk, - condition: Expression, - token: Token, + body: Chunk, + condition: Expression, + token: Token, } export interface IfBlock { - condition: Expression, - body: Chunk, - else_if_bodies: IfElseBlock[], - else_body?: Chunk, - token: Token, + condition: Expression, + body: Chunk, + else_if_bodies: IfElseBlock[], + else_body?: Chunk, + token: Token, } export interface While { - condition: Expression, - body: Chunk, - token: Token, + condition: Expression, + body: Chunk, + token: Token, } export interface For { - items: Token[], - itorator: Expression, - body: Chunk, - token: Token, + items: Token[], + itorator: Expression, + body: Chunk, + token: Token, } export interface NumericFor { - index: Token, - start: Expression, - end: Expression, - step: Expression | undefined, - body: Chunk, + index: Token, + start: Expression, + end: Expression, + step: Expression | undefined, + body: Chunk, } export interface Repeat { - body: Chunk, - condition: Expression, - token: Token, + body: Chunk, + condition: Expression, + token: Token, } export interface Do { - body: Chunk, - token: Token, + body: Chunk, + token: Token, } export interface Return { - values: Expression[], - token: Token, + values: Expression[], + token: Token, } export enum StatementKind { - Invalid, - Empty, - Expression, - Assignment, - Local, - If, - While, - For, - NumericFor, - Repeat, - Do, - Return, - Break, + Invalid, + Empty, + Expression, + Assignment, + Local, + If, + While, + For, + NumericFor, + Repeat, + Do, + Return, + Break, } export interface Statement { - kind: StatementKind, - expression?: Expression, - assignment?: Assignment, - local?: Local, - if?: IfBlock, - while?: While, - for?: For, - numeric_for?: NumericFor, - repeat?: Repeat, - do?: Do, - return?: Return, + kind: StatementKind, + expression?: Expression, + assignment?: Assignment, + local?: Local, + if?: IfBlock, + while?: While, + for?: For, + numeric_for?: NumericFor, + repeat?: Repeat, + do?: Do, + return?: Return, } export interface Chunk { - statements: Statement[], + statements: Statement[], } - diff --git a/src/compiler.mts b/src/compiler.mts index a420900..cfab135 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -10,676 +10,676 @@ import type { Op, Program } from './opcode.mts' import type { Token } from './lexer.mts' import type { Assignment, Local, Return } from './ast' -import { StatementKind, ExpressionKind, ValueKind } from './ast' +import { StatementKind, ExpressionKind, ValueKind } from './ast' import { OpCode } from './opcode.mts' import { DataType } from './runtime.mts' import { make_boolean, make_number, make_string, nil } from './runtime.mts' function compile_function(chunk: Chunk, token: Token, parameters: Token[], functions: Op[][]): number { - const ops: Op[] = [] - ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }) - for (const parameter of parameters.reverse()) - { - ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }) - ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }) - } - ops.push(...compile_block(chunk, functions)) - ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }) - ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }) - - functions.push(ops) - return functions.length - 1 + const ops: Op[] = [] + ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }) + for (const parameter of parameters.reverse()) + { + ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }) + ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }) + } + ops.push(...compile_block(chunk, functions)) + ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }) + ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }) + + functions.push(ops) + return functions.length - 1 } function compile_value(value: Value | undefined, functions: Op[][]): Op[] { - if (value == undefined) - throw new Error() - - const debug = value.token.debug - switch (value.kind) - { - case ValueKind.NilLiteral: - return [{ code: OpCode.Push, arg: nil, debug: debug }] - case ValueKind.BooleanLiteral: - return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }] - case ValueKind.NumberLiteral: - return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }] - case ValueKind.StringLiteral: - return [{ code: OpCode.Push, arg: make_string(value.string ?? ''), debug: debug }] - - case ValueKind.Function: - { - return [{ - code: OpCode.Push, arg: { - data_type: DataType.Function, - function_id: compile_function( - value.function?.body ?? { statements: [] }, - value.token, - value.function?.parameters ?? [], - functions), - }, - debug: debug, - }] - } - - case ValueKind.TableLiteral: - { - const output: Op[] = [] - output.push({ code: OpCode.NewTable, debug: debug }) - - for (const [key, expression] of [...value.table?.entries() ?? []].reverse()) - { - output.push(...compile_expression(expression, functions)) - output.push(...compile_expression(key, functions)) - } - - output.push({ code: OpCode.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }) - return output - } - - case ValueKind.Variable: - { - return [{ - code: OpCode.Load, - arg: { data_type: DataType.String, string: value.identifier ?? '' }, - debug: debug, - }] - } - - default: - throw new Error() - } + if (value == undefined) + throw new Error() + + const debug = value.token.debug + switch (value.kind) + { + case ValueKind.NilLiteral: + return [{ code: OpCode.Push, arg: nil, debug: debug }] + case ValueKind.BooleanLiteral: + return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }] + case ValueKind.NumberLiteral: + return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }] + case ValueKind.StringLiteral: + return [{ code: OpCode.Push, arg: make_string(value.string ?? ''), debug: debug }] + + case ValueKind.Function: + { + return [{ + code: OpCode.Push, arg: { + data_type: DataType.Function, + function_id: compile_function( + value.function?.body ?? { statements: [] }, + value.token, + value.function?.parameters ?? [], + functions), + }, + debug: debug, + }] + } + + case ValueKind.TableLiteral: + { + const output: Op[] = [] + output.push({ code: OpCode.NewTable, debug: debug }) + + for (const [key, expression] of [...value.table?.entries() ?? []].reverse()) + { + output.push(...compile_expression(expression, functions)) + output.push(...compile_expression(key, functions)) + } + + output.push({ code: OpCode.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }) + return output + } + + case ValueKind.Variable: + { + return [{ + code: OpCode.Load, + arg: { data_type: DataType.String, string: value.identifier ?? '' }, + debug: debug, + }] + } + + default: + throw new Error() + } } function compile_operation(expression: Expression, - operation: OpCode, - functions: Op[][]): Op[] + operation: OpCode, + functions: Op[][]): Op[] { - const { lhs, rhs } = expression - if (lhs == undefined || rhs == undefined) - throw new Error() - - const ops: Op[] = [] - ops.push(...compile_expression(rhs, functions)) - ops.push(...compile_expression(lhs, functions)) - ops.push({ code: operation, debug: expression.token.debug }) - return ops + const { lhs, rhs } = expression + if (lhs == undefined || rhs == undefined) + throw new Error() + + const ops: Op[] = [] + ops.push(...compile_expression(rhs, functions)) + ops.push(...compile_expression(lhs, functions)) + ops.push({ code: operation, debug: expression.token.debug }) + return ops } function compile_call(func: Expression | undefined, - args: Expression[] | undefined, - functions: Op[][]): Op[] + args: Expression[] | undefined, + functions: Op[][]): Op[] { - if (func == undefined || args == undefined) - throw new Error() - - const debug = func.token.debug - const ops: Op[] = [] - for (const arg of args) - ops.push(...compile_expression(arg, functions)) - ops.push(...compile_expression(func, functions)) - ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }) - ops.push({ code: OpCode.Call, debug: debug }) - return ops + if (func == undefined || args == undefined) + throw new Error() + + const debug = func.token.debug + const ops: Op[] = [] + for (const arg of args) + ops.push(...compile_expression(arg, functions)) + ops.push(...compile_expression(func, functions)) + ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }) + ops.push({ code: OpCode.Call, debug: debug }) + return ops } function compile_index(target: Expression | undefined, - index: Expression | undefined, - functions: Op[][]): Op[] + index: Expression | undefined, + functions: Op[][]): Op[] { - if (target == undefined || index == undefined) - throw new Error() - - const ops: Op[] = [] - ops.push(...compile_expression(index, functions)) - ops.push(...compile_expression(target, functions)) - ops.push({ code: OpCode.LoadIndex, debug: target.token.debug }) - return ops + if (target == undefined || index == undefined) + throw new Error() + + const ops: Op[] = [] + ops.push(...compile_expression(index, functions)) + ops.push(...compile_expression(target, functions)) + ops.push({ code: OpCode.LoadIndex, debug: target.token.debug }) + return ops } function compile_unary_operation(expression: Expression | undefined, - operation: OpCode, - functions: Op[][]): Op[] + operation: OpCode, + functions: Op[][]): Op[] { - if (expression == undefined || expression.expression == undefined) - throw new Error() + if (expression == undefined || expression.expression == undefined) + throw new Error() - const ops: Op[] = [] - ops.push(...compile_expression(expression.expression, functions)) - ops.push({ code: operation, debug: expression.token.debug }) - return ops + const ops: Op[] = [] + ops.push(...compile_expression(expression.expression, functions)) + ops.push({ code: operation, debug: expression.token.debug }) + return ops } function compile_expression(expression: Expression | undefined, functions: Op[][]): Op[] { - if (expression == undefined) - throw new Error() - - switch (expression.kind) - { - case ExpressionKind.Value: - return compile_value(expression.value, functions) - case ExpressionKind.Call: - return compile_call(expression.expression, expression.arguments, functions) - case ExpressionKind.Index: - return compile_index(expression.expression, expression.index, functions) - - case ExpressionKind.Addition: - return compile_operation(expression, OpCode.Add, functions) - case ExpressionKind.Subtract: - return compile_operation(expression, OpCode.Subtract, functions) - case ExpressionKind.Multiplication: - return compile_operation(expression, OpCode.Multiply, functions) - case ExpressionKind.Division: - return compile_operation(expression, OpCode.Divide, functions) - case ExpressionKind.FloorDivision: - return compile_operation(expression, OpCode.FloorDivide, functions) - case ExpressionKind.Modulo: - return compile_operation(expression, OpCode.Modulo, functions) - case ExpressionKind.Exponent: - return compile_operation(expression, OpCode.Exponent, functions) - case ExpressionKind.Concat: - return compile_operation(expression, OpCode.Concat, functions) - - case ExpressionKind.BitAnd: - return compile_operation(expression, OpCode.BitAnd, functions) - case ExpressionKind.BitOr: - return compile_operation(expression, OpCode.BitOr, functions) - case ExpressionKind.BitXOr: - return compile_operation(expression, OpCode.BitXOr, functions) - case ExpressionKind.BitShiftLeft: - return compile_operation(expression, OpCode.BitShiftLeft, functions) - case ExpressionKind.BitShiftRight: - return compile_operation(expression, OpCode.BitShiftRight, functions) - - case ExpressionKind.Equals: - return compile_operation(expression, OpCode.Equals, functions) - case ExpressionKind.NotEquals: - return compile_operation(expression, OpCode.NotEquals, functions) - case ExpressionKind.LessThen: - return compile_operation(expression, OpCode.LessThen, functions) - case ExpressionKind.LessThenEquals: - return compile_operation(expression, OpCode.LessThenEquals, functions) - case ExpressionKind.GreaterThen: - return compile_operation(expression, OpCode.GreaterThen, functions) - case ExpressionKind.GreaterThenEquals: - return compile_operation(expression, OpCode.GreaterThenEquals, functions) - case ExpressionKind.And: - return compile_operation(expression, OpCode.And, functions) - case ExpressionKind.Or: - return compile_operation(expression, OpCode.Or, functions) - - case ExpressionKind.Not: - return compile_unary_operation(expression, OpCode.Not, functions) - case ExpressionKind.Negate: - return compile_unary_operation(expression, OpCode.Negate, functions) - case ExpressionKind.Length: - return compile_unary_operation(expression, OpCode.Length, functions) - case ExpressionKind.BitNot: - return compile_unary_operation(expression, OpCode.BitNot, functions) - - default: - throw new Error() - } + if (expression == undefined) + throw new Error() + + switch (expression.kind) + { + case ExpressionKind.Value: + return compile_value(expression.value, functions) + case ExpressionKind.Call: + return compile_call(expression.expression, expression.arguments, functions) + case ExpressionKind.Index: + return compile_index(expression.expression, expression.index, functions) + + case ExpressionKind.Addition: + return compile_operation(expression, OpCode.Add, functions) + case ExpressionKind.Subtract: + return compile_operation(expression, OpCode.Subtract, functions) + case ExpressionKind.Multiplication: + return compile_operation(expression, OpCode.Multiply, functions) + case ExpressionKind.Division: + return compile_operation(expression, OpCode.Divide, functions) + case ExpressionKind.FloorDivision: + return compile_operation(expression, OpCode.FloorDivide, functions) + case ExpressionKind.Modulo: + return compile_operation(expression, OpCode.Modulo, functions) + case ExpressionKind.Exponent: + return compile_operation(expression, OpCode.Exponent, functions) + case ExpressionKind.Concat: + return compile_operation(expression, OpCode.Concat, functions) + + case ExpressionKind.BitAnd: + return compile_operation(expression, OpCode.BitAnd, functions) + case ExpressionKind.BitOr: + return compile_operation(expression, OpCode.BitOr, functions) + case ExpressionKind.BitXOr: + return compile_operation(expression, OpCode.BitXOr, functions) + case ExpressionKind.BitShiftLeft: + return compile_operation(expression, OpCode.BitShiftLeft, functions) + case ExpressionKind.BitShiftRight: + return compile_operation(expression, OpCode.BitShiftRight, functions) + + case ExpressionKind.Equals: + return compile_operation(expression, OpCode.Equals, functions) + case ExpressionKind.NotEquals: + return compile_operation(expression, OpCode.NotEquals, functions) + case ExpressionKind.LessThen: + return compile_operation(expression, OpCode.LessThen, functions) + case ExpressionKind.LessThenEquals: + return compile_operation(expression, OpCode.LessThenEquals, functions) + case ExpressionKind.GreaterThen: + return compile_operation(expression, OpCode.GreaterThen, functions) + case ExpressionKind.GreaterThenEquals: + return compile_operation(expression, OpCode.GreaterThenEquals, functions) + case ExpressionKind.And: + return compile_operation(expression, OpCode.And, functions) + case ExpressionKind.Or: + return compile_operation(expression, OpCode.Or, functions) + + case ExpressionKind.Not: + return compile_unary_operation(expression, OpCode.Not, functions) + case ExpressionKind.Negate: + return compile_unary_operation(expression, OpCode.Negate, functions) + case ExpressionKind.Length: + return compile_unary_operation(expression, OpCode.Length, functions) + case ExpressionKind.BitNot: + return compile_unary_operation(expression, OpCode.BitNot, functions) + + default: + throw new Error() + } } function compile_assignment(assignment: Assignment | undefined, functions: Op[][]): Op[] { - if (assignment == undefined) - throw new Error() - - const ops: Op[] = [] - const debug = assignment.token.debug - ops.push({ code: OpCode.StartStackChange, debug: debug }) - for (const rhs of assignment.rhs) - ops.push(...compile_expression(rhs, functions)) - ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }) - - for (const lhs of assignment.lhs) - { - const debug = lhs.token.debug - switch (lhs.kind) - { - case ExpressionKind.Value: - { - if (lhs.value?.kind != ValueKind.Variable) - throw new Error() - - const identifier = make_string(lhs.value?.identifier ?? '') - if (assignment.local) - ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }) - ops.push({ code: OpCode.Store, arg: identifier, debug: debug }) - break - } - - case ExpressionKind.Index: - { - // FIXME: Throw error here if `assignment.local` is true. I think? - - ops.push(...compile_expression(lhs.expression, functions)) - ops.push({ code: OpCode.Swap, debug: debug }) - ops.push(...compile_expression(lhs.index, functions)) - ops.push({ code: OpCode.StoreIndex, debug: debug }) - ops.push({ code: OpCode.Pop, debug: debug }) - break - } - - default: - throw new Error() - } - } - - return ops + if (assignment == undefined) + throw new Error() + + const ops: Op[] = [] + const debug = assignment.token.debug + ops.push({ code: OpCode.StartStackChange, debug: debug }) + for (const rhs of assignment.rhs) + ops.push(...compile_expression(rhs, functions)) + ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }) + + for (const lhs of assignment.lhs) + { + const debug = lhs.token.debug + switch (lhs.kind) + { + case ExpressionKind.Value: + { + if (lhs.value?.kind != ValueKind.Variable) + throw new Error() + + const identifier = make_string(lhs.value?.identifier ?? '') + if (assignment.local) + ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }) + ops.push({ code: OpCode.Store, arg: identifier, debug: debug }) + break + } + + case ExpressionKind.Index: + { + // FIXME: Throw error here if `assignment.local` is true. I think? + + ops.push(...compile_expression(lhs.expression, functions)) + ops.push({ code: OpCode.Swap, debug: debug }) + ops.push(...compile_expression(lhs.index, functions)) + ops.push({ code: OpCode.StoreIndex, debug: debug }) + ops.push({ code: OpCode.Pop, debug: debug }) + break + } + + default: + throw new Error() + } + } + + return ops } function compile_local(local: Local | undefined): Op[] { - if (local == undefined) - throw new Error() - - return local.names - .map(name => ({ - code: OpCode.MakeLocal, - arg: { - data_type: DataType.String, - string: name.data, - }, - debug: name.debug, - })) + if (local == undefined) + throw new Error() + + return local.names + .map(name => ({ + code: OpCode.MakeLocal, + arg: { + data_type: DataType.String, + string: name.data, + }, + debug: name.debug, + })) } function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] { - if (condition == undefined) - throw new Error() - - const ops: Op[] = [] - const debug = condition.token.debug - switch (condition.kind) - { - case ExpressionKind.And: - { - const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_conditional_jump(condition.lhs, rhs.length, functions)) - ops.push(...rhs) - break - } - - case ExpressionKind.Or: - { - const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) - ops.push(...rhs) - break - } - - case ExpressionKind.Not: - { - ops.push(...compile_expression(condition.expression, functions)) - break - } - - default: - { - ops.push(...compile_expression(condition, functions)) - ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }) - break - } - } - - return ops + if (condition == undefined) + throw new Error() + + const ops: Op[] = [] + const debug = condition.token.debug + switch (condition.kind) + { + case ExpressionKind.And: + { + const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) + ops.push(...compile_conditional_jump(condition.lhs, rhs.length, functions)) + ops.push(...rhs) + break + } + + case ExpressionKind.Or: + { + const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) + ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) + ops.push(...rhs) + break + } + + case ExpressionKind.Not: + { + ops.push(...compile_expression(condition.expression, functions)) + break + } + + default: + { + ops.push(...compile_expression(condition, functions)) + ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }) + break + } + } + + return ops } function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] { - if (condition == undefined) - throw new Error() - - const ops: Op[] = [] - const debug = condition.token.debug - switch (condition.kind) - { - case ExpressionKind.And: - { - const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) - ops.push(...rhs) - break - } - - case ExpressionKind.Or: - { - const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length, functions)) - ops.push(...rhs) - break - } - - case ExpressionKind.Not: - { - ops.push(...compile_inverted_conditional_jump(condition.expression, jump_by, functions)) - break - } - - default: - { - ops.push(...compile_expression(condition, functions)) - ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }) - break - } - } - - return ops + if (condition == undefined) + throw new Error() + + const ops: Op[] = [] + const debug = condition.token.debug + switch (condition.kind) + { + case ExpressionKind.And: + { + const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) + ops.push(...compile_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) + ops.push(...rhs) + break + } + + case ExpressionKind.Or: + { + const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) + ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length, functions)) + ops.push(...rhs) + break + } + + case ExpressionKind.Not: + { + ops.push(...compile_inverted_conditional_jump(condition.expression, jump_by, functions)) + break + } + + default: + { + ops.push(...compile_expression(condition, functions)) + ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }) + break + } + } + + return ops } function compile_if(if_block: IfBlock | undefined, functions: Op[][]): Op[] { - if (if_block == undefined) - throw new Error() - - const else_chunk: Op[] = [] - if (if_block.else_body != undefined) - else_chunk.push(...compile_block(if_block.else_body, functions)) - - const if_else_chunks: Op[][] = [] - for (const { body, condition, token } of if_block.else_if_bodies.reverse()) - { - const ops: Op[] = [] - const if_else_body = compile_block(body, functions) - ops.push(...compile_conditional_jump(condition, if_else_body.length + 1, functions)) - ops.push(...if_else_body) - - const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: token.debug }) - if_else_chunks.push(ops) - } - - const debug = if_block.token.debug - const ops: Op[] = [] - const body = compile_block(if_block.body, functions) - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)) - ops.push(...body) - - const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: debug }) - for (const if_else_chunk of if_else_chunks) - ops.push(...if_else_chunk) - ops.push(...else_chunk) - ops.push({ code: OpCode.EndBlock, debug: debug }) - - return ops + if (if_block == undefined) + throw new Error() + + const else_chunk: Op[] = [] + if (if_block.else_body != undefined) + else_chunk.push(...compile_block(if_block.else_body, functions)) + + const if_else_chunks: Op[][] = [] + for (const { body, condition, token } of if_block.else_if_bodies.reverse()) + { + const ops: Op[] = [] + const if_else_body = compile_block(body, functions) + ops.push(...compile_conditional_jump(condition, if_else_body.length + 1, functions)) + ops.push(...if_else_body) + + const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length + ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: token.debug }) + if_else_chunks.push(ops) + } + + const debug = if_block.token.debug + const ops: Op[] = [] + const body = compile_block(if_block.body, functions) + ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)) + ops.push(...body) + + const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length + ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: debug }) + for (const if_else_chunk of if_else_chunks) + ops.push(...if_else_chunk) + ops.push(...else_chunk) + ops.push({ code: OpCode.EndBlock, debug: debug }) + + return ops } function replace_breaks(code: Op[], offset_from_end: number) { - for (const [i, op] of code.entries()) - { - if (op.code == OpCode.Break) - { - const offset = code.length - i - 1 + offset_from_end - op.code = OpCode.Jump - op.arg = make_number(offset) - } - } + for (const [i, op] of code.entries()) + { + if (op.code == OpCode.Break) + { + const offset = code.length - i - 1 + offset_from_end + op.code = OpCode.Jump + op.arg = make_number(offset) + } + } } function compile_while(while_block: While | undefined, functions: Op[][]): Op[] { - if (while_block == undefined) - throw new Error() - - const debug = while_block.token.debug - const ops: Op[] = [] - const body = compile_block(while_block.body, functions) - replace_breaks(body, 1) - - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_conditional_jump(while_block.condition, body.length + 1, functions)) - ops.push(...body) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length - 1), debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + if (while_block == undefined) + throw new Error() + + const debug = while_block.token.debug + const ops: Op[] = [] + const body = compile_block(while_block.body, functions) + replace_breaks(body, 1) + + ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push(...compile_conditional_jump(while_block.condition, body.length + 1, functions)) + ops.push(...body) + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length - 1), debug: debug }) + ops.push({ code: OpCode.EndBlock, debug: debug }) + return ops } function compile_for(for_block: For | undefined, functions: Op[][]): Op[] { - if (for_block == undefined) - throw new Error() - - const ops: Op[] = [] - const body = compile_block(for_block.body, functions) - replace_breaks(body, 1) - - const debug = for_block.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push({ code: OpCode.StartStackChange, debug: debug }) - ops.push(...compile_expression(for_block.itorator, functions)) - ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }) - - const after_creating_itorator = ops.length - ops.push({ code: OpCode.StartStackChange, debug: debug }) - ops.push({ code: OpCode.IterNext, debug: debug }) - ops.push({ code: OpCode.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }) - - ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }) - for (const [i, item] of [...for_block.items].reverse().entries()) - { - if (i == for_block.items.length - 1) - ops.push({ code: OpCode.IterUpdateState, debug: debug }) - ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }) - } - ops.push(...body) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) - - ops.push({ code: OpCode.EndStackChange, arg: make_number(0), debug: debug }) - ops.push({ code: OpCode.Pop, arg: make_number(3), debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + if (for_block == undefined) + throw new Error() + + const ops: Op[] = [] + const body = compile_block(for_block.body, functions) + replace_breaks(body, 1) + + const debug = for_block.token.debug + ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push({ code: OpCode.StartStackChange, debug: debug }) + ops.push(...compile_expression(for_block.itorator, functions)) + ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }) + + const after_creating_itorator = ops.length + ops.push({ code: OpCode.StartStackChange, debug: debug }) + ops.push({ code: OpCode.IterNext, debug: debug }) + ops.push({ code: OpCode.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }) + + ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }) + for (const [i, item] of [...for_block.items].reverse().entries()) + { + if (i == for_block.items.length - 1) + ops.push({ code: OpCode.IterUpdateState, debug: debug }) + ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }) + } + ops.push(...body) + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) + + ops.push({ code: OpCode.EndStackChange, arg: make_number(0), debug: debug }) + ops.push({ code: OpCode.Pop, arg: make_number(3), debug: debug }) + ops.push({ code: OpCode.EndBlock, debug: debug }) + return ops } function compile_step(step: Expression | undefined, functions: Op[][]): Op[] { - if (step == undefined) - return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }] + if (step == undefined) + return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }] - return compile_expression(step, functions) + return compile_expression(step, functions) } function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Op[][]): Op[] { - if (numeric_for_block == undefined) - throw new Error() - - const ops: Op[] = [] - const body = compile_block(numeric_for_block.body, functions) - const step = compile_step(numeric_for_block.step, functions) - const index = numeric_for_block.index.data - const debug = numeric_for_block.index.debug - replace_breaks(body, step.length + 4) - - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_expression(numeric_for_block.start, functions)) - - const after_creating_itorator = ops.length - ops.push({ code: OpCode.Dup, debug: debug }) - ops.push(...compile_expression(numeric_for_block.end, functions)) - ops.push({ code: OpCode.NotEquals, debug: debug }) - ops.push({ code: OpCode.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }) - - ops.push({ code: OpCode.Store, arg: make_string(index), debug: debug }) - ops.push(...body) - ops.push({ code: OpCode.Load, arg: make_string(index), debug: debug }) - ops.push(...step) - ops.push({ code: OpCode.Add, debug: debug }) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) - - ops.push({ code: OpCode.Pop, debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + if (numeric_for_block == undefined) + throw new Error() + + const ops: Op[] = [] + const body = compile_block(numeric_for_block.body, functions) + const step = compile_step(numeric_for_block.step, functions) + const index = numeric_for_block.index.data + const debug = numeric_for_block.index.debug + replace_breaks(body, step.length + 4) + + ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push(...compile_expression(numeric_for_block.start, functions)) + + const after_creating_itorator = ops.length + ops.push({ code: OpCode.Dup, debug: debug }) + ops.push(...compile_expression(numeric_for_block.end, functions)) + ops.push({ code: OpCode.NotEquals, debug: debug }) + ops.push({ code: OpCode.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }) + + ops.push({ code: OpCode.Store, arg: make_string(index), debug: debug }) + ops.push(...body) + ops.push({ code: OpCode.Load, arg: make_string(index), debug: debug }) + ops.push(...step) + ops.push({ code: OpCode.Add, debug: debug }) + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) + + ops.push({ code: OpCode.Pop, debug: debug }) + ops.push({ code: OpCode.EndBlock, debug: debug }) + return ops } function compile_repeat(repeat: Repeat | undefined, functions: Op[][]): Op[] { - if (repeat == undefined) - throw new Error() + if (repeat == undefined) + throw new Error() - const ops: Op[] = [] - const debug = repeat.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) + const ops: Op[] = [] + const debug = repeat.token.debug + ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_block(repeat.body, functions)) - ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }) + ops.push(...compile_block(repeat.body, functions)) + ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)) + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + ops.push({ code: OpCode.EndBlock, debug: debug }) + return ops } function compile_do(do_block: Do | undefined, functions: Op[][]): Op[] { - if (do_block == undefined) - throw new Error() - - const ops: Op[] = [] - const debug = do_block.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_block(do_block.body, functions)) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + if (do_block == undefined) + throw new Error() + + const ops: Op[] = [] + const debug = do_block.token.debug + ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push(...compile_block(do_block.body, functions)) + ops.push({ code: OpCode.EndBlock, debug: debug }) + return ops } function compile_return(return_block: Return | undefined, functions: Op[][]): Op[] { - if (return_block == undefined) - throw new Error() + if (return_block == undefined) + throw new Error() - const ops: Op[] = [] - for (const value of return_block.values) - ops.push(...compile_expression(value, functions)) + const ops: Op[] = [] + for (const value of return_block.values) + ops.push(...compile_expression(value, functions)) - const debug = return_block.token.debug - const return_count = return_block.values.length - ops.push({ code: OpCode.Return, arg: make_number(return_count), debug: debug }) - return ops + const debug = return_block.token.debug + const return_count = return_block.values.length + ops.push({ code: OpCode.Return, arg: make_number(return_count), debug: debug }) + return ops } interface ChunkResult { - code: Op[] - has_last_expression: boolean, + code: Op[] + has_last_expression: boolean, } function compile_block(chunk: Chunk, functions: Op[][]): Op[] { - const { code, has_last_expression } = compile_chunk(chunk, functions) - if (has_last_expression) - code.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) + const { code, has_last_expression } = compile_chunk(chunk, functions) + if (has_last_expression) + code.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) - return code + return code } function compile_chunk(chunk: Chunk, functions: Op[][]): ChunkResult { - const ops = [] - let has_last_expression = false - - for (const [index, statement] of chunk.statements.entries()) - { - const is_last_statement = (index == chunk.statements.length - 1) - switch (statement.kind) - { - case StatementKind.Empty: - break - case StatementKind.Expression: - ops.push(...compile_expression(statement.expression, functions)) - if (statement.expression == undefined) - break - - if (is_last_statement) - has_last_expression = true - else - ops.push({ code: OpCode.Pop, debug: statement.expression.token.debug }) - break - case StatementKind.Assignment: - ops.push(...compile_assignment(statement.assignment, functions)) - break - case StatementKind.Local: - ops.push(...compile_local(statement.local)) - break - case StatementKind.If: - ops.push(...compile_if(statement.if, functions)) - break - case StatementKind.While: - ops.push(...compile_while(statement.while, functions)) - break - case StatementKind.For: - ops.push(...compile_for(statement.for, functions)) - break - case StatementKind.NumericFor: - ops.push(...compile_numeric_for(statement.numeric_for, functions)) - break - case StatementKind.Repeat: - ops.push(...compile_repeat(statement.repeat, functions)) - break - case StatementKind.Do: - ops.push(...compile_do(statement.do, functions)) - break - case StatementKind.Return: - ops.push(...compile_return(statement.return, functions)) - break - case StatementKind.Break: - ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }) - break - } - } - - return { - code: ops, - has_last_expression: has_last_expression, - } + const ops = [] + let has_last_expression = false + + for (const [index, statement] of chunk.statements.entries()) + { + const is_last_statement = (index == chunk.statements.length - 1) + switch (statement.kind) + { + case StatementKind.Empty: + break + case StatementKind.Expression: + ops.push(...compile_expression(statement.expression, functions)) + if (statement.expression == undefined) + break + + if (is_last_statement) + has_last_expression = true + else + ops.push({ code: OpCode.Pop, debug: statement.expression.token.debug }) + break + case StatementKind.Assignment: + ops.push(...compile_assignment(statement.assignment, functions)) + break + case StatementKind.Local: + ops.push(...compile_local(statement.local)) + break + case StatementKind.If: + ops.push(...compile_if(statement.if, functions)) + break + case StatementKind.While: + ops.push(...compile_while(statement.while, functions)) + break + case StatementKind.For: + ops.push(...compile_for(statement.for, functions)) + break + case StatementKind.NumericFor: + ops.push(...compile_numeric_for(statement.numeric_for, functions)) + break + case StatementKind.Repeat: + ops.push(...compile_repeat(statement.repeat, functions)) + break + case StatementKind.Do: + ops.push(...compile_do(statement.do, functions)) + break + case StatementKind.Return: + ops.push(...compile_return(statement.return, functions)) + break + case StatementKind.Break: + ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }) + break + } + } + + return { + code: ops, + has_last_expression: has_last_expression, + } } function link(code: Op[], function_id: number, location: number) { - for (const op of code) - { - if (op.arg?.data_type == DataType.Function && - op.arg?.function_id == function_id) - { - op.arg.function_id = location - } - } + for (const op of code) + { + if (op.arg?.data_type == DataType.Function && + op.arg?.function_id == function_id) + { + op.arg.function_id = location + } + } } export function compile(chunk: Chunk, extend?: Op[]): Program { - const ops = [...(extend ?? [])] - const functions: Op[][] = [] - const { code, has_last_expression } = compile_chunk(chunk, functions) - - const function_locations: number[] = [] - for (const func of functions) - { - function_locations.push(ops.length) - ops.push(...func) - } - - for (const [id, location] of function_locations.entries()) - link(code, id, location) - - const start = ops.length - if (extend?.length ?? 0 > 0) - ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) - ops.push(...code) - if (!has_last_expression) - ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }) - - return { - code: ops, - start: start, - } + const ops = [...(extend ?? [])] + const functions: Op[][] = [] + const { code, has_last_expression } = compile_chunk(chunk, functions) + + const function_locations: number[] = [] + for (const func of functions) + { + function_locations.push(ops.length) + ops.push(...func) + } + + for (const [id, location] of function_locations.entries()) + link(code, id, location) + + const start = ops.length + if (extend?.length ?? 0 > 0) + ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) + ops.push(...code) + if (!has_last_expression) + ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }) + + return { + code: ops, + start: start, + } } diff --git a/src/engine.mts b/src/engine.mts index 6a5d996..32e3312 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -17,611 +17,611 @@ import * as std from './lib.mts' function index(val: Variable | undefined): string | number | undefined { - if (val == undefined) - return undefined - else if (val.data_type == DataType.String) - return val.string - else if (val.data_type == DataType.Number) - return val.number - else - return undefined + if (val == undefined) + return undefined + else if (val.data_type == DataType.String) + return val.string + else if (val.data_type == DataType.Number) + return val.number + else + return undefined } function is_true(val: Variable | undefined): boolean { - if (val == undefined) - return false - - switch (val.data_type) - { - case DataType.Number: return val.number != 0 - case DataType.String: return val.string != '' - case DataType.Boolean: return val.boolean ?? false - case DataType.Function: return true - case DataType.NativeFunction: return true - default: - return false - } + if (val == undefined) + return false + + switch (val.data_type) + { + case DataType.Number: return val.number != 0 + case DataType.String: return val.string != '' + case DataType.Boolean: return val.boolean ?? false + case DataType.Function: return true + case DataType.NativeFunction: return true + default: + return false + } } function equals(a: Variable | undefined, b: Variable | undefined): boolean { - if (a == undefined && b == undefined) - return true - if (a == undefined || b == undefined) - return false - - if (a.data_type != b.data_type) - return false - - switch (a.data_type) - { - case DataType.Nil: return true - case DataType.Boolean: return a.boolean == b.boolean - case DataType.Number: return a.number == b.number - case DataType.String: return a.string == b.string - case DataType.Function: return a.function_id == b.function_id - case DataType.NativeFunction: return a.native_function == b.native_function - case DataType.Table: - { - if (a.table?.keys() != b.table?.keys()) - return false - - for (const key of a.table?.keys() ?? []) - { - if (!equals(a.table?.get(key), b.table?.get(key))) - return false - } - - return true - } - } + if (a == undefined && b == undefined) + return true + if (a == undefined || b == undefined) + return false + + if (a.data_type != b.data_type) + return false + + switch (a.data_type) + { + case DataType.Nil: return true + case DataType.Boolean: return a.boolean == b.boolean + case DataType.Number: return a.number == b.number + case DataType.String: return a.string == b.string + case DataType.Function: return a.function_id == b.function_id + case DataType.NativeFunction: return a.native_function == b.native_function + case DataType.Table: + { + if (a.table?.keys() != b.table?.keys()) + return false + + for (const key of a.table?.keys() ?? []) + { + if (!equals(a.table?.get(key), b.table?.get(key))) + return false + } + + return true + } + } } export interface LuaOptions { - trace?: boolean - trace_instructions?: boolean - trace_stack?: boolean - locals?: Map, + trace?: boolean + trace_instructions?: boolean + trace_stack?: boolean + locals?: Map, } export class Engine { - private program: Op[] - private globals: Map - private start_ip: number = 0 - - private ip: number = 0 - private stack: Variable[] = [] - private locals_stack: Map[] = [] - private locals_capture: Map[] = [] - private call_stack: number[] = [] - private assign_height_stack: number[] = [] - - private error: Error | undefined - - constructor(script?: string, - globals?: Map) - { - this.program = [] - this.globals = globals ?? std.std_lib() - this.error = undefined - this.reset() - - if (script != undefined) - { - const result = this.load(script) - if (result != undefined) - this.error = result - } - } - - load(chunk: string): undefined | Error - { - const stream = new TokenStream() - stream.feed(chunk) - - const ast = parse(stream) - if (ast instanceof Error) - return ast - - optimize_chunk(ast) - const program = compile(ast, this.program) - this.program = program.code - this.ip = program.start - this.start_ip = program.start - } - - bytecode(): string[] - { - if (this.error != undefined) - return [ this.error.message ] - - const output = [] - for (const [i, op] of this.program.entries()) - { - const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' - if (i == this.ip) - output.push(`* ${ i } ${ op_code_name(op.code) } ${ arg }`) - else - output.push(`${ i } ${ op_code_name(op.code) } ${ arg }`) - } - - return output - } - - global(name: string): Variable | undefined - { - return this.globals.get(name) - } - - define(name: string, func: NativeFunction) - { - this.globals.set(name, { - data_type: DataType.NativeFunction, - native_function: func, - }) - } - - define_table(name: string, table: Map) - { - this.globals.set(name, { - data_type: DataType.Table, - table: table, - }) - } - - reset() - { - this.ip = this.start_ip - this.stack = [] - this.locals_stack = [] - this.locals_capture = [] - this.call_stack = [] - this.assign_height_stack = [] - - this.locals_stack.push(new Map()) - } - - call(func: Variable, ...args: Variable[]): Variable[] | Error - { - if (func.native_function != undefined) - return this.call_native_function(func.native_function, ...args) - - if (func.function_id == undefined) - return new Error() // TODO: Error message - - const old_stack = this.stack - const old_call_stack = this.call_stack - const old_ip = this.ip - const old_locals_stack = this.locals_stack - this.stack = [] - this.call_stack = [] - this.locals_stack = [...func.locals ?? []] - this.ip = func.function_id ?? this.ip - - for (const arg of args) - this.stack.push(arg) - this.stack.push(make_number(args.length)) - this.locals_stack.push(new Map()) - - const result = this.run() - const return_values = this.stack - this.stack = old_stack - this.call_stack = old_call_stack - this.locals_stack = old_locals_stack - this.ip = old_ip - if (result instanceof Error) - return result - - return return_values - } - - run_for_steps(steps: number, options?: LuaOptions): Variable | Error | undefined - { - if (this.error != undefined) - return this.error - - if (options?.locals != undefined) - this.locals_stack.push(options.locals) - - let step_count = 0 - while (this.ip < this.program.length) - { - const result = this.step(options) - if (result != undefined) - return result - - step_count += 1 - if (step_count >= steps) - return undefined - } - - return this.stack[0] ?? nil - } - - run(options?: LuaOptions): Variable | Error - { - const result = this.run_for_steps(1000, options) - if (result == undefined) - return new Error('Program ran for too long') - else - return result - } - - raise_error(message: string) - { - const op = this.program.at(this.ip - 1) - this.error = this.runtime_error(op, message) - } - - private call_native_function(native_function: NativeFunction, ...args: Variable[]): Variable[] | Error - { - const results = native_function(this, ...args) - if (this.error != undefined) - { - const error = this.error - this.error = undefined - return error - } - - return results - } - - private operation(op: (x: number, y: number) => number) - { - const x = this.stack.pop()?.number ?? 0 - const y = this.stack.pop()?.number ?? 0 - this.stack.push(make_number(op(x, y))) - } - - private compair(op: (x: number, y: number) => boolean) - { - const x = this.stack.pop()?.number ?? 0 - const y = this.stack.pop()?.number ?? 0 - this.stack.push(make_boolean(op(x, y))) - } - - private force_stack_height(expected: number, got: number) - { - for (let i = got; i < expected; i++) - this.stack.push(nil) - for (let i = expected; i < got; i++) - this.stack.pop() - } - - private runtime_error(op: Op | undefined, message: string): Error - { - if (op == undefined) - return new Error(`${ message }`) - else - return new Error(`${ op.debug.line }:${ op.debug.column }: ${ message }`) - } - - private run_instruction(op: Op): undefined | Error - { - const { code, arg } = op - switch(code) - { - case OpCode.Pop: - { - const count = arg?.number ?? 1 - this.stack.splice(this.stack.length - count, count) - break - } - - case OpCode.Dup: - { - const count = arg?.number ?? 1 - const items = this.stack.splice(this.stack.length - count, count) - this.stack.push(...items, ...items) - break - } - - case OpCode.Swap: - { - const x = this.stack.splice(this.stack.length - 2, 1) - this.stack.push(...x) - break - } - - case OpCode.IterUpdateState: - { - this.stack[this.stack.length - 2] = this.stack[this.stack.length - 1] - break - } - - case OpCode.IterNext: - { - const state = this.stack[this.stack.length - 1] - const control = this.stack[this.stack.length - 2] - const iter = this.stack[this.stack.length - 3] - if (iter.native_function != undefined) - { - const result = this.call_native_function(iter.native_function, control, state) - if (result instanceof Error) - return result - this.stack.push(...result) - break - } - - this.stack.push(control, state, make_number(2)) - this.call_stack.push(this.ip) - this.locals_stack.push(new Map()) - this.locals_capture = iter.locals ?? [] - this.ip = iter.function_id ?? this.ip - break - } - - case OpCode.IterJumpIfDone: - { - if (this.stack[this.stack.length - 1].data_type == DataType.Nil) - this.ip += arg?.number ?? 0 - break - } - - case OpCode.Add: this.operation((x, y) => x + y); break - case OpCode.Subtract: this.operation((x, y) => x - y); break - case OpCode.Multiply: this.operation((x, y) => x * y); break - case OpCode.Divide: this.operation((x, y) => x / y); break - case OpCode.FloorDivide: this.operation((x, y) => Math.floor(x / y)); break - case OpCode.Modulo: this.operation((x, y) => x % y); break - case OpCode.Exponent: this.operation((x, y) => Math.pow(x, y)); break - case OpCode.LessThen: this.compair((x, y) => x < y); break - case OpCode.LessThenEquals: this.compair((x, y) => x <= y); break - case OpCode.GreaterThen: this.compair((x, y) => x > y); break - case OpCode.GreaterThenEquals: this.compair((x, y) => x >= y); break - - case OpCode.BitAnd: this.operation((x, y) => x & y); break - case OpCode.BitOr: this.operation((x, y) => x | y); break - case OpCode.BitXOr: this.operation((x, y) => x ^ y); break - case OpCode.BitShiftLeft: this.operation((x, y) => x << y); break - case OpCode.BitShiftRight: this.operation((x, y) => x >> y); break - - case OpCode.Concat: - { - const x = std.variable_to_string(this.stack.pop() ?? nil) - const y = std.variable_to_string(this.stack.pop() ?? nil) - this.stack.push(make_string(x + y)) - break - } - - case OpCode.Equals: - { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(equals(x, y))) - break - } - - case OpCode.NotEquals: - { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(!equals(x, y))) - break - } - - case OpCode.And: - { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(is_true(x) && is_true(y))) - break - } - - case OpCode.Or: - { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(is_true(x) || is_true(y))) - break - } - - case OpCode.Not: this.stack.push(make_boolean(!is_true(this.stack.pop()))); break - case OpCode.BitNot: this.stack.push(make_number(~(this.stack.pop()?.number ?? 0))); break - case OpCode.Negate: this.stack.push(make_number(-(this.stack.pop()?.number ?? 0))); break - case OpCode.IsNotNil: this.stack.push(make_boolean((this.stack.pop() ?? nil) != nil)); break - case OpCode.Jump: this.ip += arg?.number ?? 0; break - case OpCode.JumpIfNot: if (!is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break - case OpCode.JumpIf: if (is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break - case OpCode.MakeLocal: this.locals_stack.at(-1)?.set(arg?.string ?? '', nil); break - case OpCode.NewTable: this.stack.push({ data_type: DataType.Table, table: new Map() }); break - case OpCode.StartBlock: this.locals_stack.push(new Map()); break - case OpCode.EndBlock: this.locals_stack.pop(); break - - case OpCode.Length: - { - const size = std.variable_size(this.stack.pop() ?? nil) - if (size == undefined) - this.stack.push(nil) - else - this.stack.push(make_number(size)) - break - } - - case OpCode.Return: - { - this.ip = this.call_stack.pop() ?? this.program.length - this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()) - this.locals_capture = [] - break - } - - case OpCode.LoadIndex: - { - const table = this.stack.pop() - if (table?.data_type == DataType.Nil) - { - this.stack.pop() // Pop index - this.stack.push(nil) - break - } - - if (table == undefined || table.table == undefined) - return this.runtime_error(op, 'Can only index on tables') - - const i_var = this.stack.pop() - if (i_var?.data_type == DataType.Nil) - { - this.stack.push(nil) - break - } - - const i = index(i_var) - if (i == undefined) - return this.runtime_error(op, 'Invalid index, must be a number or string') - - this.stack.push(table.table.get(i) ?? nil) - break - } - - case OpCode.StoreIndex: - { - const count = arg?.number ?? 1 - const table = this.stack[this.stack.length - count*2 - 1] ?? nil - if (table.table == undefined) - return this.runtime_error(op, 'Can only index tables') - - for (let i = 0; i < count; i++) - { - const key = index(this.stack.pop()) - const value = this.stack.pop() ?? nil - if (key == undefined) - return this.runtime_error(op, 'Invalid key, must be a number or string') - - table.table.set(key, value) - } - - break - } - - case OpCode.Store: - { - const name = arg?.string ?? '' - const value = this.stack.pop() ?? nil - const local = [...this.locals_capture, ...this.locals_stack] - .reverse() - .find(x => x.has(name)) - - if (local != undefined) - local.set(name, value) - else - this.globals.set(name, value) - break - } - - case OpCode.Push: - { - if (this.locals_stack.length > 0 && arg?.data_type == DataType.Function) - arg.locals = [...this.locals_stack] - this.stack.push(arg ?? nil) - break - } - - case OpCode.Load: - { - const name = arg?.string ?? '' - const local = [...this.locals_capture, ...this.locals_stack] - .reverse() - .map(x => x.get(name)) - .find(x => x != undefined && x.data_type != DataType.Nil) - - const global = this.globals.get(name) - this.stack.push(local ?? global ?? nil) - break - } - - case OpCode.Call: - { - const count = this.stack.pop()?.number ?? 0 - const func_var = this.stack.pop() ?? nil - switch (func_var.data_type) - { - case DataType.NativeFunction: - { - const args = this.stack.splice(this.stack.length - count, count) - if (func_var.native_function != undefined) - { - const result = this.call_native_function(func_var.native_function, ...args) - if (result instanceof Error) - return result - this.stack.push(...result) - } - - if (this.error != undefined) - { - const error = this.error - this.error = undefined - return error - } - break - } - - case DataType.Function: - { - this.stack.push(make_number(count)) - this.call_stack.push(this.locals_stack.length, this.ip) - this.locals_stack.push(new Map()) - this.locals_capture = func_var.locals ?? [] - this.ip = func_var.function_id ?? this.ip - break - } - - default: - { - return this.runtime_error(op, - `Object of type '${ std.type_name(func_var.data_type) }' ` + - 'is not callable') - } - } - - break - } - - case OpCode.ArgumentCount: - { - const got = this.stack.pop()?.number ?? 0 - const expected = arg?.number ?? 0 - this.force_stack_height(expected, got) - break - } - - case OpCode.StartStackChange: - { - this.assign_height_stack.push(this.stack.length) - break - } - - case OpCode.EndStackChange: - { - const got = this.stack.length - (this.assign_height_stack.pop() ?? 0) - const expected = arg?.number ?? 0 - this.force_stack_height(expected, got) - break - } - } - } - - step(options?: LuaOptions): undefined | Error - { - if (this.error != undefined) - return this.error - - if (this.ip >= this.program.length) - return - - const op = this.program[this.ip++] - if (options?.trace || options?.trace_instructions) - { - const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' - console.log(this.ip - 1, op_code_name(op.code), arg) - } - - const result = this.run_instruction(op) - if (result != undefined) - return result - - if (options?.trace || options?.trace_stack) - console.log(this.ip - 1, ...this.stack.map(x => std.variable_to_string(x))) - } + private program: Op[] + private globals: Map + private start_ip: number = 0 + + private ip: number = 0 + private stack: Variable[] = [] + private locals_stack: Map[] = [] + private locals_capture: Map[] = [] + private call_stack: number[] = [] + private assign_height_stack: number[] = [] + + private error: Error | undefined + + constructor(script?: string, + globals?: Map) + { + this.program = [] + this.globals = globals ?? std.std_lib() + this.error = undefined + this.reset() + + if (script != undefined) + { + const result = this.load(script) + if (result != undefined) + this.error = result + } + } + + load(chunk: string): undefined | Error + { + const stream = new TokenStream() + stream.feed(chunk) + + const ast = parse(stream) + if (ast instanceof Error) + return ast + + optimize_chunk(ast) + const program = compile(ast, this.program) + this.program = program.code + this.ip = program.start + this.start_ip = program.start + } + + bytecode(): string[] + { + if (this.error != undefined) + return [ this.error.message ] + + const output = [] + for (const [i, op] of this.program.entries()) + { + const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' + if (i == this.ip) + output.push(`* ${ i } ${ op_code_name(op.code) } ${ arg }`) + else + output.push(`${ i } ${ op_code_name(op.code) } ${ arg }`) + } + + return output + } + + global(name: string): Variable | undefined + { + return this.globals.get(name) + } + + define(name: string, func: NativeFunction) + { + this.globals.set(name, { + data_type: DataType.NativeFunction, + native_function: func, + }) + } + + define_table(name: string, table: Map) + { + this.globals.set(name, { + data_type: DataType.Table, + table: table, + }) + } + + reset() + { + this.ip = this.start_ip + this.stack = [] + this.locals_stack = [] + this.locals_capture = [] + this.call_stack = [] + this.assign_height_stack = [] + + this.locals_stack.push(new Map()) + } + + call(func: Variable, ...args: Variable[]): Variable[] | Error + { + if (func.native_function != undefined) + return this.call_native_function(func.native_function, ...args) + + if (func.function_id == undefined) + return new Error() // TODO: Error message + + const old_stack = this.stack + const old_call_stack = this.call_stack + const old_ip = this.ip + const old_locals_stack = this.locals_stack + this.stack = [] + this.call_stack = [] + this.locals_stack = [...func.locals ?? []] + this.ip = func.function_id ?? this.ip + + for (const arg of args) + this.stack.push(arg) + this.stack.push(make_number(args.length)) + this.locals_stack.push(new Map()) + + const result = this.run() + const return_values = this.stack + this.stack = old_stack + this.call_stack = old_call_stack + this.locals_stack = old_locals_stack + this.ip = old_ip + if (result instanceof Error) + return result + + return return_values + } + + run_for_steps(steps: number, options?: LuaOptions): Variable | Error | undefined + { + if (this.error != undefined) + return this.error + + if (options?.locals != undefined) + this.locals_stack.push(options.locals) + + let step_count = 0 + while (this.ip < this.program.length) + { + const result = this.step(options) + if (result != undefined) + return result + + step_count += 1 + if (step_count >= steps) + return undefined + } + + return this.stack[0] ?? nil + } + + run(options?: LuaOptions): Variable | Error + { + const result = this.run_for_steps(1000, options) + if (result == undefined) + return new Error('Program ran for too long') + else + return result + } + + raise_error(message: string) + { + const op = this.program.at(this.ip - 1) + this.error = this.runtime_error(op, message) + } + + private call_native_function(native_function: NativeFunction, ...args: Variable[]): Variable[] | Error + { + const results = native_function(this, ...args) + if (this.error != undefined) + { + const error = this.error + this.error = undefined + return error + } + + return results + } + + private operation(op: (x: number, y: number) => number) + { + const x = this.stack.pop()?.number ?? 0 + const y = this.stack.pop()?.number ?? 0 + this.stack.push(make_number(op(x, y))) + } + + private compair(op: (x: number, y: number) => boolean) + { + const x = this.stack.pop()?.number ?? 0 + const y = this.stack.pop()?.number ?? 0 + this.stack.push(make_boolean(op(x, y))) + } + + private force_stack_height(expected: number, got: number) + { + for (let i = got; i < expected; i++) + this.stack.push(nil) + for (let i = expected; i < got; i++) + this.stack.pop() + } + + private runtime_error(op: Op | undefined, message: string): Error + { + if (op == undefined) + return new Error(`${ message }`) + else + return new Error(`${ op.debug.line }:${ op.debug.column }: ${ message }`) + } + + private run_instruction(op: Op): undefined | Error + { + const { code, arg } = op + switch(code) + { + case OpCode.Pop: + { + const count = arg?.number ?? 1 + this.stack.splice(this.stack.length - count, count) + break + } + + case OpCode.Dup: + { + const count = arg?.number ?? 1 + const items = this.stack.splice(this.stack.length - count, count) + this.stack.push(...items, ...items) + break + } + + case OpCode.Swap: + { + const x = this.stack.splice(this.stack.length - 2, 1) + this.stack.push(...x) + break + } + + case OpCode.IterUpdateState: + { + this.stack[this.stack.length - 2] = this.stack[this.stack.length - 1] + break + } + + case OpCode.IterNext: + { + const state = this.stack[this.stack.length - 1] + const control = this.stack[this.stack.length - 2] + const iter = this.stack[this.stack.length - 3] + if (iter.native_function != undefined) + { + const result = this.call_native_function(iter.native_function, control, state) + if (result instanceof Error) + return result + this.stack.push(...result) + break + } + + this.stack.push(control, state, make_number(2)) + this.call_stack.push(this.ip) + this.locals_stack.push(new Map()) + this.locals_capture = iter.locals ?? [] + this.ip = iter.function_id ?? this.ip + break + } + + case OpCode.IterJumpIfDone: + { + if (this.stack[this.stack.length - 1].data_type == DataType.Nil) + this.ip += arg?.number ?? 0 + break + } + + case OpCode.Add: this.operation((x, y) => x + y); break + case OpCode.Subtract: this.operation((x, y) => x - y); break + case OpCode.Multiply: this.operation((x, y) => x * y); break + case OpCode.Divide: this.operation((x, y) => x / y); break + case OpCode.FloorDivide: this.operation((x, y) => Math.floor(x / y)); break + case OpCode.Modulo: this.operation((x, y) => x % y); break + case OpCode.Exponent: this.operation((x, y) => Math.pow(x, y)); break + case OpCode.LessThen: this.compair((x, y) => x < y); break + case OpCode.LessThenEquals: this.compair((x, y) => x <= y); break + case OpCode.GreaterThen: this.compair((x, y) => x > y); break + case OpCode.GreaterThenEquals: this.compair((x, y) => x >= y); break + + case OpCode.BitAnd: this.operation((x, y) => x & y); break + case OpCode.BitOr: this.operation((x, y) => x | y); break + case OpCode.BitXOr: this.operation((x, y) => x ^ y); break + case OpCode.BitShiftLeft: this.operation((x, y) => x << y); break + case OpCode.BitShiftRight: this.operation((x, y) => x >> y); break + + case OpCode.Concat: + { + const x = std.variable_to_string(this.stack.pop() ?? nil) + const y = std.variable_to_string(this.stack.pop() ?? nil) + this.stack.push(make_string(x + y)) + break + } + + case OpCode.Equals: + { + const [x, y] = [this.stack.pop(), this.stack.pop()] + this.stack.push(make_boolean(equals(x, y))) + break + } + + case OpCode.NotEquals: + { + const [x, y] = [this.stack.pop(), this.stack.pop()] + this.stack.push(make_boolean(!equals(x, y))) + break + } + + case OpCode.And: + { + const [x, y] = [this.stack.pop(), this.stack.pop()] + this.stack.push(make_boolean(is_true(x) && is_true(y))) + break + } + + case OpCode.Or: + { + const [x, y] = [this.stack.pop(), this.stack.pop()] + this.stack.push(make_boolean(is_true(x) || is_true(y))) + break + } + + case OpCode.Not: this.stack.push(make_boolean(!is_true(this.stack.pop()))); break + case OpCode.BitNot: this.stack.push(make_number(~(this.stack.pop()?.number ?? 0))); break + case OpCode.Negate: this.stack.push(make_number(-(this.stack.pop()?.number ?? 0))); break + case OpCode.IsNotNil: this.stack.push(make_boolean((this.stack.pop() ?? nil) != nil)); break + case OpCode.Jump: this.ip += arg?.number ?? 0; break + case OpCode.JumpIfNot: if (!is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break + case OpCode.JumpIf: if (is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break + case OpCode.MakeLocal: this.locals_stack.at(-1)?.set(arg?.string ?? '', nil); break + case OpCode.NewTable: this.stack.push({ data_type: DataType.Table, table: new Map() }); break + case OpCode.StartBlock: this.locals_stack.push(new Map()); break + case OpCode.EndBlock: this.locals_stack.pop(); break + + case OpCode.Length: + { + const size = std.variable_size(this.stack.pop() ?? nil) + if (size == undefined) + this.stack.push(nil) + else + this.stack.push(make_number(size)) + break + } + + case OpCode.Return: + { + this.ip = this.call_stack.pop() ?? this.program.length + this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()) + this.locals_capture = [] + break + } + + case OpCode.LoadIndex: + { + const table = this.stack.pop() + if (table?.data_type == DataType.Nil) + { + this.stack.pop() // Pop index + this.stack.push(nil) + break + } + + if (table == undefined || table.table == undefined) + return this.runtime_error(op, 'Can only index on tables') + + const i_var = this.stack.pop() + if (i_var?.data_type == DataType.Nil) + { + this.stack.push(nil) + break + } + + const i = index(i_var) + if (i == undefined) + return this.runtime_error(op, 'Invalid index, must be a number or string') + + this.stack.push(table.table.get(i) ?? nil) + break + } + + case OpCode.StoreIndex: + { + const count = arg?.number ?? 1 + const table = this.stack[this.stack.length - count*2 - 1] ?? nil + if (table.table == undefined) + return this.runtime_error(op, 'Can only index tables') + + for (let i = 0; i < count; i++) + { + const key = index(this.stack.pop()) + const value = this.stack.pop() ?? nil + if (key == undefined) + return this.runtime_error(op, 'Invalid key, must be a number or string') + + table.table.set(key, value) + } + + break + } + + case OpCode.Store: + { + const name = arg?.string ?? '' + const value = this.stack.pop() ?? nil + const local = [...this.locals_capture, ...this.locals_stack] + .reverse() + .find(x => x.has(name)) + + if (local != undefined) + local.set(name, value) + else + this.globals.set(name, value) + break + } + + case OpCode.Push: + { + if (this.locals_stack.length > 0 && arg?.data_type == DataType.Function) + arg.locals = [...this.locals_stack] + this.stack.push(arg ?? nil) + break + } + + case OpCode.Load: + { + const name = arg?.string ?? '' + const local = [...this.locals_capture, ...this.locals_stack] + .reverse() + .map(x => x.get(name)) + .find(x => x != undefined && x.data_type != DataType.Nil) + + const global = this.globals.get(name) + this.stack.push(local ?? global ?? nil) + break + } + + case OpCode.Call: + { + const count = this.stack.pop()?.number ?? 0 + const func_var = this.stack.pop() ?? nil + switch (func_var.data_type) + { + case DataType.NativeFunction: + { + const args = this.stack.splice(this.stack.length - count, count) + if (func_var.native_function != undefined) + { + const result = this.call_native_function(func_var.native_function, ...args) + if (result instanceof Error) + return result + this.stack.push(...result) + } + + if (this.error != undefined) + { + const error = this.error + this.error = undefined + return error + } + break + } + + case DataType.Function: + { + this.stack.push(make_number(count)) + this.call_stack.push(this.locals_stack.length, this.ip) + this.locals_stack.push(new Map()) + this.locals_capture = func_var.locals ?? [] + this.ip = func_var.function_id ?? this.ip + break + } + + default: + { + return this.runtime_error(op, + `Object of type '${ std.type_name(func_var.data_type) }' ` + + 'is not callable') + } + } + + break + } + + case OpCode.ArgumentCount: + { + const got = this.stack.pop()?.number ?? 0 + const expected = arg?.number ?? 0 + this.force_stack_height(expected, got) + break + } + + case OpCode.StartStackChange: + { + this.assign_height_stack.push(this.stack.length) + break + } + + case OpCode.EndStackChange: + { + const got = this.stack.length - (this.assign_height_stack.pop() ?? 0) + const expected = arg?.number ?? 0 + this.force_stack_height(expected, got) + break + } + } + } + + step(options?: LuaOptions): undefined | Error + { + if (this.error != undefined) + return this.error + + if (this.ip >= this.program.length) + return + + const op = this.program[this.ip++] + if (options?.trace || options?.trace_instructions) + { + const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' + console.log(this.ip - 1, op_code_name(op.code), arg) + } + + const result = this.run_instruction(op) + if (result != undefined) + return result + + if (options?.trace || options?.trace_stack) + console.log(this.ip - 1, ...this.stack.map(x => std.variable_to_string(x))) + } } diff --git a/src/index.mts b/src/index.mts index f6b0d2b..9376b12 100644 --- a/src/index.mts +++ b/src/index.mts @@ -25,13 +25,12 @@ export const Table = DataType.Table export { - std_lib as std_global, - nil, - make_boolean as boolean, - make_number as number, - make_string as string, - variable_to_string as to_string, - Engine, DataType, Variable, NativeFunction, - lexer, parser, ast, compile, opcode, runtime, + std_lib as std_global, + nil, + make_boolean as boolean, + make_number as number, + make_string as string, + variable_to_string as to_string, + Engine, DataType, Variable, NativeFunction, + lexer, parser, ast, compile, opcode, runtime, } - diff --git a/src/lexer.mts b/src/lexer.mts index cbaf7c9..90b3cf5 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -5,672 +5,671 @@ */ export enum State { - Initial, - Identifier, - StringLiteral, - StringLiteralEscape, - MultiLineString, - NumberLiteral, - NumberLiteralDot, - NumberLiteralExpSign, - NumberLiteralExp, - NumberHex, - Comment, + Initial, + Identifier, + StringLiteral, + StringLiteralEscape, + MultiLineString, + NumberLiteral, + NumberLiteralDot, + NumberLiteralExpSign, + NumberLiteralExp, + NumberHex, + Comment, } export enum TokenKind { - EOF, - NotFinished, - - Identifier, - StringLiteral, - BooleanLiteral, - NumberLiteral, - NilLiteral, - - OpenBrace, - CloseBrace, - OpenSquare, - CloseSquare, - SquiglyOpen, - SquiglyClose, - - Addition, - Subtract, - Multiply, - Division, - FloorDivision, - Modulo, - Exponent, - Concat, - Hash, - - BitAnd, - BitOr, - BitXOrNot, - BitShiftLeft, - BitShiftRight, - - Equals, - NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, - And, - Or, - Not, - - Assign, - Semicolon, - Comma, - Dot, - - Function, - If, - While, - For, - Repeat, - In, - Do, - Then, - ElseIf, - Else, - Until, - End, - Return, - Break, - Local, + EOF, + NotFinished, + + Identifier, + StringLiteral, + BooleanLiteral, + NumberLiteral, + NilLiteral, + + OpenBrace, + CloseBrace, + OpenSquare, + CloseSquare, + SquiglyOpen, + SquiglyClose, + + Addition, + Subtract, + Multiply, + Division, + FloorDivision, + Modulo, + Exponent, + Concat, + Hash, + + BitAnd, + BitOr, + BitXOrNot, + BitShiftLeft, + BitShiftRight, + + Equals, + NotEquals, + LessThen, + LessThenEquals, + GreaterThen, + GreaterThenEquals, + And, + Or, + Not, + + Assign, + Semicolon, + Comma, + Dot, + + Function, + If, + While, + For, + Repeat, + In, + Do, + Then, + ElseIf, + Else, + Until, + End, + Return, + Break, + Local, } export function token_kind_to_string(kind: TokenKind) { - switch(kind) - { - case TokenKind.EOF: return 'EOF' - case TokenKind.NotFinished: return 'NotFinished' - case TokenKind.Identifier: return 'Identifier' - case TokenKind.StringLiteral: return 'StringLiteral' - case TokenKind.BooleanLiteral: return 'BooleanLiteral' - case TokenKind.NumberLiteral: return 'NumberLiteral' - case TokenKind.NilLiteral: return 'nil' - case TokenKind.OpenBrace: return '(' - case TokenKind.CloseBrace: return ')' - case TokenKind.OpenSquare: return '[' - case TokenKind.CloseSquare: return ']' - case TokenKind.SquiglyOpen: return '{' - case TokenKind.SquiglyClose: return '}' - case TokenKind.Addition: return '+' - case TokenKind.Subtract: return '-' - case TokenKind.Multiply: return '*' - case TokenKind.Division: return '/' - case TokenKind.FloorDivision: return '//' - case TokenKind.Modulo: return '%' - case TokenKind.Exponent: return '^' - case TokenKind.BitAnd: return '&' - case TokenKind.BitOr: return '|' - case TokenKind.BitXOrNot: return '~' - case TokenKind.BitShiftLeft: return '<<' - case TokenKind.BitShiftRight: return '>>' - case TokenKind.LessThen: return '<' - case TokenKind.GreaterThen: return '>' - case TokenKind.And: return 'and' - case TokenKind.Or: return 'or' - case TokenKind.Not: return 'not' - case TokenKind.Assign: return '=' - case TokenKind.Semicolon: return ';' - case TokenKind.Comma: return ',' - case TokenKind.Dot: return '.' - case TokenKind.Function: return 'function' - case TokenKind.If: return 'if' - case TokenKind.While: return 'while' - case TokenKind.For: return 'for' - case TokenKind.Repeat: return 'repeat' - case TokenKind.In: return 'in' - case TokenKind.Do: return 'do' - case TokenKind.Then: return 'then' - case TokenKind.ElseIf: return 'elseif' - case TokenKind.Else: return 'else' - case TokenKind.Until: return 'until' - case TokenKind.End: return 'end' - case TokenKind.Return: return 'return' - case TokenKind.Break: return 'break' - case TokenKind.Local: return 'local' - } + switch(kind) + { + case TokenKind.EOF: return 'EOF' + case TokenKind.NotFinished: return 'NotFinished' + case TokenKind.Identifier: return 'Identifier' + case TokenKind.StringLiteral: return 'StringLiteral' + case TokenKind.BooleanLiteral: return 'BooleanLiteral' + case TokenKind.NumberLiteral: return 'NumberLiteral' + case TokenKind.NilLiteral: return 'nil' + case TokenKind.OpenBrace: return '(' + case TokenKind.CloseBrace: return ')' + case TokenKind.OpenSquare: return '[' + case TokenKind.CloseSquare: return ']' + case TokenKind.SquiglyOpen: return '{' + case TokenKind.SquiglyClose: return '}' + case TokenKind.Addition: return '+' + case TokenKind.Subtract: return '-' + case TokenKind.Multiply: return '*' + case TokenKind.Division: return '/' + case TokenKind.FloorDivision: return '//' + case TokenKind.Modulo: return '%' + case TokenKind.Exponent: return '^' + case TokenKind.BitAnd: return '&' + case TokenKind.BitOr: return '|' + case TokenKind.BitXOrNot: return '~' + case TokenKind.BitShiftLeft: return '<<' + case TokenKind.BitShiftRight: return '>>' + case TokenKind.LessThen: return '<' + case TokenKind.GreaterThen: return '>' + case TokenKind.And: return 'and' + case TokenKind.Or: return 'or' + case TokenKind.Not: return 'not' + case TokenKind.Assign: return '=' + case TokenKind.Semicolon: return ';' + case TokenKind.Comma: return ',' + case TokenKind.Dot: return '.' + case TokenKind.Function: return 'function' + case TokenKind.If: return 'if' + case TokenKind.While: return 'while' + case TokenKind.For: return 'for' + case TokenKind.Repeat: return 'repeat' + case TokenKind.In: return 'in' + case TokenKind.Do: return 'do' + case TokenKind.Then: return 'then' + case TokenKind.ElseIf: return 'elseif' + case TokenKind.Else: return 'else' + case TokenKind.Until: return 'until' + case TokenKind.End: return 'end' + case TokenKind.Return: return 'return' + case TokenKind.Break: return 'break' + case TokenKind.Local: return 'local' + } } export interface Debug { - line: number - column: number + line: number + column: number } export interface Token { - data: string - kind: TokenKind - debug: Debug + data: string + kind: TokenKind + debug: Debug } const single_token_map: Map = new Map([ - ['(', TokenKind.OpenBrace], - [')', TokenKind.CloseBrace], - ['[', TokenKind.OpenSquare], - [']', TokenKind.CloseSquare], - ['{', TokenKind.SquiglyOpen], - ['}', TokenKind.SquiglyClose], - - ['+', TokenKind.Addition], - ['-', TokenKind.Subtract], - ['*', TokenKind.Multiply], - ['/', TokenKind.Division], - ['%', TokenKind.Modulo], - ['^', TokenKind.Exponent], - ['&', TokenKind.BitAnd], - ['|', TokenKind.BitOr], - ['~', TokenKind.BitXOrNot], - - ['<', TokenKind.LessThen], - ['>', TokenKind.GreaterThen], - - ['=', TokenKind.Assign], - [';', TokenKind.Semicolon], - [',', TokenKind.Comma], - ['.', TokenKind.Dot], - ['#', TokenKind.Hash], + ['(', TokenKind.OpenBrace], + [')', TokenKind.CloseBrace], + ['[', TokenKind.OpenSquare], + [']', TokenKind.CloseSquare], + ['{', TokenKind.SquiglyOpen], + ['}', TokenKind.SquiglyClose], + + ['+', TokenKind.Addition], + ['-', TokenKind.Subtract], + ['*', TokenKind.Multiply], + ['/', TokenKind.Division], + ['%', TokenKind.Modulo], + ['^', TokenKind.Exponent], + ['&', TokenKind.BitAnd], + ['|', TokenKind.BitOr], + ['~', TokenKind.BitXOrNot], + + ['<', TokenKind.LessThen], + ['>', TokenKind.GreaterThen], + + ['=', TokenKind.Assign], + [';', TokenKind.Semicolon], + [',', TokenKind.Comma], + ['.', TokenKind.Dot], + ['#', TokenKind.Hash], ]) const double_token_map: Map = new Map([ - ['==', TokenKind.Equals], - ['<=', TokenKind.LessThenEquals], - ['>=', TokenKind.GreaterThenEquals], - ['~=', TokenKind.NotEquals], - ['..', TokenKind.Concat], - ['//', TokenKind.FloorDivision], - ['<<', TokenKind.BitShiftLeft], - ['>>', TokenKind.BitShiftRight], + ['==', TokenKind.Equals], + ['<=', TokenKind.LessThenEquals], + ['>=', TokenKind.GreaterThenEquals], + ['~=', TokenKind.NotEquals], + ['..', TokenKind.Concat], + ['//', TokenKind.FloorDivision], + ['<<', TokenKind.BitShiftLeft], + ['>>', TokenKind.BitShiftRight], ]) const keyword_map: Map = new Map([ - ['function', TokenKind.Function], - ['if', TokenKind.If], - ['while', TokenKind.While], - ['for', TokenKind.For], - ['repeat', TokenKind.Repeat], - ['in', TokenKind.In], - ['do', TokenKind.Do], - ['then', TokenKind.Then], - ['elseif', TokenKind.ElseIf], - ['else', TokenKind.Else], - ['until', TokenKind.Until], - ['end', TokenKind.End], - ['return', TokenKind.Return], - ['break', TokenKind.Break], - - ['and', TokenKind.And], - ['or', TokenKind.Or], - ['not', TokenKind.Not], - - ['true', TokenKind.BooleanLiteral], - ['false', TokenKind.BooleanLiteral], - ['nil', TokenKind.NilLiteral], - ['local', TokenKind.Local], + ['function', TokenKind.Function], + ['if', TokenKind.If], + ['while', TokenKind.While], + ['for', TokenKind.For], + ['repeat', TokenKind.Repeat], + ['in', TokenKind.In], + ['do', TokenKind.Do], + ['then', TokenKind.Then], + ['elseif', TokenKind.ElseIf], + ['else', TokenKind.Else], + ['until', TokenKind.Until], + ['end', TokenKind.End], + ['return', TokenKind.Return], + ['break', TokenKind.Break], + + ['and', TokenKind.And], + ['or', TokenKind.Or], + ['not', TokenKind.Not], + + ['true', TokenKind.BooleanLiteral], + ['false', TokenKind.BooleanLiteral], + ['nil', TokenKind.NilLiteral], + ['local', TokenKind.Local], ]) export class TokenStream { - private readonly processing_stream: string[] - private readonly peek_queue: Token[] - - private state: State - private end_of_stream: boolean = false - private buffer: string - private token_start_debug: Debug - - private line: number - private column: number - - constructor() - { - this.state = State.Initial - this.processing_stream = [] - this.buffer = '' - this.token_start_debug = { line: 0, column: 0 } - - this.line = 1 - this.column = 1 - this.peek_queue = [] - } - - private current(): string | undefined - { - if (this.processing_stream.length > 0) - return this.processing_stream[0] - - if (this.end_of_stream) - return undefined - - this.end_of_stream = true - return '\0' - } - - private consume() - { - if (this.processing_stream.length <= 0) - return - - this.column += 1 - if (this.processing_stream.shift() == '\n') - { - this.line += 1 - this.column = 1 - } - } - - private start_token() - { - this.token_start_debug = { - line: this.line, - column: this.column, - } - this.buffer = '' - } - - private initial() - { - if (this.processing_stream.length == 0) - { - this.peek_queue.push({ - data: '', - kind: TokenKind.EOF, - debug: { - line: this.line, - column: this.column, - }, - }) - - return - } - - const c = this.current() ?? '\0' - if (/\s/.test(c)) - return this.consume() - - if (this.processing_stream.length > 1) - { - const double = c + this.processing_stream[1] - if (double == '--') - { - this.state = State.Comment - return - } - - if (double == '[[') - { - this.state = State.MultiLineString - this.start_token() - this.consume() - this.consume() - return - } - - const dobule_token_type = double_token_map.get(double) - if (dobule_token_type != undefined) - { - this.peek_queue.push({ - data: double, - kind: dobule_token_type, - debug: { - line: this.line, - column: this.column, - }, - }) - this.consume() - this.consume() - return - } - } - - const single_token_type = single_token_map.get(c) - if (single_token_type != undefined) - { - this.peek_queue.push({ - data: c, - kind: single_token_type, - debug: { - line: this.line, - column: this.column, - }, - }) - this.consume() - return - } - - if (c == '"') - { - this.start_token() - this.consume() - this.state = State.StringLiteral - return - } - - if (/[a-zA-Z_]/.test(c)) - { - this.start_token() - this.state = State.Identifier - return - } - - if (/[0-9]/.test(c)) - { - this.start_token() - this.state = State.NumberLiteral - return - } - } - - private read_string() - { - const c = this.current() - this.consume() - - if (c == '"') - { - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.StringLiteral, - debug: this.token_start_debug, - }) - - this.state = State.Initial - return - } - - if (c == '\\') - { - this.state = State.StringLiteralEscape - return - } - - this.buffer += c - } - - private read_string_escape() - { - const c = this.current() - this.consume() - this.state = State.StringLiteral - - switch (c) - { - case 'n': this.buffer += '\n'; break - case '0': this.buffer += '\0'; break - case 'r': this.buffer += '\r'; break - case 't': this.buffer += '\t'; break - default: - this.buffer += c - break - } - } - - private read_multi_line_string() - { - const c = this.current() ?? '\0' - this.consume() - - if (c + this.current() == ']]') - { - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.StringLiteral, - debug: this.token_start_debug, - }) - - this.consume() - this.state = State.Initial - return - } - - this.buffer += c - } - - private read_identifier() - { - const c = this.current() ?? '\0' - if (!/[a-zA-Z0-9_]/.test(c)) - { - const kind = keyword_map.get(this.buffer) - this.peek_queue.push({ - data: this.buffer, - kind: kind ?? TokenKind.Identifier, - debug: this.token_start_debug, - }) - - this.state = State.Initial - return - } - - this.buffer += c - this.consume() - } - - private number() - { - const c = this.current() ?? '\0' - if (/[0-9]/.test(c)) - { - this.buffer += c - this.consume() - return - } - - if (c == '.') - { - this.buffer += c - this.consume() - this.state = State.NumberLiteralDot - return - } - - if (c == 'e' || c == 'E') - { - this.buffer += c - this.consume() - this.state = State.NumberLiteralExp - return - } - - if (c == 'x') - { - if (this.buffer != '0') - { - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - return - } - - this.buffer += c - this.state = State.NumberHex - this.consume() - return - } - - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - } - - private number_dot() - { - const c = this.current() ?? '\0' - if (/[0-9]/.test(c)) - { - this.buffer += c - this.consume() - return - } - - if (c == 'e' || c == 'E') - { - this.buffer += c - this.state = State.NumberLiteralExpSign - this.consume() - return - } - - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - } - - private number_exp_sign() - { - const c = this.current() ?? '\0' - if (/[0-9+-]/.test(c)) - { - this.buffer += c - this.consume() - this.state = State.NumberLiteralExp - return - } - - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - } - - private number_exp() - { - const c = this.current() ?? '\0' - if (/[0-9]/.test(c)) - { - this.buffer += c - this.consume() - return - } - - this.peek_queue.push({ - data: this.buffer, - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - } - - private number_hex() - { - const c = this.current() ?? '\0' - if (/[0-9a-fA-F]/.test(c)) - { - this.buffer += c - this.consume() - return - } - - this.peek_queue.push({ - data: parseInt(this.buffer.slice(2), 16).toString(), - kind: TokenKind.NumberLiteral, - debug: this.token_start_debug, - }) - this.state = State.Initial - } - - private comment() - { - const c = this.current() - this.consume() - - if (c == '\n') - this.state = State.Initial - } - - private on_char() - { - if (this.current() == undefined) - { - this.peek_queue.push({ - data: '', - kind: this.state == State.Initial - ? TokenKind.EOF - : TokenKind.NotFinished, - debug: { - line: this.line, - column: this.column, - }, - }) - - return - } - - switch (this.state) - { - case State.Initial: - this.initial() - break - case State.Identifier: - this.read_identifier() - break - case State.StringLiteral: - this.read_string() - break - case State.StringLiteralEscape: - this.read_string_escape() - break - case State.MultiLineString: - this.read_multi_line_string() - break - case State.NumberLiteral: - this.number() - break - case State.NumberLiteralDot: - this.number_dot() - break - case State.NumberLiteralExpSign: - this.number_exp_sign() - break - case State.NumberLiteralExp: - this.number_exp() - break - case State.NumberHex: - this.number_hex() - break - case State.Comment: - this.comment() - break - } - } - - feed(stream: string) - { - this.processing_stream.push(...stream.split('')) - this.end_of_stream = false - } - - next(): Token - { - if (this.peek_queue.length == 0) - this.peek() - return this.peek_queue.shift() - } - - peek(count = 1): Token - { - while (this.peek_queue.length < count) - this.on_char() - return this.peek_queue[count - 1] - } + private readonly processing_stream: string[] + private readonly peek_queue: Token[] + + private state: State + private end_of_stream: boolean = false + private buffer: string + private token_start_debug: Debug + + private line: number + private column: number + + constructor() + { + this.state = State.Initial + this.processing_stream = [] + this.buffer = '' + this.token_start_debug = { line: 0, column: 0 } + + this.line = 1 + this.column = 1 + this.peek_queue = [] + } + + private current(): string | undefined + { + if (this.processing_stream.length > 0) + return this.processing_stream[0] + + if (this.end_of_stream) + return undefined + + this.end_of_stream = true + return '\0' + } + + private consume() + { + if (this.processing_stream.length <= 0) + return + + this.column += 1 + if (this.processing_stream.shift() == '\n') + { + this.line += 1 + this.column = 1 + } + } + + private start_token() + { + this.token_start_debug = { + line: this.line, + column: this.column, + } + this.buffer = '' + } + + private initial() + { + if (this.processing_stream.length == 0) + { + this.peek_queue.push({ + data: '', + kind: TokenKind.EOF, + debug: { + line: this.line, + column: this.column, + }, + }) + + return + } + + const c = this.current() ?? '\0' + if (/\s/.test(c)) + return this.consume() + + if (this.processing_stream.length > 1) + { + const double = c + this.processing_stream[1] + if (double == '--') + { + this.state = State.Comment + return + } + + if (double == '[[') + { + this.state = State.MultiLineString + this.start_token() + this.consume() + this.consume() + return + } + + const dobule_token_type = double_token_map.get(double) + if (dobule_token_type != undefined) + { + this.peek_queue.push({ + data: double, + kind: dobule_token_type, + debug: { + line: this.line, + column: this.column, + }, + }) + this.consume() + this.consume() + return + } + } + + const single_token_type = single_token_map.get(c) + if (single_token_type != undefined) + { + this.peek_queue.push({ + data: c, + kind: single_token_type, + debug: { + line: this.line, + column: this.column, + }, + }) + this.consume() + return + } + + if (c == '"') + { + this.start_token() + this.consume() + this.state = State.StringLiteral + return + } + + if (/[a-zA-Z_]/.test(c)) + { + this.start_token() + this.state = State.Identifier + return + } + + if (/[0-9]/.test(c)) + { + this.start_token() + this.state = State.NumberLiteral + return + } + } + + private read_string() + { + const c = this.current() + this.consume() + + if (c == '"') + { + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.StringLiteral, + debug: this.token_start_debug, + }) + + this.state = State.Initial + return + } + + if (c == '\\') + { + this.state = State.StringLiteralEscape + return + } + + this.buffer += c + } + + private read_string_escape() + { + const c = this.current() + this.consume() + this.state = State.StringLiteral + + switch (c) + { + case 'n': this.buffer += '\n'; break + case '0': this.buffer += '\0'; break + case 'r': this.buffer += '\r'; break + case 't': this.buffer += '\t'; break + default: + this.buffer += c + break + } + } + + private read_multi_line_string() + { + const c = this.current() ?? '\0' + this.consume() + + if (c + this.current() == ']]') + { + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.StringLiteral, + debug: this.token_start_debug, + }) + + this.consume() + this.state = State.Initial + return + } + + this.buffer += c + } + + private read_identifier() + { + const c = this.current() ?? '\0' + if (!/[a-zA-Z0-9_]/.test(c)) + { + const kind = keyword_map.get(this.buffer) + this.peek_queue.push({ + data: this.buffer, + kind: kind ?? TokenKind.Identifier, + debug: this.token_start_debug, + }) + + this.state = State.Initial + return + } + + this.buffer += c + this.consume() + } + + private number() + { + const c = this.current() ?? '\0' + if (/[0-9]/.test(c)) + { + this.buffer += c + this.consume() + return + } + + if (c == '.') + { + this.buffer += c + this.consume() + this.state = State.NumberLiteralDot + return + } + + if (c == 'e' || c == 'E') + { + this.buffer += c + this.consume() + this.state = State.NumberLiteralExp + return + } + + if (c == 'x') + { + if (this.buffer != '0') + { + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + return + } + + this.buffer += c + this.state = State.NumberHex + this.consume() + return + } + + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + } + + private number_dot() + { + const c = this.current() ?? '\0' + if (/[0-9]/.test(c)) + { + this.buffer += c + this.consume() + return + } + + if (c == 'e' || c == 'E') + { + this.buffer += c + this.state = State.NumberLiteralExpSign + this.consume() + return + } + + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + } + + private number_exp_sign() + { + const c = this.current() ?? '\0' + if (/[0-9+-]/.test(c)) + { + this.buffer += c + this.consume() + this.state = State.NumberLiteralExp + return + } + + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + } + + private number_exp() + { + const c = this.current() ?? '\0' + if (/[0-9]/.test(c)) + { + this.buffer += c + this.consume() + return + } + + this.peek_queue.push({ + data: this.buffer, + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + } + + private number_hex() + { + const c = this.current() ?? '\0' + if (/[0-9a-fA-F]/.test(c)) + { + this.buffer += c + this.consume() + return + } + + this.peek_queue.push({ + data: parseInt(this.buffer.slice(2), 16).toString(), + kind: TokenKind.NumberLiteral, + debug: this.token_start_debug, + }) + this.state = State.Initial + } + + private comment() + { + const c = this.current() + this.consume() + + if (c == '\n') + this.state = State.Initial + } + + private on_char() + { + if (this.current() == undefined) + { + this.peek_queue.push({ + data: '', + kind: this.state == State.Initial + ? TokenKind.EOF + : TokenKind.NotFinished, + debug: { + line: this.line, + column: this.column, + }, + }) + + return + } + + switch (this.state) + { + case State.Initial: + this.initial() + break + case State.Identifier: + this.read_identifier() + break + case State.StringLiteral: + this.read_string() + break + case State.StringLiteralEscape: + this.read_string_escape() + break + case State.MultiLineString: + this.read_multi_line_string() + break + case State.NumberLiteral: + this.number() + break + case State.NumberLiteralDot: + this.number_dot() + break + case State.NumberLiteralExpSign: + this.number_exp_sign() + break + case State.NumberLiteralExp: + this.number_exp() + break + case State.NumberHex: + this.number_hex() + break + case State.Comment: + this.comment() + break + } + } + + feed(stream: string) + { + this.processing_stream.push(...stream.split('')) + this.end_of_stream = false + } + + next(): Token + { + if (this.peek_queue.length == 0) + this.peek() + return this.peek_queue.shift() + } + + peek(count = 1): Token + { + while (this.peek_queue.length < count) + this.on_char() + return this.peek_queue[count - 1] + } } - diff --git a/src/lib.mts b/src/lib.mts index 3032a48..342170e 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -12,768 +12,768 @@ let rand = xoroshiro([0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100 export function variable_size(value: Variable): number | undefined { - switch (value.data_type) - { - case DataType.String: return value.string?.length ?? 0 - case DataType.Table: - return ([...value.table?.keys() ?? []] - .filter(key => typeof key == 'number') as number[]) - .reduce((acc, key) => Math.max(acc, key), 0) - default: - return undefined - } + switch (value.data_type) + { + case DataType.String: return value.string?.length ?? 0 + case DataType.Table: + return ([...value.table?.keys() ?? []] + .filter(key => typeof key == 'number') as number[]) + .reduce((acc, key) => Math.max(acc, key), 0) + default: + return undefined + } } export function variable_to_string(variable: Variable, tables_done: Variable[] = []): string { - switch (variable.data_type) - { - case DataType.Nil: return 'nil' - case DataType.Boolean: return variable.boolean ? 'true' : 'false' - case DataType.Number: return variable.number?.toString() ?? '0' - case DataType.String: return variable.string ?? '' - case DataType.Function: return `` - case DataType.NativeFunction: return `` - case DataType.Table: - { - if (tables_done.includes(variable)) - return '...' - - tables_done.push(variable) - return `{ ${ [...variable.table?.entries() ?? []] - .map(([i, v]) => - `${ i } = ${ variable_to_string(v, tables_done) }`) - .join(', ') } }` - } - default: return '' - } + switch (variable.data_type) + { + case DataType.Nil: return 'nil' + case DataType.Boolean: return variable.boolean ? 'true' : 'false' + case DataType.Number: return variable.number?.toString() ?? '0' + case DataType.String: return variable.string ?? '' + case DataType.Function: return `` + case DataType.NativeFunction: return `` + case DataType.Table: + { + if (tables_done.includes(variable)) + return '...' + + tables_done.push(variable) + return `{ ${ [...variable.table?.entries() ?? []] + .map(([i, v]) => + `${ i } = ${ variable_to_string(v, tables_done) }`) + .join(', ') } }` + } + default: return '' + } } export function type_name(data_type: DataType): string { - switch (data_type) - { - case DataType.Nil: return 'nil' - case DataType.Boolean: return 'boolean' - case DataType.Number: return 'number' - case DataType.String: return 'string' - case DataType.Function: return 'function' - case DataType.NativeFunction: return 'function' - case DataType.Table: return 'table' - } + switch (data_type) + { + case DataType.Nil: return 'nil' + case DataType.Boolean: return 'boolean' + case DataType.Number: return 'number' + case DataType.String: return 'string' + case DataType.Function: return 'function' + case DataType.NativeFunction: return 'function' + case DataType.Table: return 'table' + } } function print(_: Engine, ...args: Variable[]): Variable[] { - console.log(...args.map(arg => variable_to_string(arg))) - return [nil] + console.log(...args.map(arg => variable_to_string(arg))) + return [nil] } function type(_: Engine, v: Variable): Variable[] { - return [make_string(type_name(v.data_type))] + return [make_string(type_name(v.data_type))] } function ipairs(_: Engine, table: Variable): Variable[] { - if (table.data_type != DataType.Table || table.table == undefined) - return [nil] + if (table.data_type != DataType.Table || table.table == undefined) + return [nil] - let index = 1 - return [{ - data_type: DataType.NativeFunction, - native_function: () => - { - const key = key_variable(index) - const value = table.table?.get(index++) - if (value == undefined) - return [nil] + let index = 1 + return [{ + data_type: DataType.NativeFunction, + native_function: () => + { + const key = key_variable(index) + const value = table.table?.get(index++) + if (value == undefined) + return [nil] - return [key, value] - }, - }] + return [key, value] + }, + }] } function next(_: Engine, { table }: Variable, index?: Variable): Variable[] { - if (table == undefined) - return [nil] + if (table == undefined) + return [nil] - const keys = table.keys() - if (index == undefined || index.data_type == DataType.Nil) - { - const first_key: number | string | undefined = keys.next().value - if (first_key == undefined) - return [nil] + const keys = table.keys() + if (index == undefined || index.data_type == DataType.Nil) + { + const first_key: number | string | undefined = keys.next().value + if (first_key == undefined) + return [nil] - return [key_variable(first_key), table.get(first_key) ?? nil] - } + return [key_variable(first_key), table.get(first_key) ?? nil] + } - let next_key: number | string | undefined = keys.next().value - while (next_key != undefined && next_key != index.number && next_key != index.string) - next_key = keys.next().value + let next_key: number | string | undefined = keys.next().value + while (next_key != undefined && next_key != index.number && next_key != index.string) + next_key = keys.next().value - next_key = keys.next().value - if (next_key == undefined) - return [nil] + next_key = keys.next().value + if (next_key == undefined) + return [nil] - return [key_variable(next_key), table.get(next_key) ?? nil] + return [key_variable(next_key), table.get(next_key) ?? nil] } function pairs(_: Engine, table: Variable): Variable[] { - const next_func = { data_type: DataType.NativeFunction, native_function: next } - return [next_func, table, nil] + const next_func = { data_type: DataType.NativeFunction, native_function: next } + return [next_func, table, nil] } function range(_: Engine, count: Variable): Variable[] { - let index = 0 - return [{ - data_type: DataType.NativeFunction, - native_function: () => - { - index += 1 - if (index >= (count.number ?? 0)) - return [nil] - return [{ data_type: DataType.Number, number: index }] - }, - }] + let index = 0 + return [{ + data_type: DataType.NativeFunction, + native_function: () => + { + index += 1 + if (index >= (count.number ?? 0)) + return [nil] + return [{ data_type: DataType.Number, number: index }] + }, + }] } function random(_: Engine): Variable[] { - return [{ data_type: DataType.Number, number: Math.random() }] + return [{ data_type: DataType.Number, number: Math.random() }] } function is_empty(_: Engine, table: Variable): Variable[] { - const len = table.table?.size ?? 0 - return [{ data_type: DataType.Boolean, boolean: len == 0 }] + const len = table.table?.size ?? 0 + return [{ data_type: DataType.Boolean, boolean: len == 0 }] } function key_variable(key: number | string): Variable { - if (typeof key == 'number') - return make_number(key) - else if (typeof key == 'string') - return make_string(key) - else - return nil + if (typeof key == 'number') + return make_number(key) + else if (typeof key == 'string') + return make_string(key) + else + return nil } function sort(engine: Engine, table: Variable, by: Variable): Variable[] { - const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] - entries.sort(([_, a], [__, b]) => - { - const result = engine.call(by, a, b) - if (result instanceof Error) - return 0 + const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] + entries.sort(([_, a], [__, b]) => + { + const result = engine.call(by, a, b) + if (result instanceof Error) + return 0 - return result.at(0)?.number ?? 0 - }) + return result.at(0)?.number ?? 0 + }) - const numbered_entries = entries.map(([key, _], i) => [i + 1, key_variable(key)] as const) - return [{ data_type: DataType.Table, table: new Map(numbered_entries) }] + const numbered_entries = entries.map(([key, _], i) => [i + 1, key_variable(key)] as const) + return [{ data_type: DataType.Table, table: new Map(numbered_entries) }] } function find(engine: Engine, table: Variable, matches: Variable): Variable[] { - const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] - const found = entries.find(([_, a]) => - { - const result = engine.call(matches, a) - if (result instanceof Error) - return 0 + const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] + const found = entries.find(([_, a]) => + { + const result = engine.call(matches, a) + if (result instanceof Error) + return 0 - return result.at(0)?.boolean ?? 0 - }) + return result.at(0)?.boolean ?? 0 + }) - if (found == undefined) - return [nil] + if (found == undefined) + return [nil] - const [key, _] = found - if (typeof key == 'number') - return [make_number(key)] - else if (typeof key == 'string') - return [make_string(key)] - else - return [nil] + const [key, _] = found + if (typeof key == 'number') + return [make_number(key)] + else if (typeof key == 'string') + return [make_string(key)] + else + return [nil] } function first(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] + if (table.table == undefined) + return [nil] - const key = table.table.keys().next().value - if (typeof key == 'string') - return [make_string(key)] - else if (typeof key == 'number') - return [make_number(key)] - else - return [nil] + const key = table.table.keys().next().value + if (typeof key == 'string') + return [make_string(key)] + else if (typeof key == 'number') + return [make_number(key)] + else + return [nil] } function keys(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] + if (table.table == undefined) + return [nil] - const keys = [...table.table.keys()] - const entries = keys.map((key, i) => [i + 1, key_variable(key)] as const) - return [{ data_type: DataType.Table, table: new Map(entries) }] + const keys = [...table.table.keys()] + const entries = keys.map((key, i) => [i + 1, key_variable(key)] as const) + return [{ data_type: DataType.Table, table: new Map(entries) }] } function values(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] + if (table.table == undefined) + return [nil] - const values = [...table.table.values()] - const entries = values.map((value, i) => [i + 1, value] as const) - return [{ data_type: DataType.Table, table: new Map(entries) }] + const values = [...table.table.values()] + const entries = values.map((value, i) => [i + 1, value] as const) + return [{ data_type: DataType.Table, table: new Map(entries) }] } function to_number(_: Engine, arg: Variable): Variable[] { - switch (arg.data_type) - { - case DataType.Number: - return [arg] - case DataType.String: - return [make_number(parseFloat(arg.string ?? '0'))] - default: - return [nil] - } + switch (arg.data_type) + { + case DataType.Number: + return [arg] + case DataType.String: + return [make_number(parseFloat(arg.string ?? '0'))] + default: + return [nil] + } } function to_string(_: Engine, arg: Variable): Variable[] { - return [make_string(variable_to_string(arg))] + return [make_string(variable_to_string(arg))] } function assert(engine: Engine, condition: Variable, message?: Variable): Variable[] { - if (!(condition.boolean ?? false)) - engine.raise_error(message?.string ?? 'assertion failed!') + if (!(condition.boolean ?? false)) + engine.raise_error(message?.string ?? 'assertion failed!') - return [nil] + return [nil] } function error(engine: Engine, message: Variable): Variable[] { - engine.raise_error(message?.string ?? '') - return [nil] + engine.raise_error(message?.string ?? '') + return [nil] } let warnings_on = true function warn(_: Engine, ...messages: Variable[]): Variable[] { - if (messages.length == 1) - { - switch (messages[0].string) - { - case '@on': - warnings_on = true - break - case '@off': - warnings_on = false - break - } - } + if (messages.length == 1) + { + switch (messages[0].string) + { + case '@on': + warnings_on = true + break + case '@off': + warnings_on = false + break + } + } - if (warnings_on) - console.error('WARNING', messages.map(x => variable_to_string(x))) - return [nil] + if (warnings_on) + console.error('WARNING', messages.map(x => variable_to_string(x))) + return [nil] } function select(_: Engine, index: Variable, ...args: Variable[]): Variable[] { - if (index.number != undefined) - { - if (index.number > 0) - return args.slice(index.number - 1) - else - return args.slice(args.length + index.number - 1) - } + if (index.number != undefined) + { + if (index.number > 0) + return args.slice(index.number - 1) + else + return args.slice(args.length + index.number - 1) + } - if (index.string == '#') - return [make_number(args.length)] + if (index.string == '#') + return [make_number(args.length)] - return [nil] + return [nil] } function string_byte(_: Engine, s: Variable, i?: Variable, j?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + if (s.string == undefined) + return [nil] - const start = i?.number ?? 1 - const end = j?.number ?? start - const bytes: Variable[] = [] - for (let index = start - 1; index <= end - 1; index++) - bytes.push(make_number(s.string.charCodeAt(index))) - return bytes + const start = i?.number ?? 1 + const end = j?.number ?? start + const bytes: Variable[] = [] + for (let index = start - 1; index <= end - 1; index++) + bytes.push(make_number(s.string.charCodeAt(index))) + return bytes } function string_char(_: Engine, ...chars: Variable[]): Variable[] { - const s = String.fromCharCode(...chars - .filter(x => x.number != undefined) - .map(c => c.number ?? 0)) - return [make_string(s)] + const s = String.fromCharCode(...chars + .filter(x => x.number != undefined) + .map(c => c.number ?? 0)) + return [make_string(s)] } function string_format(_: Engine, format: Variable, ...args: Variable[]): Variable[] { - if (format.string == undefined) - return [nil] - - let result = '' - let is_format = false - let arg_index = 0 - for (const char of format.string) - { - if (is_format) - { - switch (char) - { - case 'd': result += Math.floor(args[arg_index++].number ?? 0).toString(); break - case 'f': result += args[arg_index++].number?.toString(); break - case 's': result += variable_to_string(args[arg_index++]); break - default: - result += `%${ char }` - } - is_format = false - } - else - { - if (char == '%') - is_format = true - else - result += char - } - } - - return [make_string(result)] + if (format.string == undefined) + return [nil] + + let result = '' + let is_format = false + let arg_index = 0 + for (const char of format.string) + { + if (is_format) + { + switch (char) + { + case 'd': result += Math.floor(args[arg_index++].number ?? 0).toString(); break + case 'f': result += args[arg_index++].number?.toString(); break + case 's': result += variable_to_string(args[arg_index++]); break + default: + result += `%${ char }` + } + is_format = false + } + else + { + if (char == '%') + is_format = true + else + result += char + } + } + + return [make_string(result)] } function string_find(_: Engine, s: Variable, pattern: Variable, init?: Variable, plain?: Variable): Variable[] { - if (s.string == undefined || pattern.string == undefined) - return [nil] + if (s.string == undefined || pattern.string == undefined) + return [nil] - const offset = init?.number ?? 1 - const str = s.string.slice(offset - 1) + const offset = init?.number ?? 1 + const str = s.string.slice(offset - 1) - if (plain?.boolean == true) - return [make_number(str.indexOf(pattern.string) + 1)] + if (plain?.boolean == true) + return [make_number(str.indexOf(pattern.string) + 1)] - const results = RegExp(pattern.string).exec(str) - if (results == null || results.length == 0) - return [nil] + const results = RegExp(pattern.string).exec(str) + if (results == null || results.length == 0) + return [nil] - const index = s.string.indexOf(results[0]) - return [make_number(index + 1)] + const index = s.string.indexOf(results[0]) + return [make_number(index + 1)] } function string_len(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_number(s.string.length)] + return [s.string == undefined ? nil : make_number(s.string.length)] } function string_lower(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_string(s.string.toLowerCase())] + return [s.string == undefined ? nil : make_string(s.string.toLowerCase())] } function string_rep(_: Engine, s: Variable, n: Variable, sep?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + if (s.string == undefined) + return [nil] - return [make_string(new Array(n.number ?? 0) - .fill(s.string) - .join(sep?.string ?? ''))] + return [make_string(new Array(n.number ?? 0) + .fill(s.string) + .join(sep?.string ?? ''))] } function string_sub(_: Engine, s: Variable, i: Variable, j?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + if (s.string == undefined) + return [nil] - const start = i.number ?? 1 - return [make_string(s.string.slice(start - 1, j?.number))] + const start = i.number ?? 1 + return [make_string(s.string.slice(start - 1, j?.number))] } function string_upper(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_string(s.string.toUpperCase())] + return [s.string == undefined ? nil : make_string(s.string.toUpperCase())] } function string_reverse(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : - make_string(s.string.split('').reverse().join(''))] + return [s.string == undefined ? nil : + make_string(s.string.split('').reverse().join(''))] } function table_concat(_: Engine, list: Variable, sep?: Variable, i?: Variable, j?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + if (list.table == undefined) + return [nil] - const seporator = sep?.string ?? '' - const start = i?.number ?? 1 - const result = [...list.table.values()] - .slice(start - 1, j?.number) - .map(item => variable_to_string(item)) - .join(seporator) - return [make_string(result)] + const seporator = sep?.string ?? '' + const start = i?.number ?? 1 + const result = [...list.table.values()] + .slice(start - 1, j?.number) + .map(item => variable_to_string(item)) + .join(seporator) + return [make_string(result)] } function table_insert(_: Engine, list: Variable, arg_a?: Variable, arg_b?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + if (list.table == undefined) + return [nil] - const size = variable_size(list) ?? 0 - let pos = size + 1 - let value = arg_a ?? nil - if (arg_b != undefined) - { - pos = arg_a?.number ?? pos - value = arg_b ?? nil - } + const size = variable_size(list) ?? 0 + let pos = size + 1 + let value = arg_a ?? nil + if (arg_b != undefined) + { + pos = arg_a?.number ?? pos + value = arg_b ?? nil + } - for (let i = size + 1; i > pos; --i) - list.table.set(i, list.table.get(i - 1) ?? nil) - list.table.set(pos, value) - return [nil] + for (let i = size + 1; i > pos; --i) + list.table.set(i, list.table.get(i - 1) ?? nil) + list.table.set(pos, value) + return [nil] } function table_move(_: Engine, a1: Variable, f: Variable, e: Variable, t: Variable, a2?: Variable): Variable[] { - a2 = a2 ?? a1 - if (a1.table == undefined || a2.table == undefined) - return [nil] + a2 = a2 ?? a1 + if (a1.table == undefined || a2.table == undefined) + return [nil] - if (f.number == undefined || e.number == undefined || t.number == undefined) - return [nil] + if (f.number == undefined || e.number == undefined || t.number == undefined) + return [nil] - const src_start = f.number - const src_end = e.number - const dest_start = t.number - const count = src_end - src_start - for (let index = 0; index <= count; index++) - a2.table.set(dest_start + index, a1.table.get(src_start + index) ?? nil) + const src_start = f.number + const src_end = e.number + const dest_start = t.number + const count = src_end - src_start + for (let index = 0; index <= count; index++) + a2.table.set(dest_start + index, a1.table.get(src_start + index) ?? nil) - return [a2] + return [a2] } function table_pack(_: Engine, ...args: Variable[]): Variable[] { - const elements = args - .map((item, i) => [i + 1, item] as [number | string, Variable]) + const elements = args + .map((item, i) => [i + 1, item] as [number | string, Variable]) - return [{ - data_type: DataType.Table, - table: new Map([...elements, ['n', make_number(args.length)]]), - }] + return [{ + data_type: DataType.Table, + table: new Map([...elements, ['n', make_number(args.length)]]), + }] } function table_remove(_: Engine, list: Variable, pos?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + if (list.table == undefined) + return [nil] - const size = variable_size(list) ?? 0 - const remove_index = pos?.number ?? (size + 1) - const deleted_value = list.table.get(remove_index) ?? nil + const size = variable_size(list) ?? 0 + const remove_index = pos?.number ?? (size + 1) + const deleted_value = list.table.get(remove_index) ?? nil - for (let index = remove_index; index < size; index++) - list.table.set(index, list.table.get(index + 1) ?? nil) - list.table.delete(size) + for (let index = remove_index; index < size; index++) + list.table.set(index, list.table.get(index + 1) ?? nil) + list.table.delete(size) - return [deleted_value] + return [deleted_value] } function table_unpack(_: Engine, list: Variable, i?: Variable, j?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + if (list.table == undefined) + return [nil] - const size = variable_size(list) ?? 0 - const start = i?.number ?? 1 - const end = j?.number ?? size - return [...list.table.values()].splice(start - 1, end) + const size = variable_size(list) ?? 0 + const start = i?.number ?? 1 + const end = j?.number ?? size + return [...list.table.values()].splice(start - 1, end) } function math_abs(_: Engine, x: Variable): Variable[] { - return [make_number(Math.abs(x.number ?? 0))] + return [make_number(Math.abs(x.number ?? 0))] } function math_acos(_: Engine, x: Variable): Variable[] { - return [make_number(Math.acos(x.number ?? 0))] + return [make_number(Math.acos(x.number ?? 0))] } function math_asin(_: Engine, x: Variable): Variable[] { - return [make_number(Math.asin(x.number ?? 0))] + return [make_number(Math.asin(x.number ?? 0))] } function math_atan(_: Engine, x: Variable): Variable[] { - return [make_number(Math.atan(x.number ?? 0))] + return [make_number(Math.atan(x.number ?? 0))] } function math_ceil(_: Engine, x: Variable): Variable[] { - return [make_number(Math.ceil(x.number ?? 0))] + return [make_number(Math.ceil(x.number ?? 0))] } function math_cos(_: Engine, x: Variable): Variable[] { - return [make_number(Math.cos(x.number ?? 0))] + return [make_number(Math.cos(x.number ?? 0))] } function math_deg(_: Engine, x: Variable): Variable[] { - return [make_number((x.number ?? 0.0) * (180.0/Math.PI))] + return [make_number((x.number ?? 0.0) * (180.0/Math.PI))] } function math_exp(_: Engine, x: Variable): Variable[] { - return [make_number(Math.exp(x.number ?? 0.0))] + return [make_number(Math.exp(x.number ?? 0.0))] } function math_floor(_: Engine, x: Variable): Variable[] { - return [make_number(Math.floor(x.number ?? 0.0))] + return [make_number(Math.floor(x.number ?? 0.0))] } function math_fmod(_: Engine, x: Variable, y: Variable): Variable[] { - return [make_number((x.number ?? 0) % (y.number ?? 0))] + return [make_number((x.number ?? 0) % (y.number ?? 0))] } function math_log(_: Engine, x: Variable, base: Variable): Variable[] { - return [make_number(Math.log(x.number ?? 0) / Math.log(base.number ?? 0))] + return [make_number(Math.log(x.number ?? 0) / Math.log(base.number ?? 0))] } function math_max(_: Engine, ...args: Variable[]): Variable[] { - const max = args.reduce((acc, x) => - x.number == undefined ? acc : Math.max(acc, x.number), -Infinity) - return [make_number(max)] + const max = args.reduce((acc, x) => + x.number == undefined ? acc : Math.max(acc, x.number), -Infinity) + return [make_number(max)] } function math_min(_: Engine, ...args: Variable[]): Variable[] { - const min = args.reduce((acc, x) => - x.number == undefined ? acc : Math.min(acc, x.number), Infinity) - return [make_number(min)] + const min = args.reduce((acc, x) => + x.number == undefined ? acc : Math.min(acc, x.number), Infinity) + return [make_number(min)] } function math_modf(_: Engine, x: Variable): Variable[] { - const n = x.number ?? 0 - const integral = Math.trunc(n) - return [make_number(integral), make_number(n - integral)] + const n = x.number ?? 0 + const integral = Math.trunc(n) + return [make_number(integral), make_number(n - integral)] } function math_rad(_: Engine, x: Variable): Variable[] { - return [make_number((x.number ?? 0.0) * (Math.PI/180.0))] + return [make_number((x.number ?? 0.0) * (Math.PI/180.0))] } function math_random(_: Engine, m?: Variable, n?: Variable): Variable[] { - if (m == undefined && n == undefined) - return [make_number(rand() / 0xFFFFFFFF)] + if (m == undefined && n == undefined) + return [make_number(rand() / 0xFFFFFFFF)] - if (n == undefined) - { - const max = m?.number ?? 0 - if (max == 0) - return [make_number(rand())] + if (n == undefined) + { + const max = m?.number ?? 0 + if (max == 0) + return [make_number(rand())] - return [make_number(Math.floor(rand() % max) + 1)] - } + return [make_number(Math.floor(rand() % max) + 1)] + } - const min = m?.number ?? 1 - const max = (n?.number ?? 1) - min - return [make_number(Math.floor(rand() % max) + min)] + const min = m?.number ?? 1 + const max = (n?.number ?? 1) - min + return [make_number(Math.floor(rand() % max) + min)] } function xoroshiro(s: bigint[]) { - const rotl = (x: bigint, k: bigint) => - BigInt.asUintN(64, (x << k) | (x >> (BigInt(64) - k))) + const rotl = (x: bigint, k: bigint) => + BigInt.asUintN(64, (x << k) | (x >> (BigInt(64) - k))) - return () => - { - const result = rotl(BigInt.asUintN(64, s[0] + s[3]), BigInt(23)) + s[0] - const t = BigInt.asUintN(64, s[1] << BigInt(17)) - s[2] ^= s[0] - s[3] ^= s[1] - s[1] ^= s[2] - s[0] ^= s[3] - s[2] ^= t - s[3] = rotl(s[3], BigInt(45)) + return () => + { + const result = rotl(BigInt.asUintN(64, s[0] + s[3]), BigInt(23)) + s[0] + const t = BigInt.asUintN(64, s[1] << BigInt(17)) + s[2] ^= s[0] + s[3] ^= s[1] + s[1] ^= s[2] + s[0] ^= s[3] + s[2] ^= t + s[3] = rotl(s[3], BigInt(45)) - return Number(BigInt.asUintN(64, result) & BigInt(0xFFFFFFFF)) - } + return Number(BigInt.asUintN(64, result) & BigInt(0xFFFFFFFF)) + } } function math_randomseed(_: Engine, x?: Variable, y?: Variable): Variable[] { - const seed = [0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100))) - if (x?.number != undefined) - { - seed[0] = BigInt(x.number & 0xFFFFFFFF) - seed[1] = BigInt((x.number << 32) & 0xFFFFFFFF) - } + const seed = [0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100))) + if (x?.number != undefined) + { + seed[0] = BigInt(x.number & 0xFFFFFFFF) + seed[1] = BigInt((x.number << 32) & 0xFFFFFFFF) + } - if (y?.number != undefined) - { - seed[2] = BigInt(y.number & 0xFFFFFFFF) - seed[3] = BigInt((y.number << 32) & 0xFFFFFFFF) - } + if (y?.number != undefined) + { + seed[2] = BigInt(y.number & 0xFFFFFFFF) + seed[3] = BigInt((y.number << 32) & 0xFFFFFFFF) + } - rand = xoroshiro(seed) - return [nil] + rand = xoroshiro(seed) + return [nil] } function math_sin(_: Engine, x: Variable): Variable[] { - return [make_number(Math.sin(x.number ?? 0))] + return [make_number(Math.sin(x.number ?? 0))] } function math_sqrt(_: Engine, x: Variable): Variable[] { - return [make_number(Math.sqrt(x.number ?? 0))] + return [make_number(Math.sqrt(x.number ?? 0))] } function math_tan(_: Engine, x: Variable): Variable[] { - return [make_number(Math.tan(x.number ?? 0))] + return [make_number(Math.tan(x.number ?? 0))] } function math_tointeger(_: Engine, x: Variable): Variable[] { - switch (x.data_type) - { - case DataType.Number: - return [make_number(Math.floor(x.number ?? 0))] - case DataType.String: - return [make_number(Math.floor(parseFloat(x.string ?? '0')))] - default: - return [nil] - } + switch (x.data_type) + { + case DataType.Number: + return [make_number(Math.floor(x.number ?? 0))] + case DataType.String: + return [make_number(Math.floor(parseFloat(x.string ?? '0')))] + default: + return [nil] + } } function math_type(_: Engine, x: Variable): Variable[] { - if (x.number == undefined) - return [nil] + if (x.number == undefined) + return [nil] - if (Math.floor(x.number) == x.number) - return [make_string('integer')] - else - return [make_string('float')] + if (Math.floor(x.number) == x.number) + return [make_string('integer')] + else + return [make_string('float')] } function math_ult(_: Engine, m: Variable, n: Variable): Variable[] { - const buffer = Buffer.from(new ArrayBuffer(8)) - buffer.writeInt32LE(m.number ?? 0) - buffer.writeInt32LE(n.number ?? 0, 4) + const buffer = Buffer.from(new ArrayBuffer(8)) + buffer.writeInt32LE(m.number ?? 0) + buffer.writeInt32LE(n.number ?? 0, 4) - const m_unsigned = buffer.readUInt32LE() - const n_unsigned = buffer.readUInt32LE(4) - return [make_boolean(m_unsigned < n_unsigned)] + const m_unsigned = buffer.readUInt32LE() + const n_unsigned = buffer.readUInt32LE(4) + return [make_boolean(m_unsigned < n_unsigned)] } export function std_lib(): Map { - const global: Map = new Map([ - ['print', { data_type: DataType.NativeFunction, native_function: print }], - ['type', { data_type: DataType.NativeFunction, native_function: type }], - ['ipairs', { data_type: DataType.NativeFunction, native_function: ipairs }], - ['pairs', { data_type: DataType.NativeFunction, native_function: pairs }], - ['next', { data_type: DataType.NativeFunction, native_function: next }], - ['range', { data_type: DataType.NativeFunction, native_function: range }], - ['random', { data_type: DataType.NativeFunction, native_function: random }], - ['isempty', { data_type: DataType.NativeFunction, native_function: is_empty }], - ['sort', { data_type: DataType.NativeFunction, native_function: sort }], - ['find', { data_type: DataType.NativeFunction, native_function: find }], - ['first', { data_type: DataType.NativeFunction, native_function: first }], - ['keys', { data_type: DataType.NativeFunction, native_function: keys }], - ['values', { data_type: DataType.NativeFunction, native_function: values }], - ['tonumber', { data_type: DataType.NativeFunction, native_function: to_number }], - ['tostring', { data_type: DataType.NativeFunction, native_function: to_string }], - ['assert', { data_type: DataType.NativeFunction, native_function: assert }], - ['error', { data_type: DataType.NativeFunction, native_function: error }], - ['warn', { data_type: DataType.NativeFunction, native_function: warn }], - ['select', { data_type: DataType.NativeFunction, native_function: select }], - ['string', { data_type: DataType.Table, table: new Map([ - ['byte', { data_type: DataType.NativeFunction, native_function: string_byte }], - ['char', { data_type: DataType.NativeFunction, native_function: string_char }], - ['format', { data_type: DataType.NativeFunction, native_function: string_format }], - ['find', { data_type: DataType.NativeFunction, native_function: string_find }], - ['len', { data_type: DataType.NativeFunction, native_function: string_len }], - ['lower', { data_type: DataType.NativeFunction, native_function: string_lower }], - ['rep', { data_type: DataType.NativeFunction, native_function: string_rep }], - ['reverse', { data_type: DataType.NativeFunction, native_function: string_reverse }], - ['sub', { data_type: DataType.NativeFunction, native_function: string_sub }], - ['upper', { data_type: DataType.NativeFunction, native_function: string_upper }], - ]) }], - ['table', { data_type: DataType.Table, table: new Map([ - ['concat', { data_type: DataType.NativeFunction, native_function: table_concat }], - ['insert', { data_type: DataType.NativeFunction, native_function: table_insert }], - ['move', { data_type: DataType.NativeFunction, native_function: table_move }], - ['pack', { data_type: DataType.NativeFunction, native_function: table_pack }], - ['remove', { data_type: DataType.NativeFunction, native_function: table_remove }], - ['unpack', { data_type: DataType.NativeFunction, native_function: table_unpack }], - ]) }], - ['math', { data_type: DataType.Table, table: new Map([ - ['abs', { data_type: DataType.NativeFunction, native_function: math_abs }], - ['acos', { data_type: DataType.NativeFunction, native_function: math_acos }], - ['asin', { data_type: DataType.NativeFunction, native_function: math_asin }], - ['atan', { data_type: DataType.NativeFunction, native_function: math_atan }], - ['ceil', { data_type: DataType.NativeFunction, native_function: math_ceil }], - ['cos', { data_type: DataType.NativeFunction, native_function: math_cos }], - ['deg', { data_type: DataType.NativeFunction, native_function: math_deg }], - ['exp', { data_type: DataType.NativeFunction, native_function: math_exp }], - ['floor', { data_type: DataType.NativeFunction, native_function: math_floor }], - ['fmod', { data_type: DataType.NativeFunction, native_function: math_fmod }], - ['log', { data_type: DataType.NativeFunction, native_function: math_log }], - ['max', { data_type: DataType.NativeFunction, native_function: math_max }], - ['min', { data_type: DataType.NativeFunction, native_function: math_min }], - ['modf', { data_type: DataType.NativeFunction, native_function: math_modf }], - ['rad', { data_type: DataType.NativeFunction, native_function: math_rad }], - ['random', { data_type: DataType.NativeFunction, native_function: math_random }], - ['randomseed', { data_type: DataType.NativeFunction, native_function: math_randomseed }], - ['sin', { data_type: DataType.NativeFunction, native_function: math_sin }], - ['sqrt', { data_type: DataType.NativeFunction, native_function: math_sqrt }], - ['tan', { data_type: DataType.NativeFunction, native_function: math_tan }], - ['tointeger', { data_type: DataType.NativeFunction, native_function: math_tointeger }], - ['type', { data_type: DataType.NativeFunction, native_function: math_type }], - ['ult', { data_type: DataType.NativeFunction, native_function: math_ult }], - - ['pi', { data_type: DataType.Number, number: Math.PI }], - ['maxinteger', { data_type: DataType.Number, number: 0xFFFFFFFF }], - ['mininteger', { data_type: DataType.Number, number: -(0xFFFFFFFF - 1) }], - ['huge', { data_type: DataType.Number, number: Infinity }], - ]) }], - ]) - - global.set('_G', { data_type: DataType.Table, table: global }) - return global + const global: Map = new Map([ + ['print', { data_type: DataType.NativeFunction, native_function: print }], + ['type', { data_type: DataType.NativeFunction, native_function: type }], + ['ipairs', { data_type: DataType.NativeFunction, native_function: ipairs }], + ['pairs', { data_type: DataType.NativeFunction, native_function: pairs }], + ['next', { data_type: DataType.NativeFunction, native_function: next }], + ['range', { data_type: DataType.NativeFunction, native_function: range }], + ['random', { data_type: DataType.NativeFunction, native_function: random }], + ['isempty', { data_type: DataType.NativeFunction, native_function: is_empty }], + ['sort', { data_type: DataType.NativeFunction, native_function: sort }], + ['find', { data_type: DataType.NativeFunction, native_function: find }], + ['first', { data_type: DataType.NativeFunction, native_function: first }], + ['keys', { data_type: DataType.NativeFunction, native_function: keys }], + ['values', { data_type: DataType.NativeFunction, native_function: values }], + ['tonumber', { data_type: DataType.NativeFunction, native_function: to_number }], + ['tostring', { data_type: DataType.NativeFunction, native_function: to_string }], + ['assert', { data_type: DataType.NativeFunction, native_function: assert }], + ['error', { data_type: DataType.NativeFunction, native_function: error }], + ['warn', { data_type: DataType.NativeFunction, native_function: warn }], + ['select', { data_type: DataType.NativeFunction, native_function: select }], + ['string', { data_type: DataType.Table, table: new Map([ + ['byte', { data_type: DataType.NativeFunction, native_function: string_byte }], + ['char', { data_type: DataType.NativeFunction, native_function: string_char }], + ['format', { data_type: DataType.NativeFunction, native_function: string_format }], + ['find', { data_type: DataType.NativeFunction, native_function: string_find }], + ['len', { data_type: DataType.NativeFunction, native_function: string_len }], + ['lower', { data_type: DataType.NativeFunction, native_function: string_lower }], + ['rep', { data_type: DataType.NativeFunction, native_function: string_rep }], + ['reverse', { data_type: DataType.NativeFunction, native_function: string_reverse }], + ['sub', { data_type: DataType.NativeFunction, native_function: string_sub }], + ['upper', { data_type: DataType.NativeFunction, native_function: string_upper }], + ]) }], + ['table', { data_type: DataType.Table, table: new Map([ + ['concat', { data_type: DataType.NativeFunction, native_function: table_concat }], + ['insert', { data_type: DataType.NativeFunction, native_function: table_insert }], + ['move', { data_type: DataType.NativeFunction, native_function: table_move }], + ['pack', { data_type: DataType.NativeFunction, native_function: table_pack }], + ['remove', { data_type: DataType.NativeFunction, native_function: table_remove }], + ['unpack', { data_type: DataType.NativeFunction, native_function: table_unpack }], + ]) }], + ['math', { data_type: DataType.Table, table: new Map([ + ['abs', { data_type: DataType.NativeFunction, native_function: math_abs }], + ['acos', { data_type: DataType.NativeFunction, native_function: math_acos }], + ['asin', { data_type: DataType.NativeFunction, native_function: math_asin }], + ['atan', { data_type: DataType.NativeFunction, native_function: math_atan }], + ['ceil', { data_type: DataType.NativeFunction, native_function: math_ceil }], + ['cos', { data_type: DataType.NativeFunction, native_function: math_cos }], + ['deg', { data_type: DataType.NativeFunction, native_function: math_deg }], + ['exp', { data_type: DataType.NativeFunction, native_function: math_exp }], + ['floor', { data_type: DataType.NativeFunction, native_function: math_floor }], + ['fmod', { data_type: DataType.NativeFunction, native_function: math_fmod }], + ['log', { data_type: DataType.NativeFunction, native_function: math_log }], + ['max', { data_type: DataType.NativeFunction, native_function: math_max }], + ['min', { data_type: DataType.NativeFunction, native_function: math_min }], + ['modf', { data_type: DataType.NativeFunction, native_function: math_modf }], + ['rad', { data_type: DataType.NativeFunction, native_function: math_rad }], + ['random', { data_type: DataType.NativeFunction, native_function: math_random }], + ['randomseed', { data_type: DataType.NativeFunction, native_function: math_randomseed }], + ['sin', { data_type: DataType.NativeFunction, native_function: math_sin }], + ['sqrt', { data_type: DataType.NativeFunction, native_function: math_sqrt }], + ['tan', { data_type: DataType.NativeFunction, native_function: math_tan }], + ['tointeger', { data_type: DataType.NativeFunction, native_function: math_tointeger }], + ['type', { data_type: DataType.NativeFunction, native_function: math_type }], + ['ult', { data_type: DataType.NativeFunction, native_function: math_ult }], + + ['pi', { data_type: DataType.Number, number: Math.PI }], + ['maxinteger', { data_type: DataType.Number, number: 0xFFFFFFFF }], + ['mininteger', { data_type: DataType.Number, number: -(0xFFFFFFFF - 1) }], + ['huge', { data_type: DataType.Number, number: Infinity }], + ]) }], + ]) + + global.set('_G', { data_type: DataType.Table, table: global }) + return global } diff --git a/src/opcode.mts b/src/opcode.mts index ddb9b8c..37f8a55 100644 --- a/src/opcode.mts +++ b/src/opcode.mts @@ -8,132 +8,132 @@ import type { Variable } from './runtime.mts' import type { Debug } from './lexer.mts' export enum OpCode { - Load, - Store, - Push, - Pop, - Dup, - Swap, + Load, + Store, + Push, + Pop, + Dup, + Swap, - IterUpdateState, - IterNext, - IterJumpIfDone, + IterUpdateState, + IterNext, + IterJumpIfDone, - NewTable, - LoadIndex, - StoreIndex, + NewTable, + LoadIndex, + StoreIndex, - Add, - Subtract, - Multiply, - Divide, - FloorDivide, - Modulo, - Exponent, - Concat, + Add, + Subtract, + Multiply, + Divide, + FloorDivide, + Modulo, + Exponent, + Concat, - BitAnd, - BitOr, - BitXOr, - BitNot, - BitShiftLeft, - BitShiftRight, + BitAnd, + BitOr, + BitXOr, + BitNot, + BitShiftLeft, + BitShiftRight, - Equals, - NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, - And, - Or, + Equals, + NotEquals, + LessThen, + LessThenEquals, + GreaterThen, + GreaterThenEquals, + And, + Or, - Not, - Negate, - Length, - IsNotNil, + Not, + Negate, + Length, + IsNotNil, - StartBlock, - EndBlock, - MakeLocal, - Call, - Return, - Jump, - JumpIfNot, - JumpIf, + StartBlock, + EndBlock, + MakeLocal, + Call, + Return, + Jump, + JumpIfNot, + JumpIf, - StartStackChange, - EndStackChange, - ArgumentCount, - Break, + StartStackChange, + EndStackChange, + ArgumentCount, + Break, } export function op_code_name(op_code: OpCode): string { - switch (op_code) - { - case OpCode.Load: return 'Load' - case OpCode.Store: return 'Store' - case OpCode.Push: return 'Push' - case OpCode.Pop: return 'Pop' - case OpCode.Dup: return 'Dup' - case OpCode.Swap: return 'Swap' - case OpCode.IterUpdateState: return 'IterUpdateState' - case OpCode.IterNext: return 'IterNext' - case OpCode.IterJumpIfDone: return 'IterJumpIfDone' - case OpCode.NewTable: return 'NewTable' - case OpCode.LoadIndex: return 'LoadIndex' - case OpCode.StoreIndex: return 'StoreIndex' - case OpCode.Add: return 'Add' - case OpCode.Subtract: return 'Subtract' - case OpCode.Multiply: return 'Multiply' - case OpCode.Divide: return 'Divide' - case OpCode.FloorDivide: return 'FloorDivide' - case OpCode.Modulo: return 'Modulo' - case OpCode.Exponent: return 'Exponent' - case OpCode.Concat: return 'Concat' - case OpCode.BitAnd: return 'BitAnd' - case OpCode.BitOr: return 'BitOr' - case OpCode.BitXOr: return 'BitXOr' - case OpCode.BitNot: return 'BitNot' - case OpCode.BitShiftLeft: return 'BitShiftLeft' - case OpCode.BitShiftRight: return 'BitShiftRight' - case OpCode.Equals: return 'Equals' - case OpCode.NotEquals: return 'NotEquals' - case OpCode.LessThen: return 'LessThen' - case OpCode.LessThenEquals: return 'LessThenEquals' - case OpCode.GreaterThen: return 'GreaterThen' - case OpCode.GreaterThenEquals: return 'GreaterThenEquals' - case OpCode.And: return 'And' - case OpCode.Or: return 'Or' - case OpCode.Not: return 'Not' - case OpCode.Negate: return 'Negate' - case OpCode.Length: return 'Length' - case OpCode.IsNotNil: return 'IsNotNil' - case OpCode.StartBlock: return 'StartBlock' - case OpCode.EndBlock: return 'EndBlock' - case OpCode.MakeLocal: return 'MakeLocal' - case OpCode.Call: return 'Call' - case OpCode.Return: return 'Return' - case OpCode.Jump: return 'Jump' - case OpCode.JumpIfNot: return 'JumpIfNot' - case OpCode.JumpIf: return 'JumpIf' - case OpCode.StartStackChange: return 'StartStackChange' - case OpCode.EndStackChange: return 'EndStackChange' - case OpCode.ArgumentCount: return 'ArgumentCount' - case OpCode.Break: return 'Break[Debug]' - default: - throw new Error() - } + switch (op_code) + { + case OpCode.Load: return 'Load' + case OpCode.Store: return 'Store' + case OpCode.Push: return 'Push' + case OpCode.Pop: return 'Pop' + case OpCode.Dup: return 'Dup' + case OpCode.Swap: return 'Swap' + case OpCode.IterUpdateState: return 'IterUpdateState' + case OpCode.IterNext: return 'IterNext' + case OpCode.IterJumpIfDone: return 'IterJumpIfDone' + case OpCode.NewTable: return 'NewTable' + case OpCode.LoadIndex: return 'LoadIndex' + case OpCode.StoreIndex: return 'StoreIndex' + case OpCode.Add: return 'Add' + case OpCode.Subtract: return 'Subtract' + case OpCode.Multiply: return 'Multiply' + case OpCode.Divide: return 'Divide' + case OpCode.FloorDivide: return 'FloorDivide' + case OpCode.Modulo: return 'Modulo' + case OpCode.Exponent: return 'Exponent' + case OpCode.Concat: return 'Concat' + case OpCode.BitAnd: return 'BitAnd' + case OpCode.BitOr: return 'BitOr' + case OpCode.BitXOr: return 'BitXOr' + case OpCode.BitNot: return 'BitNot' + case OpCode.BitShiftLeft: return 'BitShiftLeft' + case OpCode.BitShiftRight: return 'BitShiftRight' + case OpCode.Equals: return 'Equals' + case OpCode.NotEquals: return 'NotEquals' + case OpCode.LessThen: return 'LessThen' + case OpCode.LessThenEquals: return 'LessThenEquals' + case OpCode.GreaterThen: return 'GreaterThen' + case OpCode.GreaterThenEquals: return 'GreaterThenEquals' + case OpCode.And: return 'And' + case OpCode.Or: return 'Or' + case OpCode.Not: return 'Not' + case OpCode.Negate: return 'Negate' + case OpCode.Length: return 'Length' + case OpCode.IsNotNil: return 'IsNotNil' + case OpCode.StartBlock: return 'StartBlock' + case OpCode.EndBlock: return 'EndBlock' + case OpCode.MakeLocal: return 'MakeLocal' + case OpCode.Call: return 'Call' + case OpCode.Return: return 'Return' + case OpCode.Jump: return 'Jump' + case OpCode.JumpIfNot: return 'JumpIfNot' + case OpCode.JumpIf: return 'JumpIf' + case OpCode.StartStackChange: return 'StartStackChange' + case OpCode.EndStackChange: return 'EndStackChange' + case OpCode.ArgumentCount: return 'ArgumentCount' + case OpCode.Break: return 'Break[Debug]' + default: + throw new Error() + } } export interface Op { - code: OpCode, - arg?: Variable, - debug: Debug, + code: OpCode, + arg?: Variable, + debug: Debug, } export interface Program { - code: Op[], - start: number, + code: Op[], + start: number, } diff --git a/src/optimizer.mts b/src/optimizer.mts index 570f8d0..fe57598 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -10,349 +10,348 @@ import type { Assignment, Value } from './ast' import { StatementKind, ExpressionKind, ValueKind } from './ast' const CONSTANT_VALUES = [ - ValueKind.NilLiteral, - ValueKind.NumberLiteral, - ValueKind.BooleanLiteral, - ValueKind.StringLiteral, + ValueKind.NilLiteral, + ValueKind.NumberLiteral, + ValueKind.BooleanLiteral, + ValueKind.StringLiteral, ] function compule_arithmatic_operation(expression: Expression, - operation: (a: number, b: number) => number, - constants: Map): Value | undefined + operation: (a: number, b: number) => number, + constants: Map): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) - return undefined - - return { - kind: ValueKind.NumberLiteral, - number: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), - token: expression.token, - } + const lhs = compute_constant_expression(expression.lhs, constants) + const rhs = compute_constant_expression(expression.rhs, constants) + if (lhs == undefined || rhs == undefined) + return undefined + + return { + kind: ValueKind.NumberLiteral, + number: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), + token: expression.token, + } } function compule_comparison_operation(expression: Expression, - operation: (a: number, b: number) => boolean, - constants: Map): Value | undefined + operation: (a: number, b: number) => boolean, + constants: Map): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) - return undefined - - return { - kind: ValueKind.BooleanLiteral, - boolean: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), - token: expression.token, - } + const lhs = compute_constant_expression(expression.lhs, constants) + const rhs = compute_constant_expression(expression.rhs, constants) + if (lhs == undefined || rhs == undefined) + return undefined + + return { + kind: ValueKind.BooleanLiteral, + boolean: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), + token: expression.token, + } } function compule_logical_operation(expression: Expression, - operation: (a: boolean, b: boolean) => boolean, - constants: Map): Value | undefined + operation: (a: boolean, b: boolean) => boolean, + constants: Map): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) - return undefined - - return { - kind: ValueKind.BooleanLiteral, - boolean: operation((lhs?.boolean ?? false), (rhs?.boolean ?? false)), - token: expression.token, - } + const lhs = compute_constant_expression(expression.lhs, constants) + const rhs = compute_constant_expression(expression.rhs, constants) + if (lhs == undefined || rhs == undefined) + return undefined + + return { + kind: ValueKind.BooleanLiteral, + boolean: operation((lhs?.boolean ?? false), (rhs?.boolean ?? false)), + token: expression.token, + } } function compute_constant_expression(expression: Expression | undefined, - constants: Map): Value | undefined + constants: Map): Value | undefined { - if (expression == undefined) - return undefined - - switch (expression.kind) - { - case ExpressionKind.Value: - { - if (expression.value == undefined) - return undefined - - const value = expression.value - if (CONSTANT_VALUES.includes(value.kind)) - return value - if (value.kind == ValueKind.Variable && constants.has(value.identifier ?? '')) - return constants.get(value.identifier ?? '') - return undefined - } - - case ExpressionKind.Addition: return compule_arithmatic_operation(expression, (a, b) => a + b, constants) - case ExpressionKind.Subtract: return compule_arithmatic_operation(expression, (a, b) => a - b, constants) - case ExpressionKind.Multiplication: return compule_arithmatic_operation(expression, (a, b) => a * b, constants) - case ExpressionKind.Division: return compule_arithmatic_operation(expression, (a, b) => a / b, constants) - case ExpressionKind.FloorDivision: return compule_arithmatic_operation(expression, (a, b) => Math.floor(a / b), constants) - case ExpressionKind.Modulo: return compule_arithmatic_operation(expression, (a, b) => a % b, constants) - case ExpressionKind.Exponent: return compule_arithmatic_operation(expression, (a, b) => Math.pow(a, b), constants) - case ExpressionKind.Concat: return undefined - - case ExpressionKind.BitAnd: return compule_arithmatic_operation(expression, (a, b) => a & b, constants) - case ExpressionKind.BitOr: return compule_arithmatic_operation(expression, (a, b) => a | b, constants) - case ExpressionKind.BitXOr: return compule_arithmatic_operation(expression, (a, b) => a ^ b, constants) - case ExpressionKind.BitShiftLeft: return compule_arithmatic_operation(expression, (a, b) => a << b, constants) - case ExpressionKind.BitShiftRight: return compule_arithmatic_operation(expression, (a, b) => a >> b, constants) - case ExpressionKind.BitNot: return undefined - - case ExpressionKind.Equals: return compule_comparison_operation(expression, (a, b) => a == b, constants) - case ExpressionKind.NotEquals: return compule_comparison_operation(expression, (a, b) => a != b, constants) - case ExpressionKind.LessThen: return compule_comparison_operation(expression, (a, b) => a < b, constants) - case ExpressionKind.LessThenEquals: return compule_comparison_operation(expression, (a, b) => a <= b, constants) - case ExpressionKind.GreaterThen: return compule_comparison_operation(expression, (a, b) => a > b, constants) - case ExpressionKind.GreaterThenEquals: return compule_comparison_operation(expression, (a, b) => a >= b, constants) - case ExpressionKind.And: return compule_logical_operation(expression, (a, b) => a && b, constants) - case ExpressionKind.Or: return compule_logical_operation(expression, (a, b) => a || b, constants) - - case ExpressionKind.Not: - { - const lhs = compute_constant_expression(expression.lhs, constants) - if (lhs == undefined) - return undefined - - return { - kind: ValueKind.BooleanLiteral, - boolean: !(lhs.boolean ?? false), - token: expression.token, - } - } - - case ExpressionKind.Negate: - { - const lhs = compute_constant_expression(expression.lhs, constants) - if (lhs == undefined) - return undefined - - return { - kind: ValueKind.NumberLiteral, - number: -(lhs.number ?? false), - token: expression.token, - } - } - - case ExpressionKind.Length: - return undefined - } + if (expression == undefined) + return undefined + + switch (expression.kind) + { + case ExpressionKind.Value: + { + if (expression.value == undefined) + return undefined + + const value = expression.value + if (CONSTANT_VALUES.includes(value.kind)) + return value + if (value.kind == ValueKind.Variable && constants.has(value.identifier ?? '')) + return constants.get(value.identifier ?? '') + return undefined + } + + case ExpressionKind.Addition: return compule_arithmatic_operation(expression, (a, b) => a + b, constants) + case ExpressionKind.Subtract: return compule_arithmatic_operation(expression, (a, b) => a - b, constants) + case ExpressionKind.Multiplication: return compule_arithmatic_operation(expression, (a, b) => a * b, constants) + case ExpressionKind.Division: return compule_arithmatic_operation(expression, (a, b) => a / b, constants) + case ExpressionKind.FloorDivision: return compule_arithmatic_operation(expression, (a, b) => Math.floor(a / b), constants) + case ExpressionKind.Modulo: return compule_arithmatic_operation(expression, (a, b) => a % b, constants) + case ExpressionKind.Exponent: return compule_arithmatic_operation(expression, (a, b) => Math.pow(a, b), constants) + case ExpressionKind.Concat: return undefined + + case ExpressionKind.BitAnd: return compule_arithmatic_operation(expression, (a, b) => a & b, constants) + case ExpressionKind.BitOr: return compule_arithmatic_operation(expression, (a, b) => a | b, constants) + case ExpressionKind.BitXOr: return compule_arithmatic_operation(expression, (a, b) => a ^ b, constants) + case ExpressionKind.BitShiftLeft: return compule_arithmatic_operation(expression, (a, b) => a << b, constants) + case ExpressionKind.BitShiftRight: return compule_arithmatic_operation(expression, (a, b) => a >> b, constants) + case ExpressionKind.BitNot: return undefined + + case ExpressionKind.Equals: return compule_comparison_operation(expression, (a, b) => a == b, constants) + case ExpressionKind.NotEquals: return compule_comparison_operation(expression, (a, b) => a != b, constants) + case ExpressionKind.LessThen: return compule_comparison_operation(expression, (a, b) => a < b, constants) + case ExpressionKind.LessThenEquals: return compule_comparison_operation(expression, (a, b) => a <= b, constants) + case ExpressionKind.GreaterThen: return compule_comparison_operation(expression, (a, b) => a > b, constants) + case ExpressionKind.GreaterThenEquals: return compule_comparison_operation(expression, (a, b) => a >= b, constants) + case ExpressionKind.And: return compule_logical_operation(expression, (a, b) => a && b, constants) + case ExpressionKind.Or: return compule_logical_operation(expression, (a, b) => a || b, constants) + + case ExpressionKind.Not: + { + const lhs = compute_constant_expression(expression.lhs, constants) + if (lhs == undefined) + return undefined + + return { + kind: ValueKind.BooleanLiteral, + boolean: !(lhs.boolean ?? false), + token: expression.token, + } + } + + case ExpressionKind.Negate: + { + const lhs = compute_constant_expression(expression.lhs, constants) + if (lhs == undefined) + return undefined + + return { + kind: ValueKind.NumberLiteral, + number: -(lhs.number ?? false), + token: expression.token, + } + } + + case ExpressionKind.Length: + return undefined + } } function optimize_expression(expression: Expression | undefined, - constants: Map) + constants: Map) { - if (expression == undefined) - return - - if (expression.value?.function != undefined) - { - optimize_chunk(expression.value.function.body, constants) - return - } - - const value = compute_constant_expression(expression, constants) - if (value != undefined) - { - expression.kind = ExpressionKind.Value - expression.value = value - return - } - - optimize_expression(expression.lhs, constants) - optimize_expression(expression.rhs, constants) - optimize_expression(expression.expression, constants) - optimize_expression(expression.index, constants) - - for (const argument of expression.arguments ?? []) - optimize_expression(argument, constants) + if (expression == undefined) + return + + if (expression.value?.function != undefined) + { + optimize_chunk(expression.value.function.body, constants) + return + } + + const value = compute_constant_expression(expression, constants) + if (value != undefined) + { + expression.kind = ExpressionKind.Value + expression.value = value + return + } + + optimize_expression(expression.lhs, constants) + optimize_expression(expression.rhs, constants) + optimize_expression(expression.expression, constants) + optimize_expression(expression.index, constants) + + for (const argument of expression.arguments ?? []) + optimize_expression(argument, constants) } function mark_local_constants(assignment: Assignment, constants: Map) { - for (const [index, rhs] of assignment.rhs.entries()) - { - if (index >= assignment.lhs.length) - continue - - const lhs = assignment.lhs[index] - if (lhs.kind != ExpressionKind.Value) - continue - if (lhs.value?.identifier == undefined) - continue - - const name = lhs.value.identifier - const value = compute_constant_expression(rhs, constants) - if (value == undefined) - continue - - constants.set(name, value) - } + for (const [index, rhs] of assignment.rhs.entries()) + { + if (index >= assignment.lhs.length) + continue + + const lhs = assignment.lhs[index] + if (lhs.kind != ExpressionKind.Value) + continue + if (lhs.value?.identifier == undefined) + continue + + const name = lhs.value.identifier + const value = compute_constant_expression(rhs, constants) + if (value == undefined) + continue + + constants.set(name, value) + } } function unmark_constants_if_reassigned(assignment: Assignment, constants: Map) { - for (const lhs of assignment.lhs) - { - if (lhs.kind != ExpressionKind.Value) - continue - if (lhs.value?.identifier == undefined) - continue - - const name = lhs.value.identifier - constants.delete(name) - } + for (const lhs of assignment.lhs) + { + if (lhs.kind != ExpressionKind.Value) + continue + if (lhs.value?.identifier == undefined) + continue + + const name = lhs.value.identifier + constants.delete(name) + } } function optimize_assignment(assignment: Assignment | undefined, - constants: Map) + constants: Map) { - if (assignment == undefined) - return + if (assignment == undefined) + return - if (assignment.local) - mark_local_constants(assignment, constants) - else - unmark_constants_if_reassigned(assignment, constants) + if (assignment.local) + mark_local_constants(assignment, constants) + else + unmark_constants_if_reassigned(assignment, constants) - for (const rhs of assignment.rhs) - optimize_expression(rhs, constants) + for (const rhs of assignment.rhs) + optimize_expression(rhs, constants) } function remove_constant_local_assignments(chunk: Chunk, - constants: Map) + constants: Map) { - for (const statement of chunk.statements) - { - if (statement.assignment == undefined || !statement.assignment.local) - continue - - const assignment = statement.assignment - for (const name of constants.keys()) - { - const index = assignment.lhs.findIndex( - x => x.value?.identifier == name) - - if (index < 0) - continue - assignment.lhs.splice(index, 1) - assignment.rhs.splice(index, 1) - } - } - - chunk.statements = chunk.statements - .filter(x => x.assignment == undefined || x.assignment.lhs.length > 0) + for (const statement of chunk.statements) + { + if (statement.assignment == undefined || !statement.assignment.local) + continue + + const assignment = statement.assignment + for (const name of constants.keys()) + { + const index = assignment.lhs.findIndex( + x => x.value?.identifier == name) + + if (index < 0) + continue + assignment.lhs.splice(index, 1) + assignment.rhs.splice(index, 1) + } + } + + chunk.statements = chunk.statements + .filter(x => x.assignment == undefined || x.assignment.lhs.length > 0) } function optimize_if(if_block: IfBlock | undefined, constants: Map) { - if (if_block == undefined) - return + if (if_block == undefined) + return - optimize_expression(if_block.condition, constants) - optimize_chunk(if_block.body, constants) + optimize_expression(if_block.condition, constants) + optimize_chunk(if_block.body, constants) } function optimize_while(while_block: While | undefined, constants: Map) { - if (while_block == undefined) - return + if (while_block == undefined) + return - optimize_expression(while_block.condition, constants) - optimize_chunk(while_block.body, constants) + optimize_expression(while_block.condition, constants) + optimize_chunk(while_block.body, constants) } function optimize_for(for_block: For | undefined, constants: Map) { - if (for_block == undefined) - return + if (for_block == undefined) + return - optimize_expression(for_block.itorator, constants) - optimize_chunk(for_block.body, constants) + optimize_expression(for_block.itorator, constants) + optimize_chunk(for_block.body, constants) } function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map) { - if (numeric_for_block == undefined) - return + if (numeric_for_block == undefined) + return - optimize_expression(numeric_for_block.start, constants) - optimize_expression(numeric_for_block.end, constants) - optimize_expression(numeric_for_block.step, constants) - optimize_chunk(numeric_for_block.body, constants) + optimize_expression(numeric_for_block.start, constants) + optimize_expression(numeric_for_block.end, constants) + optimize_expression(numeric_for_block.step, constants) + optimize_chunk(numeric_for_block.body, constants) } function optimize_repeat(repeat_block: Repeat | undefined, constants: Map) { - if (repeat_block == undefined) - return + if (repeat_block == undefined) + return - optimize_expression(repeat_block.condition, constants) - optimize_chunk(repeat_block.body, constants) + optimize_expression(repeat_block.condition, constants) + optimize_chunk(repeat_block.body, constants) } export function optimize_chunk(chunk: Chunk, parent_constants?: Map) { - const constants = new Map(parent_constants) - for (const statement of chunk.statements) - { - switch (statement.kind) - { - case StatementKind.Assignment: - optimize_assignment(statement.assignment, constants) - break - - case StatementKind.Expression: - optimize_expression(statement.expression, constants) - break - - case StatementKind.If: - optimize_if(statement.if, constants) - break - - case StatementKind.While: - optimize_while(statement.if, constants) - break - - case StatementKind.For: - optimize_for(statement.for, constants) - break - - case StatementKind.NumericFor: - optimize_numeric_for(statement.numeric_for, constants) - break - - case StatementKind.Repeat: - optimize_repeat(statement.repeat, constants) - break - - case StatementKind.Do: - if (statement.do != undefined) - optimize_chunk(statement.do.body, constants) - break - - case StatementKind.Return: - for (const expression of statement.return?.values ?? []) - optimize_expression(expression, constants) - break - - case StatementKind.Local: - case StatementKind.Break: - break - } - } - - if (parent_constants != undefined) - { - for (const name of [...parent_constants.keys()]) - { - if (!constants.has(name)) - parent_constants.delete(name) - } - } - - remove_constant_local_assignments(chunk, constants) + const constants = new Map(parent_constants) + for (const statement of chunk.statements) + { + switch (statement.kind) + { + case StatementKind.Assignment: + optimize_assignment(statement.assignment, constants) + break + + case StatementKind.Expression: + optimize_expression(statement.expression, constants) + break + + case StatementKind.If: + optimize_if(statement.if, constants) + break + + case StatementKind.While: + optimize_while(statement.if, constants) + break + + case StatementKind.For: + optimize_for(statement.for, constants) + break + + case StatementKind.NumericFor: + optimize_numeric_for(statement.numeric_for, constants) + break + + case StatementKind.Repeat: + optimize_repeat(statement.repeat, constants) + break + + case StatementKind.Do: + if (statement.do != undefined) + optimize_chunk(statement.do.body, constants) + break + + case StatementKind.Return: + for (const expression of statement.return?.values ?? []) + optimize_expression(expression, constants) + break + + case StatementKind.Local: + case StatementKind.Break: + break + } + } + + if (parent_constants != undefined) + { + for (const name of [...parent_constants.keys()]) + { + if (!constants.has(name)) + parent_constants.delete(name) + } + } + + remove_constant_local_assignments(chunk, constants) } - diff --git a/src/parser.mts b/src/parser.mts index 66e13cc..c6a6fc4 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -12,941 +12,941 @@ import { ExpressionKind, StatementKind, ValueKind } from './ast' import { TokenKind, TokenStream, token_kind_to_string } from './lexer.mts' const UNARY = [ - TokenKind.Not, - TokenKind.Subtract, - TokenKind.Hash, + TokenKind.Not, + TokenKind.Subtract, + TokenKind.Hash, ] const ORDERS = [ - [TokenKind.Or], - [TokenKind.And], - [TokenKind.LessThen, TokenKind.LessThenEquals, TokenKind.GreaterThen, TokenKind.GreaterThenEquals, TokenKind.Equals, TokenKind.NotEquals], - [TokenKind.BitOr], - [TokenKind.BitAnd], - [TokenKind.BitXOrNot], - [TokenKind.BitShiftLeft, TokenKind.BitShiftRight], - [TokenKind.Concat], - [TokenKind.Addition, TokenKind.Subtract], - [TokenKind.Multiply, TokenKind.Division, TokenKind.FloorDivision, TokenKind.Modulo], - [TokenKind.Exponent], + [TokenKind.Or], + [TokenKind.And], + [TokenKind.LessThen, TokenKind.LessThenEquals, TokenKind.GreaterThen, TokenKind.GreaterThenEquals, TokenKind.Equals, TokenKind.NotEquals], + [TokenKind.BitOr], + [TokenKind.BitAnd], + [TokenKind.BitXOrNot], + [TokenKind.BitShiftLeft, TokenKind.BitShiftRight], + [TokenKind.Concat], + [TokenKind.Addition, TokenKind.Subtract], + [TokenKind.Multiply, TokenKind.Division, TokenKind.FloorDivision, TokenKind.Modulo], + [TokenKind.Exponent], ] function error(token: Token, message: string): Error { - return new Error( - `${ token.debug.line }:${ token.debug.column }: ${ message }`) + return new Error( + `${ token.debug.line }:${ token.debug.column }: ${ message }`) } function expect(stream: TokenStream, kind: TokenKind): Token | Error { - const token = stream.peek() - if (token.kind != kind) - { - return error(token, - `expected '${ token_kind_to_string(kind) }', ` + - `got '${ token_kind_to_string(token.kind) }' instead`) - } + const token = stream.peek() + if (token.kind != kind) + { + return error(token, + `expected '${ token_kind_to_string(kind) }', ` + + `got '${ token_kind_to_string(token.kind) }' instead`) + } - return stream.next() + return stream.next() } function consume(stream: TokenStream, kind: TokenKind): boolean { - const token = stream.peek() - if (token.kind != kind) - return false + const token = stream.peek() + if (token.kind != kind) + return false - stream.next() - return true + stream.next() + return true } function parse_table_key(stream: TokenStream): Expression | Error { - if (consume(stream, TokenKind.OpenSquare)) - { - const element = parse_expression(stream) - if (element instanceof Error) - return element + if (consume(stream, TokenKind.OpenSquare)) + { + const element = parse_expression(stream) + if (element instanceof Error) + return element - const close_square = expect(stream, TokenKind.CloseSquare) - if (close_square instanceof Error) - return close_square + const close_square = expect(stream, TokenKind.CloseSquare) + if (close_square instanceof Error) + return close_square - return element - } + return element + } - const value = parse_value(stream) - if (value instanceof Error) - return value + const value = parse_value(stream) + if (value instanceof Error) + return value - if (value.kind == ValueKind.Variable) - { - value.kind = ValueKind.StringLiteral - value.string = value.identifier - value.identifier = undefined - } + if (value.kind == ValueKind.Variable) + { + value.kind = ValueKind.StringLiteral + value.string = value.identifier + value.identifier = undefined + } - return { - kind: ExpressionKind.Value, - token: value.token, - value: value, - } + return { + kind: ExpressionKind.Value, + token: value.token, + value: value, + } } function parse_table(stream: TokenStream): Value | Error { - const squigly_open = expect(stream, TokenKind.SquiglyOpen) - if (squigly_open instanceof Error) - return squigly_open - - const elements: Map = new Map() - let current_numeric_key = 1 - while (stream.peek().kind != TokenKind.SquiglyClose) - { - const element = parse_table_key(stream) - if (element instanceof Error) - return element - - if (consume(stream, TokenKind.Assign)) - { - const value = parse_expression(stream) - if (value instanceof Error) - return value - - elements.set(element, value) - } - else - { - const key_token = { - kind: TokenKind.NumberLiteral, - data: current_numeric_key.toString(), - debug: element.token.debug, - } - - const key = { - kind: ExpressionKind.Value, - token: key_token, - value: { - kind: ValueKind.NumberLiteral, - token: key_token, - number: current_numeric_key, - }, - } - - current_numeric_key += 1 - elements.set(key, element) - } - - if (!consume(stream, TokenKind.Comma)) - break - } - - const close_squigly = expect(stream, TokenKind.SquiglyClose) - if (close_squigly instanceof Error) - return close_squigly - - return { - kind: ValueKind.TableLiteral, - token: squigly_open, - table: elements, - } + const squigly_open = expect(stream, TokenKind.SquiglyOpen) + if (squigly_open instanceof Error) + return squigly_open + + const elements: Map = new Map() + let current_numeric_key = 1 + while (stream.peek().kind != TokenKind.SquiglyClose) + { + const element = parse_table_key(stream) + if (element instanceof Error) + return element + + if (consume(stream, TokenKind.Assign)) + { + const value = parse_expression(stream) + if (value instanceof Error) + return value + + elements.set(element, value) + } + else + { + const key_token = { + kind: TokenKind.NumberLiteral, + data: current_numeric_key.toString(), + debug: element.token.debug, + } + + const key = { + kind: ExpressionKind.Value, + token: key_token, + value: { + kind: ValueKind.NumberLiteral, + token: key_token, + number: current_numeric_key, + }, + } + + current_numeric_key += 1 + elements.set(key, element) + } + + if (!consume(stream, TokenKind.Comma)) + break + } + + const close_squigly = expect(stream, TokenKind.SquiglyClose) + if (close_squigly instanceof Error) + return close_squigly + + return { + kind: ValueKind.TableLiteral, + token: squigly_open, + table: elements, + } } function parse_value(stream: TokenStream): Value | Error { - const token = stream.peek() - switch (token.kind) - { - case TokenKind.NumberLiteral: - return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } - case TokenKind.BooleanLiteral: - return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data == 'true' } - case TokenKind.StringLiteral: - return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } - case TokenKind.NilLiteral: - return { kind: ValueKind.NilLiteral, token: stream.next() } - case TokenKind.Identifier: - return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data } - - case TokenKind.SquiglyOpen: - return parse_table(stream) - case TokenKind.Function: - return parse_function_value(stream.next(), stream) - - default: - return error(token, `Expected value, got ${ token_kind_to_string(token.kind) } instead`) - } + const token = stream.peek() + switch (token.kind) + { + case TokenKind.NumberLiteral: + return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } + case TokenKind.BooleanLiteral: + return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data == 'true' } + case TokenKind.StringLiteral: + return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } + case TokenKind.NilLiteral: + return { kind: ValueKind.NilLiteral, token: stream.next() } + case TokenKind.Identifier: + return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data } + + case TokenKind.SquiglyOpen: + return parse_table(stream) + case TokenKind.Function: + return parse_function_value(stream.next(), stream) + + default: + return error(token, `Expected value, got ${ token_kind_to_string(token.kind) } instead`) + } } function unary_type_to_expression_kind(kind: TokenKind): ExpressionKind { - switch (kind) - { - case TokenKind.Not: return ExpressionKind.Not - case TokenKind.Subtract: return ExpressionKind.Negate - case TokenKind.Hash: return ExpressionKind.Length - case TokenKind.BitXOrNot: return ExpressionKind.BitNot - default: - throw new Error() - } + switch (kind) + { + case TokenKind.Not: return ExpressionKind.Not + case TokenKind.Subtract: return ExpressionKind.Negate + case TokenKind.Hash: return ExpressionKind.Length + case TokenKind.BitXOrNot: return ExpressionKind.BitNot + default: + throw new Error() + } } function parse_unary_operator(stream: TokenStream): Expression | Error { - const operator_token = stream.next() - const operator = unary_type_to_expression_kind(operator_token.kind) + const operator_token = stream.next() + const operator = unary_type_to_expression_kind(operator_token.kind) - const expression = parse_expression(stream) - if (expression instanceof Error) - return expression + const expression = parse_expression(stream) + if (expression instanceof Error) + return expression - return { - kind: operator, - token: operator_token, - expression: expression, - } + return { + kind: operator, + token: operator_token, + expression: expression, + } } function parse_value_expression(stream: TokenStream): Expression | Error { - if (consume(stream, TokenKind.OpenBrace)) - { - const sub_expression = parse_expression(stream) - if (sub_expression instanceof Error) - return sub_expression + if (consume(stream, TokenKind.OpenBrace)) + { + const sub_expression = parse_expression(stream) + if (sub_expression instanceof Error) + return sub_expression - const close_brace = expect(stream, TokenKind.CloseBrace) - if (close_brace instanceof Error) - return close_brace + const close_brace = expect(stream, TokenKind.CloseBrace) + if (close_brace instanceof Error) + return close_brace - return sub_expression - } + return sub_expression + } - if (UNARY.includes(stream.peek().kind)) - return parse_unary_operator(stream) + if (UNARY.includes(stream.peek().kind)) + return parse_unary_operator(stream) - const value = parse_value(stream) - if (value instanceof Error) - return value + const value = parse_value(stream) + if (value instanceof Error) + return value - const expression_value = { - kind: ExpressionKind.Value, - token: value.token, - value: value, - } + const expression_value = { + kind: ExpressionKind.Value, + token: value.token, + value: value, + } - return parse_access_expression(expression_value, stream) + return parse_access_expression(expression_value, stream) } function parse_call(func: Expression, stream: TokenStream): Expression | Error { - const open_brace = stream.next() - const args: Expression[] = [] - while (stream.peek().kind != TokenKind.CloseBrace) - { - const argument = parse_expression(stream) - if (argument instanceof Error) - break + const open_brace = stream.next() + const args: Expression[] = [] + while (stream.peek().kind != TokenKind.CloseBrace) + { + const argument = parse_expression(stream) + if (argument instanceof Error) + break - args.push(argument) - if (!consume(stream, TokenKind.Comma)) - break - } + args.push(argument) + if (!consume(stream, TokenKind.Comma)) + break + } - const close_brace = expect(stream, TokenKind.CloseBrace) - if (close_brace instanceof Error) - return close_brace + const close_brace = expect(stream, TokenKind.CloseBrace) + if (close_brace instanceof Error) + return close_brace - return parse_access_expression({ - kind: ExpressionKind.Call, - token: open_brace, - expression: func, - arguments: args, - }, stream) + return parse_access_expression({ + kind: ExpressionKind.Call, + token: open_brace, + expression: func, + arguments: args, + }, stream) } function parse_index(table: Expression, stream: TokenStream): Expression | Error { - const open_square = stream.next() - const index = parse_expression(stream) - if (index instanceof Error) - return index + const open_square = stream.next() + const index = parse_expression(stream) + if (index instanceof Error) + return index - const close_square = expect(stream, TokenKind.CloseSquare) - if (close_square instanceof Error) - return close_square + const close_square = expect(stream, TokenKind.CloseSquare) + if (close_square instanceof Error) + return close_square - return parse_access_expression({ - kind: ExpressionKind.Index, - token: open_square, - expression: table, - index: index, - }, stream) + return parse_access_expression({ + kind: ExpressionKind.Index, + token: open_square, + expression: table, + index: index, + }, stream) } function parse_dot(table: Expression, stream: TokenStream): Expression | Error { - const dot = stream.next() - const index = expect(stream, TokenKind.Identifier) - if (index instanceof Error) - return index - - return parse_access_expression({ - kind: ExpressionKind.Index, - expression: table, - token: dot, - index: { - kind: ExpressionKind.Value, - token: index, - value: { - kind: ValueKind.StringLiteral, - token: index, - string: index.data, - }, - }, - }, stream) + const dot = stream.next() + const index = expect(stream, TokenKind.Identifier) + if (index instanceof Error) + return index + + return parse_access_expression({ + kind: ExpressionKind.Index, + expression: table, + token: dot, + index: { + kind: ExpressionKind.Value, + token: index, + value: { + kind: ValueKind.StringLiteral, + token: index, + string: index.data, + }, + }, + }, stream) } function parse_single_argument_call(func: Expression, stream: TokenStream): Expression | Error { - const argument = parse_expression(stream) - if (argument instanceof Error) - return argument + const argument = parse_expression(stream) + if (argument instanceof Error) + return argument - return { - kind: ExpressionKind.Call, - token: func.token, - expression: func, - arguments: [argument], - } + return { + kind: ExpressionKind.Call, + token: func.token, + expression: func, + arguments: [argument], + } } function parse_access_expression(expression: Expression, stream: TokenStream): Expression | Error { - switch (stream.peek().kind) - { - case TokenKind.OpenBrace: - return parse_call(expression, stream) + switch (stream.peek().kind) + { + case TokenKind.OpenBrace: + return parse_call(expression, stream) - case TokenKind.OpenSquare: - return parse_index(expression, stream) + case TokenKind.OpenSquare: + return parse_index(expression, stream) - case TokenKind.Dot: - return parse_dot(expression, stream) + case TokenKind.Dot: + return parse_dot(expression, stream) - case TokenKind.SquiglyOpen: - case TokenKind.StringLiteral: - return parse_single_argument_call(expression, stream) - } + case TokenKind.SquiglyOpen: + case TokenKind.StringLiteral: + return parse_single_argument_call(expression, stream) + } - return expression + return expression } function operation_type_to_expression_kind( - operation_type: TokenKind): ExpressionKind -{ - switch (operation_type) - { - case TokenKind.Addition: return ExpressionKind.Addition - case TokenKind.Subtract: return ExpressionKind.Subtract - case TokenKind.Multiply: return ExpressionKind.Multiplication - case TokenKind.Division: return ExpressionKind.Division - case TokenKind.FloorDivision: return ExpressionKind.FloorDivision - case TokenKind.Modulo: return ExpressionKind.Modulo - case TokenKind.Exponent: return ExpressionKind.Exponent - case TokenKind.Concat: return ExpressionKind.Concat - case TokenKind.BitAnd: return ExpressionKind.BitAnd - case TokenKind.BitOr: return ExpressionKind.BitOr - case TokenKind.BitXOrNot: return ExpressionKind.BitXOr - case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft - case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight - case TokenKind.LessThen: return ExpressionKind.LessThen - case TokenKind.LessThenEquals: return ExpressionKind.LessThenEquals - case TokenKind.GreaterThen: return ExpressionKind.GreaterThen - case TokenKind.GreaterThenEquals: return ExpressionKind.GreaterThenEquals - case TokenKind.Equals: return ExpressionKind.Equals - case TokenKind.NotEquals: return ExpressionKind.NotEquals - case TokenKind.And: return ExpressionKind.And - case TokenKind.Or: return ExpressionKind.Or - default: - throw new Error() - } + operation_type: TokenKind): ExpressionKind +{ + switch (operation_type) + { + case TokenKind.Addition: return ExpressionKind.Addition + case TokenKind.Subtract: return ExpressionKind.Subtract + case TokenKind.Multiply: return ExpressionKind.Multiplication + case TokenKind.Division: return ExpressionKind.Division + case TokenKind.FloorDivision: return ExpressionKind.FloorDivision + case TokenKind.Modulo: return ExpressionKind.Modulo + case TokenKind.Exponent: return ExpressionKind.Exponent + case TokenKind.Concat: return ExpressionKind.Concat + case TokenKind.BitAnd: return ExpressionKind.BitAnd + case TokenKind.BitOr: return ExpressionKind.BitOr + case TokenKind.BitXOrNot: return ExpressionKind.BitXOr + case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft + case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight + case TokenKind.LessThen: return ExpressionKind.LessThen + case TokenKind.LessThenEquals: return ExpressionKind.LessThenEquals + case TokenKind.GreaterThen: return ExpressionKind.GreaterThen + case TokenKind.GreaterThenEquals: return ExpressionKind.GreaterThenEquals + case TokenKind.Equals: return ExpressionKind.Equals + case TokenKind.NotEquals: return ExpressionKind.NotEquals + case TokenKind.And: return ExpressionKind.And + case TokenKind.Or: return ExpressionKind.Or + default: + throw new Error() + } } function parse_operation(stream: TokenStream, - order: number): Expression | Error + order: number): Expression | Error { - if (order >= ORDERS.length) - return parse_value_expression(stream) + if (order >= ORDERS.length) + return parse_value_expression(stream) - let lhs = parse_operation(stream, order + 1) - if (lhs instanceof Error) - return lhs + let lhs = parse_operation(stream, order + 1) + if (lhs instanceof Error) + return lhs - while (ORDERS[order].includes(stream.peek().kind)) - { - const operation_type = stream.next() - const rhs = parse_operation(stream, order + 1) - if (rhs instanceof Error) - return rhs + while (ORDERS[order].includes(stream.peek().kind)) + { + const operation_type = stream.next() + const rhs = parse_operation(stream, order + 1) + if (rhs instanceof Error) + return rhs - const expression_kind = operation_type_to_expression_kind(operation_type.kind) - lhs = { - kind: expression_kind, - token: operation_type, - lhs: lhs, - rhs: rhs, - } - } + const expression_kind = operation_type_to_expression_kind(operation_type.kind) + lhs = { + kind: expression_kind, + token: operation_type, + lhs: lhs, + rhs: rhs, + } + } - return lhs + return lhs } function parse_expression(stream: TokenStream): Expression | Error { - if (stream.peek().kind == TokenKind.BitXOrNot) - return parse_unary_operator(stream) + if (stream.peek().kind == TokenKind.BitXOrNot) + return parse_unary_operator(stream) - return parse_operation(stream, 0) + return parse_operation(stream, 0) } function parse_local_statement(local: Token, values: Expression[]): Statement | Error { - const names: Token[] = [] - for (const expression of values) - { - const value = expression.value - if (value == undefined || value.kind != ValueKind.Variable) - return error(expression.token, 'Invalid local name') - names.push(value.token) - } + const names: Token[] = [] + for (const expression of values) + { + const value = expression.value + if (value == undefined || value.kind != ValueKind.Variable) + return error(expression.token, 'Invalid local name') + names.push(value.token) + } - return { - kind: StatementKind.Local, - local: { - token: local, - names: names, - }, - } + return { + kind: StatementKind.Local, + local: { + token: local, + names: names, + }, + } } function parse_assign_or_expression(stream: TokenStream): Statement | Error { - const local = expect(stream, TokenKind.Local) - const lhs: Expression[] = [] - while (lhs.length == 0 || consume(stream, TokenKind.Comma)) - { - const lvalue = parse_expression(stream) - if (lvalue instanceof Error) - return lvalue - lhs.push(lvalue) - } - - const assign = expect(stream, TokenKind.Assign) - if (assign instanceof Error) - { - if (!(local instanceof Error)) - return parse_local_statement(local, lhs) - else - return { kind: StatementKind.Expression, expression: lhs[0] } - } - - const rhs: Expression[] = [] - while (rhs.length == 0 || consume(stream, TokenKind.Comma)) - { - const rvalue = parse_expression(stream) - if (rvalue instanceof Error) - return rvalue - rhs.push(rvalue) - } - - return { - kind: StatementKind.Assignment, - assignment: { - local: !(local instanceof Error), - lhs: lhs.reverse(), - rhs: rhs, - token: assign, - }, - } + const local = expect(stream, TokenKind.Local) + const lhs: Expression[] = [] + while (lhs.length == 0 || consume(stream, TokenKind.Comma)) + { + const lvalue = parse_expression(stream) + if (lvalue instanceof Error) + return lvalue + lhs.push(lvalue) + } + + const assign = expect(stream, TokenKind.Assign) + if (assign instanceof Error) + { + if (!(local instanceof Error)) + return parse_local_statement(local, lhs) + else + return { kind: StatementKind.Expression, expression: lhs[0] } + } + + const rhs: Expression[] = [] + while (rhs.length == 0 || consume(stream, TokenKind.Comma)) + { + const rvalue = parse_expression(stream) + if (rvalue instanceof Error) + return rvalue + rhs.push(rvalue) + } + + return { + kind: StatementKind.Assignment, + assignment: { + local: !(local instanceof Error), + lhs: lhs.reverse(), + rhs: rhs, + token: assign, + }, + } } function parse_return(stream: TokenStream): Statement | Error { - const ret = expect(stream, TokenKind.Return) - if (ret instanceof Error) - return ret - - const values: Expression[] = [] - while (values.length == 0 || consume(stream, TokenKind.Comma)) - { - const value = parse_expression(stream) - if (value instanceof Error) - { - if (values.length > 0) - return value - break - } - - values.push(value) - } - - if (values.length == 0) - { - values.push({ - kind: ExpressionKind.Value, - token: ret, - value: { - kind: ValueKind.NilLiteral, - token: ret, - }, - }) - } - - return { - kind: StatementKind.Return, - return: { - values: values, - token: ret, - }, - } + const ret = expect(stream, TokenKind.Return) + if (ret instanceof Error) + return ret + + const values: Expression[] = [] + while (values.length == 0 || consume(stream, TokenKind.Comma)) + { + const value = parse_expression(stream) + if (value instanceof Error) + { + if (values.length > 0) + return value + break + } + + values.push(value) + } + + if (values.length == 0) + { + values.push({ + kind: ExpressionKind.Value, + token: ret, + value: { + kind: ValueKind.NilLiteral, + token: ret, + }, + }) + } + + return { + kind: StatementKind.Return, + return: { + values: values, + token: ret, + }, + } } function parse_break(stream: TokenStream): Statement | Error { - const break_token = expect(stream, TokenKind.Break) - if (break_token instanceof Error) - return break_token + const break_token = expect(stream, TokenKind.Break) + if (break_token instanceof Error) + return break_token - return { - kind: StatementKind.Break, - } + return { + kind: StatementKind.Break, + } } function parse_if(stream: TokenStream): Statement | Error { - const if_token = expect(stream, TokenKind.If) - if (if_token instanceof Error) - return if_token - - const condition = parse_expression(stream) - if (condition instanceof Error) - return condition - - const then = expect(stream, TokenKind.Then) - if (then instanceof Error) - return then - - const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End) - if (body instanceof Error) - return body - - const else_if_bodies: IfElseBlock[] = [] - let else_body: Chunk | undefined = undefined - while (consume(stream, TokenKind.ElseIf)) - { - const condition = parse_expression(stream) - if (condition instanceof Error) - return condition - - const then = expect(stream, TokenKind.Then) - if (then instanceof Error) - return then - - const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else) - if (chunk instanceof Error) - return chunk - - else_if_bodies.push({ - body: chunk, - condition: condition, - token: then, - }) - } - - if (consume(stream, TokenKind.Else)) - { - const chunk = parse(stream, TokenKind.End) - if (chunk instanceof Error) - return chunk - else_body = chunk - } - - const end = expect(stream, TokenKind.End) - if (end instanceof Error) - return end - - return { - kind: StatementKind.If, - if: { - condition: condition, - body: body, - else_if_bodies: else_if_bodies, - else_body: else_body, - token: if_token, - }, - } + const if_token = expect(stream, TokenKind.If) + if (if_token instanceof Error) + return if_token + + const condition = parse_expression(stream) + if (condition instanceof Error) + return condition + + const then = expect(stream, TokenKind.Then) + if (then instanceof Error) + return then + + const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End) + if (body instanceof Error) + return body + + const else_if_bodies: IfElseBlock[] = [] + let else_body: Chunk | undefined = undefined + while (consume(stream, TokenKind.ElseIf)) + { + const condition = parse_expression(stream) + if (condition instanceof Error) + return condition + + const then = expect(stream, TokenKind.Then) + if (then instanceof Error) + return then + + const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else) + if (chunk instanceof Error) + return chunk + + else_if_bodies.push({ + body: chunk, + condition: condition, + token: then, + }) + } + + if (consume(stream, TokenKind.Else)) + { + const chunk = parse(stream, TokenKind.End) + if (chunk instanceof Error) + return chunk + else_body = chunk + } + + const end = expect(stream, TokenKind.End) + if (end instanceof Error) + return end + + return { + kind: StatementKind.If, + if: { + condition: condition, + body: body, + else_if_bodies: else_if_bodies, + else_body: else_body, + token: if_token, + }, + } } function parse_while(stream: TokenStream): Statement | Error { - const while_token = expect(stream, TokenKind.While) - if (while_token instanceof Error) - return while_token + const while_token = expect(stream, TokenKind.While) + if (while_token instanceof Error) + return while_token - const condition = parse_expression(stream) - if (condition instanceof Error) - return condition + const condition = parse_expression(stream) + if (condition instanceof Error) + return condition - const do_token = expect(stream, TokenKind.Do) - if (do_token instanceof Error) - return do_token + const do_token = expect(stream, TokenKind.Do) + if (do_token instanceof Error) + return do_token - const body = parse(stream, TokenKind.End) - if (body instanceof Error) - return body + const body = parse(stream, TokenKind.End) + if (body instanceof Error) + return body - consume(stream, TokenKind.End) - return { - kind: StatementKind.While, - while: { - condition: condition, - body: body, - token: while_token, - }, - } + consume(stream, TokenKind.End) + return { + kind: StatementKind.While, + while: { + condition: condition, + body: body, + token: while_token, + }, + } } function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error { - const start = parse_expression(stream) - if (start instanceof Error) - return start - - const comma = expect(stream, TokenKind.Comma) - if (comma instanceof Error) - return comma - - const end = parse_expression(stream) - if (end instanceof Error) - return end - - let step: Expression | undefined = undefined - if (consume(stream, TokenKind.Comma)) - { - const expression = parse_expression(stream) - if (expression instanceof Error) - return expression - - step = expression - } - - const do_token = expect(stream, TokenKind.Do) - if (do_token instanceof Error) - return do_token - - const body = parse(stream, TokenKind.End) - if (body instanceof Error) - return body - - consume(stream, TokenKind.End) - return { - kind: StatementKind.NumericFor, - numeric_for: { - index: index, - start: start, - end: end, - step: step, - body: body, - }, - } + const start = parse_expression(stream) + if (start instanceof Error) + return start + + const comma = expect(stream, TokenKind.Comma) + if (comma instanceof Error) + return comma + + const end = parse_expression(stream) + if (end instanceof Error) + return end + + let step: Expression | undefined = undefined + if (consume(stream, TokenKind.Comma)) + { + const expression = parse_expression(stream) + if (expression instanceof Error) + return expression + + step = expression + } + + const do_token = expect(stream, TokenKind.Do) + if (do_token instanceof Error) + return do_token + + const body = parse(stream, TokenKind.End) + if (body instanceof Error) + return body + + consume(stream, TokenKind.End) + return { + kind: StatementKind.NumericFor, + numeric_for: { + index: index, + start: start, + end: end, + step: step, + body: body, + }, + } } function parse_for(stream: TokenStream): Statement | Error { - const for_token = expect(stream, TokenKind.For) - if (for_token instanceof Error) - return for_token - - const items: Token[] = [] - while (items.length == 0 || consume(stream, TokenKind.Comma)) - { - const item = expect(stream, TokenKind.Identifier) - if (item instanceof Error) - return item - items.push(item) - } - - if (consume(stream, TokenKind.Assign)) - return parse_numeric_for(items[0], stream) - - const in_token = expect(stream, TokenKind.In) - if (in_token instanceof Error) - return in_token - - const itorator = parse_expression(stream) - if (itorator instanceof Error) - return itorator - - const do_token = expect(stream, TokenKind.Do) - if (do_token instanceof Error) - return do_token - - const body = parse(stream, TokenKind.End) - if (body instanceof Error) - return body - - consume(stream, TokenKind.End) - return { - kind: StatementKind.For, - for: { - items: items, - itorator: itorator, - body: body, - token: for_token, - }, - } + const for_token = expect(stream, TokenKind.For) + if (for_token instanceof Error) + return for_token + + const items: Token[] = [] + while (items.length == 0 || consume(stream, TokenKind.Comma)) + { + const item = expect(stream, TokenKind.Identifier) + if (item instanceof Error) + return item + items.push(item) + } + + if (consume(stream, TokenKind.Assign)) + return parse_numeric_for(items[0], stream) + + const in_token = expect(stream, TokenKind.In) + if (in_token instanceof Error) + return in_token + + const itorator = parse_expression(stream) + if (itorator instanceof Error) + return itorator + + const do_token = expect(stream, TokenKind.Do) + if (do_token instanceof Error) + return do_token + + const body = parse(stream, TokenKind.End) + if (body instanceof Error) + return body + + consume(stream, TokenKind.End) + return { + kind: StatementKind.For, + for: { + items: items, + itorator: itorator, + body: body, + token: for_token, + }, + } } function parse_repeat(stream: TokenStream): Statement | Error { - const repeat_token = expect(stream, TokenKind.Repeat) - if (repeat_token instanceof Error) - return repeat_token + const repeat_token = expect(stream, TokenKind.Repeat) + if (repeat_token instanceof Error) + return repeat_token - const body = parse(stream, TokenKind.Until) - if (body instanceof Error) - return body + const body = parse(stream, TokenKind.Until) + if (body instanceof Error) + return body - const until_token = expect(stream, TokenKind.Until) - if (until_token instanceof Error) - return until_token + const until_token = expect(stream, TokenKind.Until) + if (until_token instanceof Error) + return until_token - const condition = parse_expression(stream) - if (condition instanceof Error) - return condition + const condition = parse_expression(stream) + if (condition instanceof Error) + return condition - return { - kind: StatementKind.Repeat, - repeat: { - body: body, - condition: condition, - token: repeat_token, - }, - } + return { + kind: StatementKind.Repeat, + repeat: { + body: body, + condition: condition, + token: repeat_token, + }, + } } function parse_do(stream: TokenStream): Statement | Error { - const do_token = expect(stream, TokenKind.Do) - if (do_token instanceof Error) - return do_token + const do_token = expect(stream, TokenKind.Do) + if (do_token instanceof Error) + return do_token - const body = parse(stream, TokenKind.End) - if (body instanceof Error) - return body + const body = parse(stream, TokenKind.End) + if (body instanceof Error) + return body - const end_token = expect(stream, TokenKind.End) - if (end_token instanceof Error) - return end_token + const end_token = expect(stream, TokenKind.End) + if (end_token instanceof Error) + return end_token - return { - kind: StatementKind.Do, - do: { - body: body, - token: do_token, - }, - } + return { + kind: StatementKind.Do, + do: { + body: body, + token: do_token, + }, + } } function parse_function_params(stream: TokenStream): Token[] | Error { - const open_brace = expect(stream, TokenKind.OpenBrace) - if (open_brace instanceof Error) - return open_brace + const open_brace = expect(stream, TokenKind.OpenBrace) + if (open_brace instanceof Error) + return open_brace - const params: Token[] = [] - while (stream.peek().kind != TokenKind.CloseBrace) - { - const param = expect(stream, TokenKind.Identifier) - if (param instanceof Error) - break + const params: Token[] = [] + while (stream.peek().kind != TokenKind.CloseBrace) + { + const param = expect(stream, TokenKind.Identifier) + if (param instanceof Error) + break - params.push(param) - if (!consume(stream, TokenKind.Comma)) - break - } + params.push(param) + if (!consume(stream, TokenKind.Comma)) + break + } - const close_brace = expect(stream, TokenKind.CloseBrace) - if (close_brace instanceof Error) - return close_brace + const close_brace = expect(stream, TokenKind.CloseBrace) + if (close_brace instanceof Error) + return close_brace - return params + return params } function parse_function_value(function_token: Token, stream: TokenStream): Value | Error { - const params = parse_function_params(stream) - if (params instanceof Error) - return params + const params = parse_function_params(stream) + if (params instanceof Error) + return params - const body = parse(stream, TokenKind.End) - if (body instanceof Error) - return body + const body = parse(stream, TokenKind.End) + if (body instanceof Error) + return body - consume(stream, TokenKind.End) - return { - kind: ValueKind.Function, - token: function_token, - function: { - parameters: params, - body: body, - }, - } + consume(stream, TokenKind.End) + return { + kind: ValueKind.Function, + token: function_token, + function: { + parameters: params, + body: body, + }, + } } function parse_local_function(table_name: Token, stream: TokenStream): Statement | Error { - const local_name = expect(stream, TokenKind.Identifier) - if (local_name instanceof Error) - return local_name - - const function_value = parse_function_value(local_name, stream) - if (function_value instanceof Error) - return function_value - - return { - kind: StatementKind.Assignment, - assignment: { - token: table_name, - local: false, - lhs: [{ - kind: ExpressionKind.Index, - token: table_name, - expression: { - kind: ExpressionKind.Value, - token: table_name, - value: { - kind: ValueKind.Variable, - token: table_name, - identifier: table_name.data, - }, - }, - index: { - kind: ExpressionKind.Value, - token: local_name, - value: { - kind: ValueKind.StringLiteral, - token: local_name, - string: local_name.data, - }, - }, - }], - rhs: [{ - kind: ExpressionKind.Value, - token: local_name, - value: function_value, - }], - }, - } + const local_name = expect(stream, TokenKind.Identifier) + if (local_name instanceof Error) + return local_name + + const function_value = parse_function_value(local_name, stream) + if (function_value instanceof Error) + return function_value + + return { + kind: StatementKind.Assignment, + assignment: { + token: table_name, + local: false, + lhs: [{ + kind: ExpressionKind.Index, + token: table_name, + expression: { + kind: ExpressionKind.Value, + token: table_name, + value: { + kind: ValueKind.Variable, + token: table_name, + identifier: table_name.data, + }, + }, + index: { + kind: ExpressionKind.Value, + token: local_name, + value: { + kind: ValueKind.StringLiteral, + token: local_name, + string: local_name.data, + }, + }, + }], + rhs: [{ + kind: ExpressionKind.Value, + token: local_name, + value: function_value, + }], + }, + } } function parse_function(stream: TokenStream): Statement | Error { - const function_token = expect(stream, TokenKind.Function) - if (function_token instanceof Error) - return function_token - - const name = expect(stream, TokenKind.Identifier) - if (name instanceof Error) - return name - - if (consume(stream, TokenKind.Dot)) - return parse_local_function(name, stream) - - const function_value = parse_function_value(name, stream) - if (function_value instanceof Error) - return function_value - - return { - kind: StatementKind.Assignment, - assignment: { - token: name, - local: false, - lhs: [{ - kind: ExpressionKind.Value, - token: name, - value: { - kind: ValueKind.Variable, - token: name, - identifier: name.data, - }, - }], - rhs: [{ - kind: ExpressionKind.Value, - token: name, - value: function_value, - }], - }, - } + const function_token = expect(stream, TokenKind.Function) + if (function_token instanceof Error) + return function_token + + const name = expect(stream, TokenKind.Identifier) + if (name instanceof Error) + return name + + if (consume(stream, TokenKind.Dot)) + return parse_local_function(name, stream) + + const function_value = parse_function_value(name, stream) + if (function_value instanceof Error) + return function_value + + return { + kind: StatementKind.Assignment, + assignment: { + token: name, + local: false, + lhs: [{ + kind: ExpressionKind.Value, + token: name, + value: { + kind: ValueKind.Variable, + token: name, + identifier: name.data, + }, + }], + rhs: [{ + kind: ExpressionKind.Value, + token: name, + value: function_value, + }], + }, + } } function parse_statement(stream: TokenStream, end_tokens: TokenKind[]): Statement | Error | undefined { - const token = stream.peek() - switch (token.kind) - { - case TokenKind.Identifier: - case TokenKind.NilLiteral: - case TokenKind.StringLiteral: - case TokenKind.NumberLiteral: - case TokenKind.BooleanLiteral: - case TokenKind.SquiglyOpen: - case TokenKind.Local: - return parse_assign_or_expression(stream) - case TokenKind.Return: - return parse_return(stream) - case TokenKind.Break: - return parse_break(stream) - case TokenKind.If: - return parse_if(stream) - case TokenKind.While: - return parse_while(stream) - case TokenKind.For: - return parse_for(stream) - case TokenKind.Repeat: - return parse_repeat(stream) - case TokenKind.Do: - return parse_do(stream) - case TokenKind.Function: - return parse_function(stream) - case TokenKind.Semicolon: - stream.next() - return { kind: StatementKind.Empty } - default: - if (end_tokens.includes(token.kind)) - return undefined - return error(token, `Missing '${ token_kind_to_string(end_tokens[0]) }', ` + - `got '${ token_kind_to_string(token.kind) }' instead`) - } + const token = stream.peek() + switch (token.kind) + { + case TokenKind.Identifier: + case TokenKind.NilLiteral: + case TokenKind.StringLiteral: + case TokenKind.NumberLiteral: + case TokenKind.BooleanLiteral: + case TokenKind.SquiglyOpen: + case TokenKind.Local: + return parse_assign_or_expression(stream) + case TokenKind.Return: + return parse_return(stream) + case TokenKind.Break: + return parse_break(stream) + case TokenKind.If: + return parse_if(stream) + case TokenKind.While: + return parse_while(stream) + case TokenKind.For: + return parse_for(stream) + case TokenKind.Repeat: + return parse_repeat(stream) + case TokenKind.Do: + return parse_do(stream) + case TokenKind.Function: + return parse_function(stream) + case TokenKind.Semicolon: + stream.next() + return { kind: StatementKind.Empty } + default: + if (end_tokens.includes(token.kind)) + return undefined + return error(token, `Missing '${ token_kind_to_string(end_tokens[0]) }', ` + + `got '${ token_kind_to_string(token.kind) }' instead`) + } } export function parse(stream: TokenStream, ...end_tokens: TokenKind[]): Chunk | Error { - const chunk: Chunk = { statements: [] } - if (end_tokens.length == 0) - end_tokens.push(TokenKind.EOF) + const chunk: Chunk = { statements: [] } + if (end_tokens.length == 0) + end_tokens.push(TokenKind.EOF) - while (true) - { - const statement = parse_statement(stream, end_tokens) - if (statement == undefined) - break - if (statement instanceof Error) - return statement + while (true) + { + const statement = parse_statement(stream, end_tokens) + if (statement == undefined) + break + if (statement instanceof Error) + return statement - chunk.statements.push(statement) - } + chunk.statements.push(statement) + } - return chunk + return chunk } diff --git a/src/runtime.mts b/src/runtime.mts index 2476e8a..6882260 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -7,41 +7,41 @@ import { Engine } from './engine.mts' export enum DataType { - Nil, - Boolean, - Number, - String, - Function, - NativeFunction, - Table, + Nil, + Boolean, + Number, + String, + Function, + NativeFunction, + Table, } export type NativeFunction = (engine: Engine, ...args: Variable[]) => Variable[] export interface Variable { - data_type: DataType, - boolean?: boolean, - number?: number, - string?: string, - native_function?: NativeFunction, - table?: Map, - - function_id?: number, - locals?: Map[], + data_type: DataType, + boolean?: boolean, + number?: number, + string?: string, + native_function?: NativeFunction, + table?: Map, + + function_id?: number, + locals?: Map[], } export const nil: Variable = { data_type: DataType.Nil } export function make_boolean(boolean: boolean): Variable { - return { data_type: DataType.Boolean, boolean: boolean } + return { data_type: DataType.Boolean, boolean: boolean } } export function make_number(number: number): Variable { - return { data_type: DataType.Number, number: number } + return { data_type: DataType.Number, number: number } } export function make_string(string: string): Variable { - return { data_type: DataType.String, string: string } + return { data_type: DataType.String, string: string } } From 7934895ff8a9f705443be40a6e340ca269977fb5 Mon Sep 17 00:00:00 2001 From: Zamralik Date: Thu, 15 Jan 2026 05:09:08 +0100 Subject: [PATCH 04/71] fix: various errors --- src/ast.mts | 106 ++++++++++++++++++++++++------------------- src/compiler.mts | 65 +++++++++++++------------- src/engine.mts | 100 +++++++++++++++++++++++++--------------- src/index.mts | 36 +++++++-------- src/lexer.mts | 21 +++++---- src/lib.mts | 85 ++++++++++++++++++++++++---------- src/opcode.mts | 10 +--- src/optimizer.mts | 113 ++++++++++++++++++++++++++-------------------- src/parser.mts | 77 +++++++++++++++++++++---------- src/runtime.mts | 13 ++---- 10 files changed, 368 insertions(+), 258 deletions(-) diff --git a/src/ast.mts b/src/ast.mts index 6386bdb..01df624 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,12 +1,7 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ +import type { Token } from './lexer.mjs' -import type { Token } from './lexer' - -export enum ValueKind { +export enum ValueKind +{ NilLiteral, NumberLiteral, BooleanLiteral, @@ -16,24 +11,27 @@ export enum ValueKind { Variable, } -export interface LuaFunction { - parameters: Token[], +export interface LuaFunction +{ + parameters: Array, body: Chunk, } -export interface Value { +export interface Value +{ kind: ValueKind, token: Token, - number?: number, - boolean?: boolean, - string?: string, - table?: Map, - function?: LuaFunction, - identifier?: string, + number?: number | undefined, + boolean?: boolean | undefined, + string?: string | undefined, + table?: Map | undefined, + function?: LuaFunction | undefined, + identifier?: string | undefined, } -export enum ExpressionKind { +export enum ExpressionKind +{ Value, Call, Index, @@ -68,7 +66,8 @@ export enum ExpressionKind { Length, } -export interface Expression { +export interface Expression +{ kind: ExpressionKind, token: Token, @@ -80,46 +79,53 @@ export interface Expression { arguments?: Expression[], } -export interface Assignment { +export interface Assignment +{ local: boolean, lhs: Expression[], rhs: Expression[], token: Token, } -export interface Local { +export interface Local +{ names: Token[], token: Token, } -export interface IfElseBlock { +export interface ElseIfBlock +{ body: Chunk, condition: Expression, token: Token, } -export interface IfBlock { +export interface IfBlock +{ condition: Expression, body: Chunk, - else_if_bodies: IfElseBlock[], - else_body?: Chunk, + else_if_bodies: ElseIfBlock[], + else_body?: Chunk | undefined, token: Token, } -export interface While { +export interface While +{ condition: Expression, body: Chunk, token: Token, } -export interface For { +export interface For +{ items: Token[], - itorator: Expression, + iterator: Expression, body: Chunk, token: Token, } -export interface NumericFor { +export interface NumericFor +{ index: Token, start: Expression, end: Expression, @@ -127,23 +133,27 @@ export interface NumericFor { body: Chunk, } -export interface Repeat { +export interface Repeat +{ body: Chunk, condition: Expression, token: Token, } -export interface Do { +export interface Do +{ body: Chunk, token: Token, } -export interface Return { +export interface Return +{ values: Expression[], token: Token, } -export enum StatementKind { +export enum StatementKind +{ Invalid, Empty, Expression, @@ -159,20 +169,22 @@ export enum StatementKind { Break, } -export interface Statement { +export interface Statement +{ kind: StatementKind, - expression?: Expression, - assignment?: Assignment, - local?: Local, - if?: IfBlock, - while?: While, - for?: For, - numeric_for?: NumericFor, - repeat?: Repeat, - do?: Do, - return?: Return, -} - -export interface Chunk { + expression?: Expression | undefined, + assignment?: Assignment | undefined, + local?: Local | undefined, + if?: IfBlock | undefined, + while?: While | undefined, + for?: For | undefined, + numeric_for?: NumericFor | undefined, + repeat?: Repeat | undefined, + do?: Do | undefined, + return?: Return | undefined, +} + +export interface Chunk +{ statements: Statement[], } diff --git a/src/compiler.mts b/src/compiler.mts index cfab135..c512166 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,19 +1,13 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import type { Chunk, Expression, Value } from './ast' -import type { IfBlock, While, For, NumericFor, Repeat, Do } from './ast' -import type { Op, Program } from './opcode.mts' -import type { Token } from './lexer.mts' -import type { Assignment, Local, Return } from './ast' - -import { StatementKind, ExpressionKind, ValueKind } from './ast' -import { OpCode } from './opcode.mts' -import { DataType } from './runtime.mts' -import { make_boolean, make_number, make_string, nil } from './runtime.mts' +import type { Chunk, Expression, Value } from './ast.mjs' +import type { IfBlock, While, For, NumericFor, Repeat, Do } from './ast.mjs' +import type { Op, Program } from './opcode.mjs' +import type { Token } from './lexer.mjs' +import type { Assignment, Local, Return } from './ast.mjs' + +import { StatementKind, ExpressionKind, ValueKind } from './ast.mjs' +import { OpCode } from './opcode.mjs' +import { DataType } from './runtime.mjs' +import { make_boolean, make_number, make_string, nil } from './runtime.mjs' function compile_function(chunk: Chunk, token: Token, parameters: Token[], functions: Op[][]): number { @@ -93,9 +87,11 @@ function compile_value(value: Value | undefined, functions: Op[][]): Op[] } } -function compile_operation(expression: Expression, - operation: OpCode, - functions: Op[][]): Op[] +function compile_operation( + expression: Expression, + operation: OpCode, + functions: Op[][] +): Op[] { const { lhs, rhs } = expression if (lhs == undefined || rhs == undefined) @@ -108,9 +104,11 @@ function compile_operation(expression: Expression, return ops } -function compile_call(func: Expression | undefined, - args: Expression[] | undefined, - functions: Op[][]): Op[] +function compile_call( + func: Expression | undefined, + args: Expression[] | undefined, + functions: Op[][] +): Op[] { if (func == undefined || args == undefined) throw new Error() @@ -125,9 +123,11 @@ function compile_call(func: Expression | undefined, return ops } -function compile_index(target: Expression | undefined, - index: Expression | undefined, - functions: Op[][]): Op[] +function compile_index( + target: Expression | undefined, + index: Expression | undefined, + functions: Op[][] +): Op[] { if (target == undefined || index == undefined) throw new Error() @@ -139,10 +139,11 @@ function compile_index(target: Expression | undefined, return ops } -function compile_unary_operation(expression: Expression | undefined, - operation: OpCode, - functions: Op[][]): Op[] - +function compile_unary_operation( + expression: Expression | undefined, + operation: OpCode, + functions: Op[][] +): Op[] { if (expression == undefined || expression.expression == undefined) throw new Error() @@ -414,7 +415,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Op[][]): Op[] return ops } -function replace_breaks(code: Op[], offset_from_end: number) +function replace_breaks(code: Op[], offset_from_end: number): void { for (const [i, op] of code.entries()) { @@ -457,7 +458,7 @@ function compile_for(for_block: For | undefined, functions: Op[][]): Op[] const debug = for_block.token.debug ops.push({ code: OpCode.StartBlock, debug: debug }) ops.push({ code: OpCode.StartStackChange, debug: debug }) - ops.push(...compile_expression(for_block.itorator, functions)) + ops.push(...compile_expression(for_block.iterator, functions)) ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }) const after_creating_itorator = ops.length @@ -643,7 +644,7 @@ function compile_chunk(chunk: Chunk, functions: Op[][]): ChunkResult } } -function link(code: Op[], function_id: number, location: number) +function link(code: Op[], function_id: number, location: number): void { for (const op of code) { diff --git a/src/engine.mts b/src/engine.mts index 32e3312..c3695d5 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,19 +1,13 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import type { Op } from './opcode.mts' -import type { NativeFunction, Variable } from './runtime.mts' - -import { OpCode, op_code_name } from './opcode.mts' -import { DataType, nil, make_number, make_boolean, make_string } from './runtime.mts' -import { TokenStream } from './lexer.mts' -import { parse } from './parser.mts' -import { compile } from './compiler.mts' -import { optimize_chunk } from './optimizer.mts' -import * as std from './lib.mts' +import type { Op } from './opcode.mjs' +import type { NativeFunction, Variable } from './runtime.mjs' + +import { OpCode, op_code_name } from './opcode.mjs' +import { DataType, nil, make_number, make_boolean, make_string } from './runtime.mjs' +import { TokenStream } from './lexer.mjs' +import { parse } from './parser.mjs' +import { compile } from './compiler.mjs' +import { optimize_chunk } from './optimizer.mjs' +import * as std from './lib.mjs' function index(val: Variable | undefined): string | number | undefined { @@ -102,8 +96,10 @@ export class Engine private error: Error | undefined - constructor(script?: string, - globals?: Map) + constructor( + script?: string, + globals?: Map + ) { this.program = [] this.globals = globals ?? std.std_lib() @@ -132,6 +128,8 @@ export class Engine this.program = program.code this.ip = program.start this.start_ip = program.start + + return undefined } bytecode(): string[] @@ -157,7 +155,7 @@ export class Engine return this.globals.get(name) } - define(name: string, func: NativeFunction) + define(name: string, func: NativeFunction): void { this.globals.set(name, { data_type: DataType.NativeFunction, @@ -165,7 +163,7 @@ export class Engine }) } - define_table(name: string, table: Map) + define_table(name: string, table: Map): void { this.globals.set(name, { data_type: DataType.Table, @@ -173,7 +171,7 @@ export class Engine }) } - reset() + reset(): void { this.ip = this.start_ip this.stack = [] @@ -213,6 +211,7 @@ export class Engine this.call_stack = old_call_stack this.locals_stack = old_locals_stack this.ip = old_ip + if (result instanceof Error) return result @@ -231,6 +230,7 @@ export class Engine while (this.ip < this.program.length) { const result = this.step(options) + if (result != undefined) return result @@ -239,52 +239,65 @@ export class Engine return undefined } - return this.stack[0] ?? nil + return this.stack_get(0); } run(options?: LuaOptions): Variable | Error { const result = this.run_for_steps(1000, options) + if (result == undefined) return new Error('Program ran for too long') else return result } - raise_error(message: string) + raise_error(message: string): void { const op = this.program.at(this.ip - 1) this.error = this.runtime_error(op, message) } - private call_native_function(native_function: NativeFunction, ...args: Variable[]): Variable[] | Error + private stack_get(index: number): Variable + { + if (index < 0) + { + return this.stack[this.stack.length + index] ?? nil; + } + + return this.stack[index] ?? nil; + } + + private call_native_function(native_function: NativeFunction, ...args: Array): Array | Error { const results = native_function(this, ...args) + if (this.error != undefined) { const error = this.error this.error = undefined + return error } return results } - private operation(op: (x: number, y: number) => number) + private operation(op: (x: number, y: number) => number): void { const x = this.stack.pop()?.number ?? 0 const y = this.stack.pop()?.number ?? 0 this.stack.push(make_number(op(x, y))) } - private compair(op: (x: number, y: number) => boolean) + private compair(op: (x: number, y: number) => boolean): void { const x = this.stack.pop()?.number ?? 0 const y = this.stack.pop()?.number ?? 0 this.stack.push(make_boolean(op(x, y))) } - private force_stack_height(expected: number, got: number) + private force_stack_height(expected: number, got: number): void { for (let i = got; i < expected; i++) this.stack.push(nil) @@ -300,7 +313,7 @@ export class Engine return new Error(`${ op.debug.line }:${ op.debug.column }: ${ message }`) } - private run_instruction(op: Op): undefined | Error + private run_instruction(op: Op): Error | undefined { const { code, arg } = op switch(code) @@ -329,20 +342,23 @@ export class Engine case OpCode.IterUpdateState: { - this.stack[this.stack.length - 2] = this.stack[this.stack.length - 1] + this.stack[this.stack.length - 2] = this.stack_get(-1) break } case OpCode.IterNext: { - const state = this.stack[this.stack.length - 1] - const control = this.stack[this.stack.length - 2] - const iter = this.stack[this.stack.length - 3] - if (iter.native_function != undefined) + const state = this.stack_get(-1) + const control = this.stack_get(-2) + const iter = this.stack_get(-3) + + if (iter.native_function !== undefined) { const result = this.call_native_function(iter.native_function, control, state) + if (result instanceof Error) return result + this.stack.push(...result) break } @@ -357,7 +373,7 @@ export class Engine case OpCode.IterJumpIfDone: { - if (this.stack[this.stack.length - 1].data_type == DataType.Nil) + if (this.stack_get(-1).data_type == DataType.Nil) this.ip += arg?.number ?? 0 break } @@ -477,7 +493,8 @@ export class Engine case OpCode.StoreIndex: { const count = arg?.number ?? 1 - const table = this.stack[this.stack.length - count*2 - 1] ?? nil + const table = this.stack_get(- count*2 - 1) + if (table.table == undefined) return this.runtime_error(op, 'Can only index tables') @@ -542,8 +559,10 @@ export class Engine if (func_var.native_function != undefined) { const result = this.call_native_function(func_var.native_function, ...args) + if (result instanceof Error) return result + this.stack.push(...result) } @@ -599,9 +618,11 @@ export class Engine break } } + + return undefined } - step(options?: LuaOptions): undefined | Error + step(options?: LuaOptions): Error | undefined { if (this.error != undefined) return this.error @@ -610,6 +631,10 @@ export class Engine return const op = this.program[this.ip++] + + if (op === undefined) + return this.runtime_error(op, 'Instruction pointer out of bounds') + if (options?.trace || options?.trace_instructions) { const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' @@ -617,11 +642,14 @@ export class Engine } const result = this.run_instruction(op) + if (result != undefined) return result if (options?.trace || options?.trace_stack) console.log(this.ip - 1, ...this.stack.map(x => std.variable_to_string(x))) + + return undefined } } diff --git a/src/index.mts b/src/index.mts index 9376b12..457107e 100644 --- a/src/index.mts +++ b/src/index.mts @@ -1,19 +1,13 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import { Engine } from './src/engine' -import { DataType, Variable, NativeFunction, nil } from './src/runtime' -import { make_boolean, make_number, make_string } from './src/runtime' -import { compile } from './src/compiler' -import { std_lib, variable_to_string } from './src/lib' -import * as lexer from './src/lexer' -import * as parser from './src/parser' -import * as ast from './src/ast' -import * as opcode from './src/opcode' -import * as runtime from './src/runtime' +import { Engine } from './engine.mjs' +import { DataType, type Variable, type NativeFunction, nil } from './runtime.mjs' +import { make_boolean, make_number, make_string } from './runtime.mjs' +import { compile } from './compiler.mjs' +import { std_lib, variable_to_string } from './lib.mjs' +export * as lexer from './lexer.mjs' +export * as parser from './parser.mjs' +export * as ast from './ast.mjs' +export * as opcode from './opcode.mjs' +export * as runtime from './runtime.mjs' export const Nil = DataType.Nil export const Boolean = DataType.Boolean @@ -23,14 +17,16 @@ export const Function = DataType.Function export const NativeFunctionType = DataType.NativeFunction export const Table = DataType.Table -export -{ +export { std_lib as std_global, nil, make_boolean as boolean, make_number as number, make_string as string, variable_to_string as to_string, - Engine, DataType, Variable, NativeFunction, - lexer, parser, ast, compile, opcode, runtime, + Engine, + DataType, + Variable, + NativeFunction, + compile, } diff --git a/src/lexer.mts b/src/lexer.mts index 90b3cf5..3b2ce2b 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,9 +1,3 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - export enum State { Initial, Identifier, @@ -83,7 +77,7 @@ export enum TokenKind { Local, } -export function token_kind_to_string(kind: TokenKind) +export function token_kind_to_string(kind: TokenKind): string | undefined { switch(kind) { @@ -136,6 +130,8 @@ export function token_kind_to_string(kind: TokenKind) case TokenKind.Return: return 'return' case TokenKind.Break: return 'break' case TokenKind.Local: return 'local' + default: + return "unknown token" } } @@ -662,14 +658,21 @@ export class TokenStream { if (this.peek_queue.length == 0) this.peek() - return this.peek_queue.shift() + + return this.peek_queue.shift() as Token } peek(count = 1): Token { while (this.peek_queue.length < count) this.on_char() - return this.peek_queue[count - 1] + + const token = this.peek_queue[count - 1] + + if (token === undefined) + throw new Error() + + return token } } diff --git a/src/lib.mts b/src/lib.mts index 342170e..a127967 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -1,14 +1,8 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ +import type { Variable } from './runtime.mjs' +import { Engine } from './engine.mjs' +import { DataType, make_boolean, make_number, make_string, nil } from './runtime.mjs' -import type { Variable } from './runtime.mts' -import { Engine } from './engine.mts' -import { DataType, make_boolean, make_number, make_string, nil } from './runtime.mts' - -let rand = xoroshiro([0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100)))) +let rand = xoroshiro(create_seed()) export function variable_size(value: Variable): number | undefined { @@ -272,9 +266,11 @@ function error(engine: Engine, message: Variable): Variable[] let warnings_on = true function warn(_: Engine, ...messages: Variable[]): Variable[] { - if (messages.length == 1) + const message = messages[0] + + if (message !== undefined) { - switch (messages[0].string) + switch (message.string) { case '@on': warnings_on = true @@ -335,15 +331,37 @@ function string_format(_: Engine, format: Variable, ...args: Variable[]): Variab let result = '' let is_format = false let arg_index = 0 + for (const char of format.string) { if (is_format) { switch (char) { - case 'd': result += Math.floor(args[arg_index++].number ?? 0).toString(); break - case 'f': result += args[arg_index++].number?.toString(); break - case 's': result += variable_to_string(args[arg_index++]); break + case 'd': + { + const arg = args[arg_index] + ++arg_index + result += Math.floor(arg?.number ?? 0).toString() + break + } + case 'f': + { + const arg = args[arg_index] + ++arg_index + result += arg?.number?.toString() + break + } + case 's': + { + const arg = args[arg_index] + ++arg_index + + if (arg !== undefined) + result += variable_to_string(arg) + + break + } default: result += `%${ char }` } @@ -611,21 +629,36 @@ function math_random(_: Engine, m?: Variable, n?: Variable): Variable[] return [make_number(Math.floor(rand() % max) + min)] } -function xoroshiro(s: bigint[]) +function create_seed(): [bigint, bigint, bigint, bigint] +{ + const random_bigint = (): bigint => + BigInt(Math.floor(Math.random() * 100)) + + const seed: [bigint, bigint, bigint, bigint] = [ + random_bigint(), + random_bigint(), + random_bigint(), + random_bigint(), + ] + + return seed +} + +function xoroshiro(seed: [bigint, bigint, bigint, bigint]): () => number { const rotl = (x: bigint, k: bigint) => BigInt.asUintN(64, (x << k) | (x >> (BigInt(64) - k))) return () => { - const result = rotl(BigInt.asUintN(64, s[0] + s[3]), BigInt(23)) + s[0] - const t = BigInt.asUintN(64, s[1] << BigInt(17)) - s[2] ^= s[0] - s[3] ^= s[1] - s[1] ^= s[2] - s[0] ^= s[3] - s[2] ^= t - s[3] = rotl(s[3], BigInt(45)) + const result = rotl(BigInt.asUintN(64, seed[0] + seed[3]), BigInt(23)) + seed[0] + const t = BigInt.asUintN(64, seed[1] << BigInt(17)) + seed[2] ^= seed[0] + seed[3] ^= seed[1] + seed[1] ^= seed[2] + seed[0] ^= seed[3] + seed[2] ^= t + seed[3] = rotl(seed[3], BigInt(45)) return Number(BigInt.asUintN(64, result) & BigInt(0xFFFFFFFF)) } @@ -633,7 +666,8 @@ function xoroshiro(s: bigint[]) function math_randomseed(_: Engine, x?: Variable, y?: Variable): Variable[] { - const seed = [0, 0, 0, 0].map(_ => BigInt(Math.floor(Math.random() * 100))) + const seed = create_seed() + if (x?.number != undefined) { seed[0] = BigInt(x.number & 0xFFFFFFFF) @@ -647,6 +681,7 @@ function math_randomseed(_: Engine, x?: Variable, y?: Variable): Variable[] } rand = xoroshiro(seed) + return [nil] } diff --git a/src/opcode.mts b/src/opcode.mts index 37f8a55..a18a40b 100644 --- a/src/opcode.mts +++ b/src/opcode.mts @@ -1,11 +1,5 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import type { Variable } from './runtime.mts' -import type { Debug } from './lexer.mts' +import type { Variable } from './runtime.mjs' +import type { Debug } from './lexer.mjs' export enum OpCode { Load, diff --git a/src/optimizer.mts b/src/optimizer.mts index fe57598..874dc6f 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,13 +1,7 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import type { Chunk, Expression } from './ast' -import type { IfBlock, While, For, NumericFor, Repeat } from './ast' -import type { Assignment, Value } from './ast' -import { StatementKind, ExpressionKind, ValueKind } from './ast' +import type { Chunk, Expression } from './ast.mjs' +import type { IfBlock, While, For, NumericFor, Repeat } from './ast.mjs' +import type { Assignment, Value } from './ast.mjs' +import { StatementKind, ExpressionKind, ValueKind } from './ast.mjs' const CONSTANT_VALUES = [ ValueKind.NilLiteral, @@ -16,9 +10,11 @@ const CONSTANT_VALUES = [ ValueKind.StringLiteral, ] -function compule_arithmatic_operation(expression: Expression, - operation: (a: number, b: number) => number, - constants: Map): Value | undefined +function compule_arithmetic_operation( + expression: Expression, + operation: (a: number, b: number) => number, + constants: Map +): Value | undefined { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) @@ -32,9 +28,11 @@ function compule_arithmatic_operation(expression: Expression, } } -function compule_comparison_operation(expression: Expression, - operation: (a: number, b: number) => boolean, - constants: Map): Value | undefined +function compule_comparison_operation( + expression: Expression, + operation: (a: number, b: number) => boolean, + constants: Map +): Value | undefined { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) @@ -48,9 +46,11 @@ function compule_comparison_operation(expression: Expression, } } -function compule_logical_operation(expression: Expression, - operation: (a: boolean, b: boolean) => boolean, - constants: Map): Value | undefined +function compule_logical_operation( + expression: Expression, + operation: (a: boolean, b: boolean) => boolean, + constants: Map +): Value | undefined { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) @@ -64,8 +64,10 @@ function compule_logical_operation(expression: Expression, } } -function compute_constant_expression(expression: Expression | undefined, - constants: Map): Value | undefined +function compute_constant_expression( + expression: Expression | undefined, + constants: Map +): Value | undefined { if (expression == undefined) return undefined @@ -85,20 +87,20 @@ function compute_constant_expression(expression: Expression | undefined, return undefined } - case ExpressionKind.Addition: return compule_arithmatic_operation(expression, (a, b) => a + b, constants) - case ExpressionKind.Subtract: return compule_arithmatic_operation(expression, (a, b) => a - b, constants) - case ExpressionKind.Multiplication: return compule_arithmatic_operation(expression, (a, b) => a * b, constants) - case ExpressionKind.Division: return compule_arithmatic_operation(expression, (a, b) => a / b, constants) - case ExpressionKind.FloorDivision: return compule_arithmatic_operation(expression, (a, b) => Math.floor(a / b), constants) - case ExpressionKind.Modulo: return compule_arithmatic_operation(expression, (a, b) => a % b, constants) - case ExpressionKind.Exponent: return compule_arithmatic_operation(expression, (a, b) => Math.pow(a, b), constants) + case ExpressionKind.Addition: return compule_arithmetic_operation(expression, (a, b) => a + b, constants) + case ExpressionKind.Subtract: return compule_arithmetic_operation(expression, (a, b) => a - b, constants) + case ExpressionKind.Multiplication: return compule_arithmetic_operation(expression, (a, b) => a * b, constants) + case ExpressionKind.Division: return compule_arithmetic_operation(expression, (a, b) => a / b, constants) + case ExpressionKind.FloorDivision: return compule_arithmetic_operation(expression, (a, b) => Math.floor(a / b), constants) + case ExpressionKind.Modulo: return compule_arithmetic_operation(expression, (a, b) => a % b, constants) + case ExpressionKind.Exponent: return compule_arithmetic_operation(expression, (a, b) => Math.pow(a, b), constants) case ExpressionKind.Concat: return undefined - case ExpressionKind.BitAnd: return compule_arithmatic_operation(expression, (a, b) => a & b, constants) - case ExpressionKind.BitOr: return compule_arithmatic_operation(expression, (a, b) => a | b, constants) - case ExpressionKind.BitXOr: return compule_arithmatic_operation(expression, (a, b) => a ^ b, constants) - case ExpressionKind.BitShiftLeft: return compule_arithmatic_operation(expression, (a, b) => a << b, constants) - case ExpressionKind.BitShiftRight: return compule_arithmatic_operation(expression, (a, b) => a >> b, constants) + case ExpressionKind.BitAnd: return compule_arithmetic_operation(expression, (a, b) => a & b, constants) + case ExpressionKind.BitOr: return compule_arithmetic_operation(expression, (a, b) => a | b, constants) + case ExpressionKind.BitXOr: return compule_arithmetic_operation(expression, (a, b) => a ^ b, constants) + case ExpressionKind.BitShiftLeft: return compule_arithmetic_operation(expression, (a, b) => a << b, constants) + case ExpressionKind.BitShiftRight: return compule_arithmetic_operation(expression, (a, b) => a >> b, constants) case ExpressionKind.BitNot: return undefined case ExpressionKind.Equals: return compule_comparison_operation(expression, (a, b) => a == b, constants) @@ -138,11 +140,16 @@ function compute_constant_expression(expression: Expression | undefined, case ExpressionKind.Length: return undefined + + default: + return undefined } } -function optimize_expression(expression: Expression | undefined, - constants: Map) +function optimize_expression( + expression: Expression | undefined, + constants: Map +): void { if (expression == undefined) return @@ -170,7 +177,7 @@ function optimize_expression(expression: Expression | undefined, optimize_expression(argument, constants) } -function mark_local_constants(assignment: Assignment, constants: Map) +function mark_local_constants(assignment: Assignment, constants: Map): void { for (const [index, rhs] of assignment.rhs.entries()) { @@ -178,13 +185,19 @@ function mark_local_constants(assignment: Assignment, constants: Map) +function unmark_constants_if_reassigned(assignment: Assignment, constants: Map): void { for (const lhs of assignment.lhs) { @@ -206,8 +219,10 @@ function unmark_constants_if_reassigned(assignment: Assignment, constants: Map) +function optimize_assignment( + assignment: Assignment | undefined, + constants: Map +): void { if (assignment == undefined) return @@ -221,8 +236,10 @@ function optimize_assignment(assignment: Assignment | undefined, optimize_expression(rhs, constants) } -function remove_constant_local_assignments(chunk: Chunk, - constants: Map) +function remove_constant_local_assignments( + chunk: Chunk, + constants: Map +): void { for (const statement of chunk.statements) { @@ -246,7 +263,7 @@ function remove_constant_local_assignments(chunk: Chunk, .filter(x => x.assignment == undefined || x.assignment.lhs.length > 0) } -function optimize_if(if_block: IfBlock | undefined, constants: Map) +function optimize_if(if_block: IfBlock | undefined, constants: Map): void { if (if_block == undefined) return @@ -255,7 +272,7 @@ function optimize_if(if_block: IfBlock | undefined, constants: Map) +function optimize_while(while_block: While | undefined, constants: Map): void { if (while_block == undefined) return @@ -264,16 +281,16 @@ function optimize_while(while_block: While | undefined, constants: Map) +function optimize_for(for_block: For | undefined, constants: Map): void { if (for_block == undefined) return - optimize_expression(for_block.itorator, constants) + optimize_expression(for_block.iterator, constants) optimize_chunk(for_block.body, constants) } -function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map) +function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map): void { if (numeric_for_block == undefined) return @@ -284,7 +301,7 @@ function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constan optimize_chunk(numeric_for_block.body, constants) } -function optimize_repeat(repeat_block: Repeat | undefined, constants: Map) +function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void { if (repeat_block == undefined) return @@ -293,7 +310,7 @@ function optimize_repeat(repeat_block: Repeat | undefined, constants: Map) +export function optimize_chunk(chunk: Chunk, parent_constants?: Map): void { const constants = new Map(parent_constants) for (const statement of chunk.statements) diff --git a/src/parser.mts b/src/parser.mts index c6a6fc4..56518d6 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,15 +1,9 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ +import type { Chunk, ElseIfBlock } from './ast.mjs' +import type { Expression, Statement, Value } from './ast.mjs' +import type { Token } from './lexer.mjs' -import type { Chunk, IfElseBlock } from './ast' -import type { Expression, Statement, Value } from './ast' -import type { Token } from './lexer.mts' - -import { ExpressionKind, StatementKind, ValueKind } from './ast' -import { TokenKind, TokenStream, token_kind_to_string } from './lexer.mts' +import { ExpressionKind, StatementKind, ValueKind } from './ast.mjs' +import { TokenKind, TokenStream, token_kind_to_string } from './lexer.mjs' const UNARY = [ TokenKind.Not, @@ -40,6 +34,10 @@ function error(token: Token, message: string): Error function expect(stream: TokenStream, kind: TokenKind): Token | Error { const token = stream.peek() + + if (token === undefined) + throw new Error() + if (token.kind != kind) { return error(token, @@ -53,6 +51,10 @@ function expect(stream: TokenStream, kind: TokenKind): Token | Error function consume(stream: TokenStream, kind: TokenKind): boolean { const token = stream.peek() + + if (token === undefined) + throw new Error() + if (token.kind != kind) return false @@ -76,6 +78,7 @@ function parse_table_key(stream: TokenStream): Expression | Error } const value = parse_value(stream) + if (value instanceof Error) return value @@ -101,6 +104,7 @@ function parse_table(stream: TokenStream): Value | Error const elements: Map = new Map() let current_numeric_key = 1 + while (stream.peek().kind != TokenKind.SquiglyClose) { const element = parse_table_key(stream) @@ -373,24 +377,34 @@ function operation_type_to_expression_kind( } } -function parse_operation(stream: TokenStream, - order: number): Expression | Error +function parse_operation( + stream: TokenStream, + order: number +): Expression | Error { if (order >= ORDERS.length) return parse_value_expression(stream) let lhs = parse_operation(stream, order + 1) + if (lhs instanceof Error) return lhs - while (ORDERS[order].includes(stream.peek().kind)) + const orders_order = ORDERS[order] + + if (orders_order === undefined) + throw new Error() + + while (orders_order.includes(stream.peek().kind)) { const operation_type = stream.next() const rhs = parse_operation(stream, order + 1) + if (rhs instanceof Error) return rhs const expression_kind = operation_type_to_expression_kind(operation_type.kind) + lhs = { kind: expression_kind, token: operation_type, @@ -445,10 +459,11 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error const assign = expect(stream, TokenKind.Assign) if (assign instanceof Error) { - if (!(local instanceof Error)) - return parse_local_statement(local, lhs) - else + if (local instanceof Error) + // @TODO: Investigate as lhs[0] seems to always be undefined return { kind: StatementKind.Expression, expression: lhs[0] } + + return parse_local_statement(local, lhs) } const rhs: Expression[] = [] @@ -541,7 +556,7 @@ function parse_if(stream: TokenStream): Statement | Error if (body instanceof Error) return body - const else_if_bodies: IfElseBlock[] = [] + const else_if_bodies: ElseIfBlock[] = [] let else_body: Chunk | undefined = undefined while (consume(stream, TokenKind.ElseIf)) { @@ -678,15 +693,22 @@ function parse_for(stream: TokenStream): Statement | Error } if (consume(stream, TokenKind.Assign)) - return parse_numeric_for(items[0], stream) + { + const token = items[0]; + + if (token === undefined) + throw new Error() + + return parse_numeric_for(token, stream) + } const in_token = expect(stream, TokenKind.In) if (in_token instanceof Error) return in_token - const itorator = parse_expression(stream) - if (itorator instanceof Error) - return itorator + const iterator = parse_expression(stream) + if (iterator instanceof Error) + return iterator const do_token = expect(stream, TokenKind.Do) if (do_token instanceof Error) @@ -697,11 +719,12 @@ function parse_for(stream: TokenStream): Statement | Error return body consume(stream, TokenKind.End) + return { kind: StatementKind.For, for: { items: items, - itorator: itorator, + iterator: iterator, body: body, token: for_token, }, @@ -926,7 +949,13 @@ function parse_statement(stream: TokenStream, end_tokens: TokenKind[]): Statemen default: if (end_tokens.includes(token.kind)) return undefined - return error(token, `Missing '${ token_kind_to_string(end_tokens[0]) }', ` + + + const first_end_token = end_tokens[0] + + if (first_end_token === undefined) + throw new Error() + + return error(token, `Missing '${ token_kind_to_string(first_end_token) }', ` + `got '${ token_kind_to_string(token.kind) }' instead`) } } diff --git a/src/runtime.mts b/src/runtime.mts index 6882260..2b06f0e 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -1,10 +1,4 @@ -/* - * Copyright (c) 2022, Ben Jilks - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -import { Engine } from './engine.mts' +import { Engine } from './engine.mjs' export enum DataType { Nil, @@ -16,14 +10,15 @@ export enum DataType { Table, } -export type NativeFunction = (engine: Engine, ...args: Variable[]) => Variable[] +export type NativeFunction = (engine: Engine, ...args: Array) => Array | Error + export interface Variable { data_type: DataType, boolean?: boolean, number?: number, string?: string, native_function?: NativeFunction, - table?: Map, + table?: Map, function_id?: number, locals?: Map[], From d6d9ee407aefaf09371c52789b364b1e05945b47 Mon Sep 17 00:00:00 2001 From: Zamralik Date: Tue, 20 Jan 2026 13:27:35 +0100 Subject: [PATCH 05/71] fix: various errors --- src/ast.mts | 8 +- src/boundary/_index.mts | 2 + src/boundary/definition/_index.mts | 1 + src/boundary/definition/type/_index.mts | 4 + .../definition/type/matrix2d.type.mts | 3 + .../definition/type/native-function.type.mts | 6 + .../definition/type/table-input.type.mts | 3 + .../definition/type/table-map.type.mts | 5 + src/boundary/predicate/_index.mts | 2 + .../predicate/is-table-input-type.mts | 9 + src/boundary/predicate/is-table-key.mts | 8 + src/compiler.mts | 79 +- src/create-binding.mts | 109 +++ src/engine.mts | 482 ++++++---- src/index.mts | 34 +- src/lexer.mts | 58 +- src/lib.mts | 907 ++++++++++-------- src/opcode.mts | 18 +- src/optimizer.mts | 145 +-- src/parser.mts | 42 +- src/runtime-error.mts | 38 + src/runtime.mts | 146 ++- src/variable/_index.mts | 5 + src/variable/definition/_index.mts | 3 + src/variable/definition/enum/_index.mts | 1 + .../definition/enum/variable-kind.enum.mts | 12 + src/variable/definition/interface/_index.mts | 8 + .../interface/base-variable.interface.mts | 8 + .../interface/variable-boolean.interface.mts | 10 + .../interface/variable-function.interface.mts | 10 + .../variable-native-function.interface.mts | 11 + .../interface/variable-nil.interface.mts | 9 + .../interface/variable-number.interface.mts | 10 + .../interface/variable-string.interface.mts | 10 + .../interface/variable-table.interface.mts | 11 + src/variable/definition/type/_index.mts | 2 + .../definition/type/variable-value.type.mts | 16 + .../definition/type/variable.type.mts | 19 + src/variable/equals.mts | 47 + src/variable/nil.mts | 6 + src/variable/predicate/_index.mts | 6 + .../predicate/assert-variable-kind.mts | 16 + src/variable/predicate/assert-variable.mts | 13 + src/variable/predicate/is-nil.mts | 12 + .../predicate/is-variable-kind-enum.mts | 17 + src/variable/predicate/is-variable-kind.mts | 10 + src/variable/predicate/is-variable.mts | 18 + src/variable/unwrap-variable.mts | 82 ++ 48 files changed, 1701 insertions(+), 780 deletions(-) create mode 100644 src/boundary/_index.mts create mode 100644 src/boundary/definition/_index.mts create mode 100644 src/boundary/definition/type/_index.mts create mode 100644 src/boundary/definition/type/matrix2d.type.mts create mode 100644 src/boundary/definition/type/native-function.type.mts create mode 100644 src/boundary/definition/type/table-input.type.mts create mode 100644 src/boundary/definition/type/table-map.type.mts create mode 100644 src/boundary/predicate/_index.mts create mode 100644 src/boundary/predicate/is-table-input-type.mts create mode 100644 src/boundary/predicate/is-table-key.mts create mode 100644 src/create-binding.mts create mode 100644 src/runtime-error.mts create mode 100644 src/variable/_index.mts create mode 100644 src/variable/definition/_index.mts create mode 100644 src/variable/definition/enum/_index.mts create mode 100644 src/variable/definition/enum/variable-kind.enum.mts create mode 100644 src/variable/definition/interface/_index.mts create mode 100644 src/variable/definition/interface/base-variable.interface.mts create mode 100644 src/variable/definition/interface/variable-boolean.interface.mts create mode 100644 src/variable/definition/interface/variable-function.interface.mts create mode 100644 src/variable/definition/interface/variable-native-function.interface.mts create mode 100644 src/variable/definition/interface/variable-nil.interface.mts create mode 100644 src/variable/definition/interface/variable-number.interface.mts create mode 100644 src/variable/definition/interface/variable-string.interface.mts create mode 100644 src/variable/definition/interface/variable-table.interface.mts create mode 100644 src/variable/definition/type/_index.mts create mode 100644 src/variable/definition/type/variable-value.type.mts create mode 100644 src/variable/definition/type/variable.type.mts create mode 100644 src/variable/equals.mts create mode 100644 src/variable/nil.mts create mode 100644 src/variable/predicate/_index.mts create mode 100644 src/variable/predicate/assert-variable-kind.mts create mode 100644 src/variable/predicate/assert-variable.mts create mode 100644 src/variable/predicate/is-nil.mts create mode 100644 src/variable/predicate/is-variable-kind-enum.mts create mode 100644 src/variable/predicate/is-variable-kind.mts create mode 100644 src/variable/predicate/is-variable.mts create mode 100644 src/variable/unwrap-variable.mts diff --git a/src/ast.mts b/src/ast.mts index 01df624..eba0a15 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -54,10 +54,10 @@ export enum ExpressionKind Equals, NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, + LessThan, + LessThanEquals, + GreaterThan, + GreaterThanEquals, And, Or, diff --git a/src/boundary/_index.mts b/src/boundary/_index.mts new file mode 100644 index 0000000..9a7a2d4 --- /dev/null +++ b/src/boundary/_index.mts @@ -0,0 +1,2 @@ +export type * from "./definition/_index.mjs"; +export * from "./predicate/_index.mjs"; diff --git a/src/boundary/definition/_index.mts b/src/boundary/definition/_index.mts new file mode 100644 index 0000000..343d0f1 --- /dev/null +++ b/src/boundary/definition/_index.mts @@ -0,0 +1 @@ +export type * from "./type/_index.mjs"; diff --git a/src/boundary/definition/type/_index.mts b/src/boundary/definition/type/_index.mts new file mode 100644 index 0000000..5f80059 --- /dev/null +++ b/src/boundary/definition/type/_index.mts @@ -0,0 +1,4 @@ +export type * from "./matrix2d.type.mjs"; +export type * from "./native-function.type.mjs"; +export type * from "./table-map.type.mjs"; +export type * from "./table-input.type.mjs"; diff --git a/src/boundary/definition/type/matrix2d.type.mts b/src/boundary/definition/type/matrix2d.type.mts new file mode 100644 index 0000000..6cd965f --- /dev/null +++ b/src/boundary/definition/type/matrix2d.type.mts @@ -0,0 +1,3 @@ +type Matrix2D = Array>; + +export type { Matrix2D }; diff --git a/src/boundary/definition/type/native-function.type.mts b/src/boundary/definition/type/native-function.type.mts new file mode 100644 index 0000000..db1f948 --- /dev/null +++ b/src/boundary/definition/type/native-function.type.mts @@ -0,0 +1,6 @@ +import type { Engine } from "../../../engine.mjs"; +import type { Variable } from "../../../variable/definition/type/variable.type.mjs"; + +type NativeFunction = (engine: Engine, ...args: Array) => Array + +export type { NativeFunction }; diff --git a/src/boundary/definition/type/table-input.type.mts b/src/boundary/definition/type/table-input.type.mts new file mode 100644 index 0000000..c3ff6dc --- /dev/null +++ b/src/boundary/definition/type/table-input.type.mts @@ -0,0 +1,3 @@ +type TableInputType = Array | Record | Map; + +export type { TableInputType }; diff --git a/src/boundary/definition/type/table-map.type.mts b/src/boundary/definition/type/table-map.type.mts new file mode 100644 index 0000000..4595983 --- /dev/null +++ b/src/boundary/definition/type/table-map.type.mts @@ -0,0 +1,5 @@ +import type { Variable } from "../../../variable/definition/type/variable.type.mjs"; + +type TableMap = Map; + +export type { TableMap }; diff --git a/src/boundary/predicate/_index.mts b/src/boundary/predicate/_index.mts new file mode 100644 index 0000000..f2b740b --- /dev/null +++ b/src/boundary/predicate/_index.mts @@ -0,0 +1,2 @@ +export * from "./is-table-input-type.mjs"; +export * from "./is-table-key.mjs"; diff --git a/src/boundary/predicate/is-table-input-type.mts b/src/boundary/predicate/is-table-input-type.mts new file mode 100644 index 0000000..3f11f3e --- /dev/null +++ b/src/boundary/predicate/is-table-input-type.mts @@ -0,0 +1,9 @@ +import { isArray, isInstanceOf, isRecord, isUnion, unary } from "@vitruvius-labs/ts-predicate"; +import type { TableInputType } from "../definition/type/table-input.type.mjs"; + +function isTableInputType(input: unknown): input is TableInputType +{ + return isUnion(input, [isArray, isRecord, unary(isInstanceOf, Map)]); +} + +export { isTableInputType }; diff --git a/src/boundary/predicate/is-table-key.mts b/src/boundary/predicate/is-table-key.mts new file mode 100644 index 0000000..07ccdb7 --- /dev/null +++ b/src/boundary/predicate/is-table-key.mts @@ -0,0 +1,8 @@ +import { isNumber, isString, isUnion } from "@vitruvius-labs/ts-predicate"; + +function isTableKey(value: unknown): value is string | number +{ + return isUnion(value, [isNumber, isString]); +} + +export { isTableKey }; diff --git a/src/compiler.mts b/src/compiler.mts index c512166..43fb540 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -6,8 +6,9 @@ import type { Assignment, Local, Return } from './ast.mjs' import { StatementKind, ExpressionKind, ValueKind } from './ast.mjs' import { OpCode } from './opcode.mjs' -import { DataType } from './runtime.mjs' -import { make_boolean, make_number, make_string, nil } from './runtime.mjs' +import { make_boolean, make_number, make_string } from './runtime.mjs' +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" +import { nil } from "./variable/nil.mjs" function compile_function(chunk: Chunk, token: Token, parameters: Token[], functions: Op[][]): number { @@ -28,7 +29,7 @@ function compile_function(chunk: Chunk, token: Token, parameters: Token[], funct function compile_value(value: Value | undefined, functions: Op[][]): Op[] { - if (value == undefined) + if (value === undefined) throw new Error() const debug = value.token.debug @@ -47,7 +48,7 @@ function compile_value(value: Value | undefined, functions: Op[][]): Op[] { return [{ code: OpCode.Push, arg: { - data_type: DataType.Function, + data_type: VariableKind.Function, function_id: compile_function( value.function?.body ?? { statements: [] }, value.token, @@ -77,7 +78,7 @@ function compile_value(value: Value | undefined, functions: Op[][]): Op[] { return [{ code: OpCode.Load, - arg: { data_type: DataType.String, string: value.identifier ?? '' }, + arg: { data_type: VariableKind.String, string: value.identifier ?? '' }, debug: debug, }] } @@ -94,7 +95,7 @@ function compile_operation( ): Op[] { const { lhs, rhs } = expression - if (lhs == undefined || rhs == undefined) + if (lhs === undefined || rhs === undefined) throw new Error() const ops: Op[] = [] @@ -110,7 +111,7 @@ function compile_call( functions: Op[][] ): Op[] { - if (func == undefined || args == undefined) + if (func === undefined || args === undefined) throw new Error() const debug = func.token.debug @@ -129,7 +130,7 @@ function compile_index( functions: Op[][] ): Op[] { - if (target == undefined || index == undefined) + if (target === undefined || index === undefined) throw new Error() const ops: Op[] = [] @@ -145,7 +146,7 @@ function compile_unary_operation( functions: Op[][] ): Op[] { - if (expression == undefined || expression.expression == undefined) + if (expression === undefined || expression.expression === undefined) throw new Error() const ops: Op[] = [] @@ -156,7 +157,7 @@ function compile_unary_operation( function compile_expression(expression: Expression | undefined, functions: Op[][]): Op[] { - if (expression == undefined) + if (expression === undefined) throw new Error() switch (expression.kind) @@ -200,14 +201,14 @@ function compile_expression(expression: Expression | undefined, functions: Op[][ return compile_operation(expression, OpCode.Equals, functions) case ExpressionKind.NotEquals: return compile_operation(expression, OpCode.NotEquals, functions) - case ExpressionKind.LessThen: - return compile_operation(expression, OpCode.LessThen, functions) - case ExpressionKind.LessThenEquals: - return compile_operation(expression, OpCode.LessThenEquals, functions) - case ExpressionKind.GreaterThen: - return compile_operation(expression, OpCode.GreaterThen, functions) - case ExpressionKind.GreaterThenEquals: - return compile_operation(expression, OpCode.GreaterThenEquals, functions) + case ExpressionKind.LessThan: + return compile_operation(expression, OpCode.LessThan, functions) + case ExpressionKind.LessThanEquals: + return compile_operation(expression, OpCode.LessThanEquals, functions) + case ExpressionKind.GreaterThan: + return compile_operation(expression, OpCode.GreaterThan, functions) + case ExpressionKind.GreaterThanEquals: + return compile_operation(expression, OpCode.GreaterThanEquals, functions) case ExpressionKind.And: return compile_operation(expression, OpCode.And, functions) case ExpressionKind.Or: @@ -229,7 +230,7 @@ function compile_expression(expression: Expression | undefined, functions: Op[][ function compile_assignment(assignment: Assignment | undefined, functions: Op[][]): Op[] { - if (assignment == undefined) + if (assignment === undefined) throw new Error() const ops: Op[] = [] @@ -246,7 +247,7 @@ function compile_assignment(assignment: Assignment | undefined, functions: Op[][ { case ExpressionKind.Value: { - if (lhs.value?.kind != ValueKind.Variable) + if (lhs.value?.kind !== ValueKind.Variable) throw new Error() const identifier = make_string(lhs.value?.identifier ?? '') @@ -278,14 +279,14 @@ function compile_assignment(assignment: Assignment | undefined, functions: Op[][ function compile_local(local: Local | undefined): Op[] { - if (local == undefined) + if (local === undefined) throw new Error() return local.names .map(name => ({ code: OpCode.MakeLocal, arg: { - data_type: DataType.String, + data_type: VariableKind.String, string: name.data, }, debug: name.debug, @@ -294,7 +295,7 @@ function compile_local(local: Local | undefined): Op[] function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] { - if (condition == undefined) + if (condition === undefined) throw new Error() const ops: Op[] = [] @@ -336,7 +337,7 @@ function compile_inverted_conditional_jump(condition: Expression | undefined, ju function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] { - if (condition == undefined) + if (condition === undefined) throw new Error() const ops: Op[] = [] @@ -378,11 +379,11 @@ function compile_conditional_jump(condition: Expression | undefined, jump_by: nu function compile_if(if_block: IfBlock | undefined, functions: Op[][]): Op[] { - if (if_block == undefined) + if (if_block === undefined) throw new Error() const else_chunk: Op[] = [] - if (if_block.else_body != undefined) + if (if_block.else_body !== undefined) else_chunk.push(...compile_block(if_block.else_body, functions)) const if_else_chunks: Op[][] = [] @@ -419,7 +420,7 @@ function replace_breaks(code: Op[], offset_from_end: number): void { for (const [i, op] of code.entries()) { - if (op.code == OpCode.Break) + if (op.code === OpCode.Break) { const offset = code.length - i - 1 + offset_from_end op.code = OpCode.Jump @@ -430,7 +431,7 @@ function replace_breaks(code: Op[], offset_from_end: number): void function compile_while(while_block: While | undefined, functions: Op[][]): Op[] { - if (while_block == undefined) + if (while_block === undefined) throw new Error() const debug = while_block.token.debug @@ -448,7 +449,7 @@ function compile_while(while_block: While | undefined, functions: Op[][]): Op[] function compile_for(for_block: For | undefined, functions: Op[][]): Op[] { - if (for_block == undefined) + if (for_block === undefined) throw new Error() const ops: Op[] = [] @@ -469,7 +470,7 @@ function compile_for(for_block: For | undefined, functions: Op[][]): Op[] ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }) for (const [i, item] of [...for_block.items].reverse().entries()) { - if (i == for_block.items.length - 1) + if (i === for_block.items.length - 1) ops.push({ code: OpCode.IterUpdateState, debug: debug }) ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }) } @@ -484,7 +485,7 @@ function compile_for(for_block: For | undefined, functions: Op[][]): Op[] function compile_step(step: Expression | undefined, functions: Op[][]): Op[] { - if (step == undefined) + if (step === undefined) return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }] return compile_expression(step, functions) @@ -492,7 +493,7 @@ function compile_step(step: Expression | undefined, functions: Op[][]): Op[] function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Op[][]): Op[] { - if (numeric_for_block == undefined) + if (numeric_for_block === undefined) throw new Error() const ops: Op[] = [] @@ -525,7 +526,7 @@ function compile_numeric_for(numeric_for_block: NumericFor | undefined, function function compile_repeat(repeat: Repeat | undefined, functions: Op[][]): Op[] { - if (repeat == undefined) + if (repeat === undefined) throw new Error() const ops: Op[] = [] @@ -542,7 +543,7 @@ function compile_repeat(repeat: Repeat | undefined, functions: Op[][]): Op[] function compile_do(do_block: Do | undefined, functions: Op[][]): Op[] { - if (do_block == undefined) + if (do_block === undefined) throw new Error() const ops: Op[] = [] @@ -555,7 +556,7 @@ function compile_do(do_block: Do | undefined, functions: Op[][]): Op[] function compile_return(return_block: Return | undefined, functions: Op[][]): Op[] { - if (return_block == undefined) + if (return_block === undefined) throw new Error() const ops: Op[] = [] @@ -590,14 +591,14 @@ function compile_chunk(chunk: Chunk, functions: Op[][]): ChunkResult for (const [index, statement] of chunk.statements.entries()) { - const is_last_statement = (index == chunk.statements.length - 1) + const is_last_statement = (index === chunk.statements.length - 1) switch (statement.kind) { case StatementKind.Empty: break case StatementKind.Expression: ops.push(...compile_expression(statement.expression, functions)) - if (statement.expression == undefined) + if (statement.expression === undefined) break if (is_last_statement) @@ -648,8 +649,8 @@ function link(code: Op[], function_id: number, location: number): void { for (const op of code) { - if (op.arg?.data_type == DataType.Function && - op.arg?.function_id == function_id) + if (op.arg?.data_type === VariableKind.Function && + op.arg?.function_id === function_id) { op.arg.function_id = location } diff --git a/src/create-binding.mts b/src/create-binding.mts new file mode 100644 index 0000000..a234020 --- /dev/null +++ b/src/create-binding.mts @@ -0,0 +1,109 @@ +import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError } from "@vitruvius-labs/ts-predicate"; +import type { Engine } from "./engine.mjs"; +import { make_variable } from "./runtime.mjs"; +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; +import type { Variable } from "./variable/definition/type/variable.type.mjs"; +import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; +import { RuntimeError } from "./runtime-error.mjs"; +import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; + +export const enum ParameterOptionEnum +{ + REQUIRED = "required", + OPTIONAL = "optional", + VARIADIC = "variadic", +} + +export interface ParameterDescriptorInterface +{ + test: (value: unknown) => asserts value is T, + option?: ParameterOptionEnum, +} + +function sanitize_parameters(input: Array, descriptors: Array): Array +{ + const output: Array = []; + + for (let i = 0; i < descriptors.length; ++i) + { + const descriptor: ParameterDescriptorInterface | undefined = descriptors.at(i); + + assertDefined(descriptor); + + const option: ParameterOptionEnum = descriptor.option ?? ParameterOptionEnum.REQUIRED; + + if (option === ParameterOptionEnum.VARIADIC) + { + const variadic_args: Array = input.slice(i); + + assertArray(variadic_args, descriptor.test); + + output.push(...variadic_args); + + break; + } + + const parameter_value: unknown = input.at(i); + + if (isNullish(parameter_value)) + { + if (option === ParameterOptionEnum.REQUIRED) + { + throw new Error(`Missing required parameter at index ${i.toFixed(0)}.`); + } + + output.push(undefined); + + continue; + } + + // @ts-expect-error: We cannot know which type is expected + descriptor.test(parameter_value); + + output.push(parameter_value); + } + + return output; +} + +function handle_error(error: unknown, callable: Function): never +{ + if (isInstanceOf(error, RuntimeError) || isInstanceOf(error, ValidationError)) + { + throw error; + } + + if (callable.name === "") + { + throw new RuntimeError("An error occurred during native anonymous function execution.", { cause: error }); + } + + throw new RuntimeError(`An error occurred during native function ${callable.name} execution.`, { cause: error }); +} + +export function make_function(callable: Function, parameters_descriptor: Array): Variable +{ + const proxy_function: NativeFunction = (_: Engine, ...args: Array): Array => { + try + { + const unwrapped_args: Array = args.map(VariableUnwrapUtility.unwrap); + const sanitized_args: Array = sanitize_parameters(unwrapped_args, parameters_descriptor); + const result: unknown = callable(...sanitized_args); + const variable: Variable = make_variable(result); + + return [variable]; + } + catch (error: unknown) + { + handle_error(error, callable); + } + }; + + // Bypass readonly constraint + Object.defineProperty(proxy_function, "name", { value: callable.name }); + + return { + data_type: VariableKind.NativeFunction, + native_function: proxy_function, + }; +} diff --git a/src/engine.mts b/src/engine.mts index c3695d5..fdfd2d0 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,75 +1,50 @@ -import type { Op } from './opcode.mjs' -import type { NativeFunction, Variable } from './runtime.mjs' +import { assertUnion, unary } from "@vitruvius-labs/ts-predicate" +import type { Op } from './opcode.mjs' import { OpCode, op_code_name } from './opcode.mjs' -import { DataType, nil, make_number, make_boolean, make_string } from './runtime.mjs' +import { make_number, make_boolean, make_string, make_table } from './runtime.mjs' import { TokenStream } from './lexer.mjs' import { parse } from './parser.mjs' import { compile } from './compiler.mjs' import { optimize_chunk } from './optimizer.mjs' import * as std from './lib.mjs' +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" +import type { Variable } from "./variable/definition/type/variable.type.mjs" +import { RuntimeError } from "./runtime-error.mjs" +import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs" +import { isNil } from "./variable/predicate/is-nil.mjs" +import { nil } from "./variable/nil.mjs" +import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs" +import type { TableMap } from "./boundary/definition/type/table-map.type.mjs" +import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; +import type { VariableFunction } from "./variable/definition/interface/variable-function.interface.mjs" +import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs" +import { equals } from "./variable/equals.mjs" +import { assertVariable } from "./variable/predicate/assert-variable.mjs" function index(val: Variable | undefined): string | number | undefined { - if (val == undefined) + if (val === undefined) return undefined - else if (val.data_type == DataType.String) + + if (val.data_type === VariableKind.String) return val.string - else if (val.data_type == DataType.Number) - return val.number - else - return undefined -} -function is_true(val: Variable | undefined): boolean -{ - if (val == undefined) - return false + if (val.data_type === VariableKind.Number) + return val.number - switch (val.data_type) - { - case DataType.Number: return val.number != 0 - case DataType.String: return val.string != '' - case DataType.Boolean: return val.boolean ?? false - case DataType.Function: return true - case DataType.NativeFunction: return true - default: - return false - } + return undefined } -function equals(a: Variable | undefined, b: Variable | undefined): boolean +function is_true(val: Variable | undefined): boolean { - if (a == undefined && b == undefined) - return true - if (a == undefined || b == undefined) - return false - - if (a.data_type != b.data_type) + if (isNil(val)) return false - switch (a.data_type) - { - case DataType.Nil: return true - case DataType.Boolean: return a.boolean == b.boolean - case DataType.Number: return a.number == b.number - case DataType.String: return a.string == b.string - case DataType.Function: return a.function_id == b.function_id - case DataType.NativeFunction: return a.native_function == b.native_function - case DataType.Table: - { - if (a.table?.keys() != b.table?.keys()) - return false - - for (const key of a.table?.keys() ?? []) - { - if (!equals(a.table?.get(key), b.table?.get(key))) - return false - } + if (isVariableKind(val, VariableKind.Boolean)) + return val.boolean - return true - } - } + return true } export interface LuaOptions @@ -82,23 +57,22 @@ export interface LuaOptions export class Engine { + private program: Op[]; + private globals: TableMap; + private start_ip: number = 0; - private program: Op[] - private globals: Map - private start_ip: number = 0 - - private ip: number = 0 - private stack: Variable[] = [] - private locals_stack: Map[] = [] - private locals_capture: Map[] = [] - private call_stack: number[] = [] - private assign_height_stack: number[] = [] + private ip: number = 0; + private stack: Variable[] = []; + private locals_stack: Map[] = []; + private locals_capture: Map[] = []; + private call_stack: number[] = []; + private assign_height_stack: number[] = []; - private error: Error | undefined + private error: Error | undefined; constructor( script?: string, - globals?: Map + globals?: TableMap ) { this.program = [] @@ -106,10 +80,10 @@ export class Engine this.error = undefined this.reset() - if (script != undefined) + if (script !== undefined) { const result = this.load(script) - if (result != undefined) + if (result !== undefined) this.error = result } } @@ -134,14 +108,14 @@ export class Engine bytecode(): string[] { - if (this.error != undefined) + if (this.error !== undefined) return [ this.error.message ] const output = [] for (const [i, op] of this.program.entries()) { - const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' - if (i == this.ip) + const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : '' + if (i === this.ip) output.push(`* ${ i } ${ op_code_name(op.code) } ${ arg }`) else output.push(`${ i } ${ op_code_name(op.code) } ${ arg }`) @@ -158,7 +132,7 @@ export class Engine define(name: string, func: NativeFunction): void { this.globals.set(name, { - data_type: DataType.NativeFunction, + data_type: VariableKind.NativeFunction, native_function: func, }) } @@ -166,7 +140,7 @@ export class Engine define_table(name: string, table: Map): void { this.globals.set(name, { - data_type: DataType.Table, + data_type: VariableKind.Table, table: table, }) } @@ -185,11 +159,10 @@ export class Engine call(func: Variable, ...args: Variable[]): Variable[] | Error { - if (func.native_function != undefined) + if (isVariableKind(func, VariableKind.NativeFunction)) return this.call_native_function(func.native_function, ...args) - if (func.function_id == undefined) - return new Error() // TODO: Error message + assertUnion(func, [unary(assertVariableKind, VariableKind.Function), unary(assertVariableKind, VariableKind.NativeFunction)]); const old_stack = this.stack const old_call_stack = this.call_stack @@ -202,6 +175,7 @@ export class Engine for (const arg of args) this.stack.push(arg) + this.stack.push(make_number(args.length)) this.locals_stack.push(new Map()) @@ -220,10 +194,10 @@ export class Engine run_for_steps(steps: number, options?: LuaOptions): Variable | Error | undefined { - if (this.error != undefined) + if (this.error !== undefined) return this.error - if (options?.locals != undefined) + if (options?.locals !== undefined) this.locals_stack.push(options.locals) let step_count = 0 @@ -231,7 +205,7 @@ export class Engine { const result = this.step(options) - if (result != undefined) + if (result !== undefined) return result step_count += 1 @@ -246,7 +220,7 @@ export class Engine { const result = this.run_for_steps(1000, options) - if (result == undefined) + if (result === undefined) return new Error('Program ran for too long') else return result @@ -268,11 +242,34 @@ export class Engine return this.stack[index] ?? nil; } + private stack_pop_maybe(): Variable + { + return this.stack.pop() ?? nil; + } + + private stack_pop(): Variable + { + const value = this.stack.pop(); + + assertVariable(value); + + return value; + } + + private stack_pop_kind(kind: K): Variable & { data_type: K } + { + const value = this.stack.pop(); + + assertVariableKind(value, kind); + + return value; + } + private call_native_function(native_function: NativeFunction, ...args: Array): Array | Error { const results = native_function(this, ...args) - if (this.error != undefined) + if (this.error !== undefined) { const error = this.error this.error = undefined @@ -285,49 +282,53 @@ export class Engine private operation(op: (x: number, y: number) => number): void { - const x = this.stack.pop()?.number ?? 0 - const y = this.stack.pop()?.number ?? 0 - this.stack.push(make_number(op(x, y))) + const x = this.stack_pop_kind(VariableKind.Number); + const y = this.stack_pop_kind(VariableKind.Number); + + this.stack.push(make_number(op(x.number, y.number))) } - private compair(op: (x: number, y: number) => boolean): void + private compare(op: (x: number, y: number) => boolean): void { - const x = this.stack.pop()?.number ?? 0 - const y = this.stack.pop()?.number ?? 0 - this.stack.push(make_boolean(op(x, y))) + const x = this.stack_pop_kind(VariableKind.Number); + const y = this.stack_pop_kind(VariableKind.Number); + + this.stack.push(make_boolean(op(x.number, y.number))) } private force_stack_height(expected: number, got: number): void { - for (let i = got; i < expected; i++) + for (let i = got; i < expected; ++i) this.stack.push(nil) - for (let i = expected; i < got; i++) + + for (let i = expected; i < got; ++i) this.stack.pop() } - private runtime_error(op: Op | undefined, message: string): Error + private runtime_error(op: Op | undefined, message: string): never { - if (op == undefined) - return new Error(`${ message }`) - else - return new Error(`${ op.debug.line }:${ op.debug.column }: ${ message }`) + if (op === undefined) + throw new RuntimeError(`${ message }`) + + throw new RuntimeError(message, {}, op.debug) } private run_instruction(op: Op): Error | undefined { const { code, arg } = op + switch(code) { case OpCode.Pop: { - const count = arg?.number ?? 1 + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 this.stack.splice(this.stack.length - count, count) break } case OpCode.Dup: { - const count = arg?.number ?? 1 + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 const items = this.stack.splice(this.stack.length - count, count) this.stack.push(...items, ...items) break @@ -352,7 +353,7 @@ export class Engine const control = this.stack_get(-2) const iter = this.stack_get(-3) - if (iter.native_function !== undefined) + if (isVariableKind(iter, VariableKind.NativeFunction)) { const result = this.call_native_function(iter.native_function, control, state) @@ -367,90 +368,196 @@ export class Engine this.call_stack.push(this.ip) this.locals_stack.push(new Map()) this.locals_capture = iter.locals ?? [] - this.ip = iter.function_id ?? this.ip + + if (isVariableKind(iter, VariableKind.Function) && iter.function_id !== undefined) + { + this.ip = iter.function_id; + } + break } case OpCode.IterJumpIfDone: { - if (this.stack_get(-1).data_type == DataType.Nil) - this.ip += arg?.number ?? 0 + if (isVariableKind(arg, VariableKind.Number) && isNil(this.stack_get(-1))) + { + this.ip += arg.number + } + break } - case OpCode.Add: this.operation((x, y) => x + y); break - case OpCode.Subtract: this.operation((x, y) => x - y); break - case OpCode.Multiply: this.operation((x, y) => x * y); break - case OpCode.Divide: this.operation((x, y) => x / y); break - case OpCode.FloorDivide: this.operation((x, y) => Math.floor(x / y)); break - case OpCode.Modulo: this.operation((x, y) => x % y); break - case OpCode.Exponent: this.operation((x, y) => Math.pow(x, y)); break - case OpCode.LessThen: this.compair((x, y) => x < y); break - case OpCode.LessThenEquals: this.compair((x, y) => x <= y); break - case OpCode.GreaterThen: this.compair((x, y) => x > y); break - case OpCode.GreaterThenEquals: this.compair((x, y) => x >= y); break - - case OpCode.BitAnd: this.operation((x, y) => x & y); break - case OpCode.BitOr: this.operation((x, y) => x | y); break - case OpCode.BitXOr: this.operation((x, y) => x ^ y); break - case OpCode.BitShiftLeft: this.operation((x, y) => x << y); break - case OpCode.BitShiftRight: this.operation((x, y) => x >> y); break + case OpCode.Add: + this.operation((x, y) => x + y); + break + case OpCode.Subtract: + this.operation((x, y) => x - y); + break + case OpCode.Multiply: + this.operation((x, y) => x * y); + break + case OpCode.Divide: + this.operation((x, y) => x / y); + break + case OpCode.FloorDivide: + this.operation((x, y) => Math.floor(x / y)); + break + case OpCode.Modulo: + this.operation((x, y) => x % y); + break + case OpCode.Exponent: + this.operation((x, y) => Math.pow(x, y)); + break + + case OpCode.LessThan: + this.compare((x, y) => x < y); + break + case OpCode.LessThanEquals: + this.compare((x, y) => x <= y); + break + case OpCode.GreaterThan: + this.compare((x, y) => x > y); + break + case OpCode.GreaterThanEquals: + this.compare((x, y) => x >= y); + break + + case OpCode.BitAnd: + this.operation((x, y) => x & y); + break + case OpCode.BitOr: + this.operation((x, y) => x | y); + break + case OpCode.BitXOr: + this.operation((x, y) => x ^ y); + break + case OpCode.BitShiftLeft: + this.operation((x, y) => x << y); + break + case OpCode.BitShiftRight: + this.operation((x, y) => x >> y); + break case OpCode.Concat: { - const x = std.variable_to_string(this.stack.pop() ?? nil) - const y = std.variable_to_string(this.stack.pop() ?? nil) - this.stack.push(make_string(x + y)) + const x = this.stack_pop(); + const y = this.stack_pop(); + + const result = std.variable_to_string(x) + std.variable_to_string(y); + + this.stack.push(make_string(result)) break } case OpCode.Equals: { - const [x, y] = [this.stack.pop(), this.stack.pop()] + const x = this.stack_pop(); + const y = this.stack_pop(); this.stack.push(make_boolean(equals(x, y))) break } case OpCode.NotEquals: { - const [x, y] = [this.stack.pop(), this.stack.pop()] + const x = this.stack_pop(); + const y = this.stack_pop(); this.stack.push(make_boolean(!equals(x, y))) break } case OpCode.And: { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(is_true(x) && is_true(y))) + const x = this.stack_pop(); + const y = this.stack_pop(); + + const result = is_true(x) ? y : x; + this.stack.push(result) break } case OpCode.Or: { - const [x, y] = [this.stack.pop(), this.stack.pop()] - this.stack.push(make_boolean(is_true(x) || is_true(y))) + const x = this.stack_pop(); + const y = this.stack_pop(); + + const result = is_true(x) ? x : y; + this.stack.push(result) break } - case OpCode.Not: this.stack.push(make_boolean(!is_true(this.stack.pop()))); break - case OpCode.BitNot: this.stack.push(make_number(~(this.stack.pop()?.number ?? 0))); break - case OpCode.Negate: this.stack.push(make_number(-(this.stack.pop()?.number ?? 0))); break - case OpCode.IsNotNil: this.stack.push(make_boolean((this.stack.pop() ?? nil) != nil)); break - case OpCode.Jump: this.ip += arg?.number ?? 0; break - case OpCode.JumpIfNot: if (!is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break - case OpCode.JumpIf: if (is_true(this.stack.pop())) { this.ip += arg?.number ?? 0 } break - case OpCode.MakeLocal: this.locals_stack.at(-1)?.set(arg?.string ?? '', nil); break - case OpCode.NewTable: this.stack.push({ data_type: DataType.Table, table: new Map() }); break - case OpCode.StartBlock: this.locals_stack.push(new Map()); break - case OpCode.EndBlock: this.locals_stack.pop(); break + case OpCode.Not: + this.stack.push(make_boolean(!is_true(this.stack_pop()))); + break + + case OpCode.BitNot: + this.stack.push(make_number(~(this.stack_pop_kind(VariableKind.Number).number))); + break + + case OpCode.Negate: + this.stack.push(make_number(-(this.stack_pop_kind(VariableKind.Number).number))); + break + + case OpCode.IsNotNil: + this.stack.push(make_boolean(!isNil(this.stack_pop()))); + break + + case OpCode.Jump: + if (isVariableKind(arg, VariableKind.Number)) + { + this.ip += arg.number; + } + break + + case OpCode.JumpIfNot: + if (isVariableKind(arg, VariableKind.Number) && !is_true(this.stack_pop())) + { + this.ip += arg.number + } + break + + case OpCode.JumpIf: + if (isVariableKind(arg, VariableKind.Number) && is_true(this.stack_pop())) + { + this.ip += arg.number + } + break + + case OpCode.MakeLocal: + const last_locals = this.locals_stack.at(-1) + + if (last_locals) + { + last_locals.set(isVariableKind(arg, VariableKind.String) ? arg.string : '', nil); + } + break + + case OpCode.NewTable: + this.stack.push(make_table()); + break + + case OpCode.StartBlock: + this.locals_stack.push(new Map()); + break + + case OpCode.EndBlock: + this.locals_stack.pop(); + break case OpCode.Length: { - const size = std.variable_size(this.stack.pop() ?? nil) - if (size == undefined) - this.stack.push(nil) - else - this.stack.push(make_number(size)) + const variable = this.stack_pop_maybe() + + switch (variable.data_type) + { + case VariableKind.String: + this.stack.push(make_number(variable.string.length)) + break + case VariableKind.Table: + this.stack.push(make_number(std.table_size(variable))) + break + default: + this.runtime_error(op, `Attempt to get length of a ${ variable.data_type } value`) + } break } @@ -464,27 +571,26 @@ export class Engine case OpCode.LoadIndex: { - const table = this.stack.pop() - if (table?.data_type == DataType.Nil) + const table = this.stack_pop_maybe() + + if (isNil(table)) { - this.stack.pop() // Pop index - this.stack.push(nil) - break + this.runtime_error(op, 'Attempt to index a nil value') } - if (table == undefined || table.table == undefined) - return this.runtime_error(op, 'Can only index on tables') + assertVariableKind(table, VariableKind.Table); - const i_var = this.stack.pop() - if (i_var?.data_type == DataType.Nil) + const i_var = this.stack_pop_maybe() + + if (isNil(i_var)) { - this.stack.push(nil) - break + this.runtime_error(op, 'Attempt to index with a nil value') } const i = index(i_var) - if (i == undefined) - return this.runtime_error(op, 'Invalid index, must be a number or string') + + if (i === undefined) + this.runtime_error(op, 'Invalid index, must be a number or string') this.stack.push(table.table.get(i) ?? nil) break @@ -492,17 +598,17 @@ export class Engine case OpCode.StoreIndex: { - const count = arg?.number ?? 1 - const table = this.stack_get(- count*2 - 1) + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 + const table = this.stack_get(- 1 - count * 2) - if (table.table == undefined) - return this.runtime_error(op, 'Can only index tables') + assertVariableKind(table, VariableKind.Table); - for (let i = 0; i < count; i++) + for (let i = 0; i < count; ++i) { - const key = index(this.stack.pop()) - const value = this.stack.pop() ?? nil - if (key == undefined) + const key = index(this.stack_pop_maybe()) + const value = this.stack_pop_maybe() + + if (key === undefined) return this.runtime_error(op, 'Invalid key, must be a number or string') table.table.set(key, value) @@ -513,13 +619,11 @@ export class Engine case OpCode.Store: { - const name = arg?.string ?? '' - const value = this.stack.pop() ?? nil - const local = [...this.locals_capture, ...this.locals_stack] - .reverse() - .find(x => x.has(name)) + const name = isVariableKind(arg, VariableKind.String) ? arg.string : '' + const value = this.stack_pop_maybe() + const local = [...this.locals_capture, ...this.locals_stack].findLast(x => x.has(name)) - if (local != undefined) + if (local !== undefined) local.set(name, value) else this.globals.set(name, value) @@ -528,7 +632,7 @@ export class Engine case OpCode.Push: { - if (this.locals_stack.length > 0 && arg?.data_type == DataType.Function) + if (this.locals_stack.length > 0 && isVariableKind(arg, VariableKind.Function)) arg.locals = [...this.locals_stack] this.stack.push(arg ?? nil) break @@ -536,11 +640,10 @@ export class Engine case OpCode.Load: { - const name = arg?.string ?? '' + const name = isVariableKind(arg, VariableKind.String) ? arg.string : '' const local = [...this.locals_capture, ...this.locals_stack] - .reverse() .map(x => x.get(name)) - .find(x => x != undefined && x.data_type != DataType.Nil) + .findLast(x => !isNil(x)) const global = this.globals.get(name) this.stack.push(local ?? global ?? nil) @@ -549,14 +652,16 @@ export class Engine case OpCode.Call: { - const count = this.stack.pop()?.number ?? 0 - const func_var = this.stack.pop() ?? nil + const x = this.stack_pop_maybe(); + const count = isVariableKind(x, VariableKind.Number) ? x.number : 0 + const func_var = this.stack_pop_maybe() + switch (func_var.data_type) { - case DataType.NativeFunction: + case VariableKind.NativeFunction: { const args = this.stack.splice(this.stack.length - count, count) - if (func_var.native_function != undefined) + if (func_var.native_function !== undefined) { const result = this.call_native_function(func_var.native_function, ...args) @@ -566,7 +671,7 @@ export class Engine this.stack.push(...result) } - if (this.error != undefined) + if (this.error !== undefined) { const error = this.error this.error = undefined @@ -575,7 +680,7 @@ export class Engine break } - case DataType.Function: + case VariableKind.Function: { this.stack.push(make_number(count)) this.call_stack.push(this.locals_stack.length, this.ip) @@ -587,9 +692,7 @@ export class Engine default: { - return this.runtime_error(op, - `Object of type '${ std.type_name(func_var.data_type) }' ` + - 'is not callable') + return this.runtime_error(op, `Object of type '${ func_var.data_type }' is not callable`) } } @@ -598,8 +701,9 @@ export class Engine case OpCode.ArgumentCount: { - const got = this.stack.pop()?.number ?? 0 - const expected = arg?.number ?? 0 + const x = this.stack_pop_maybe(); + const got = isVariableKind(x, VariableKind.Number) ? x.number : 0 + const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0 this.force_stack_height(expected, got) break } @@ -613,7 +717,7 @@ export class Engine case OpCode.EndStackChange: { const got = this.stack.length - (this.assign_height_stack.pop() ?? 0) - const expected = arg?.number ?? 0 + const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0 this.force_stack_height(expected, got) break } @@ -624,26 +728,27 @@ export class Engine step(options?: LuaOptions): Error | undefined { - if (this.error != undefined) + if (this.error !== undefined) return this.error if (this.ip >= this.program.length) return - const op = this.program[this.ip++] + const op = this.program[this.ip] + ++this.ip if (op === undefined) return this.runtime_error(op, 'Instruction pointer out of bounds') if (options?.trace || options?.trace_instructions) { - const arg = op.arg != undefined ? std.variable_to_string(op.arg) : '' + const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : '' console.log(this.ip - 1, op_code_name(op.code), arg) } const result = this.run_instruction(op) - if (result != undefined) + if (result !== undefined) return result if (options?.trace || options?.trace_stack) @@ -651,5 +756,4 @@ export class Engine return undefined } - } diff --git a/src/index.mts b/src/index.mts index 457107e..c9adc17 100644 --- a/src/index.mts +++ b/src/index.mts @@ -1,32 +1,40 @@ import { Engine } from './engine.mjs' -import { DataType, type Variable, type NativeFunction, nil } from './runtime.mjs' -import { make_boolean, make_number, make_string } from './runtime.mjs' +import { make_boolean, make_number, make_string, make_table, make_variable } from './runtime.mjs' import { compile } from './compiler.mjs' import { std_lib, variable_to_string } from './lib.mjs' +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" +import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs" +import type { Variable } from "./variable/definition/type/variable.type.mjs" +import { nil } from "./variable/nil.mjs" +import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs" export * as lexer from './lexer.mjs' export * as parser from './parser.mjs' export * as ast from './ast.mjs' export * as opcode from './opcode.mjs' export * as runtime from './runtime.mjs' +export * from './create-binding.mjs' -export const Nil = DataType.Nil -export const Boolean = DataType.Boolean -export const Number = DataType.Number -export const String = DataType.String -export const Function = DataType.Function -export const NativeFunctionType = DataType.NativeFunction -export const Table = DataType.Table +export const Nil = VariableKind.Nil +export const Boolean = VariableKind.Boolean +export const Number = VariableKind.Number +export const String = VariableKind.String +export const Function = VariableKind.Function +export const NativeFunctionType = VariableKind.NativeFunction +export const Table = VariableKind.Table export { std_lib as std_global, nil, - make_boolean as boolean, - make_number as number, - make_string as string, + make_boolean, + make_number, + make_string, + make_table, + make_variable, variable_to_string as to_string, Engine, - DataType, + VariableKind, Variable, + VariableUnwrapUtility, NativeFunction, compile, } diff --git a/src/lexer.mts b/src/lexer.mts index 3b2ce2b..778a49b 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -47,10 +47,10 @@ export enum TokenKind { Equals, NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, + LessThan, + LessThanEquals, + GreaterThan, + GreaterThanEquals, And, Or, Not, @@ -106,8 +106,8 @@ export function token_kind_to_string(kind: TokenKind): string | undefined case TokenKind.BitXOrNot: return '~' case TokenKind.BitShiftLeft: return '<<' case TokenKind.BitShiftRight: return '>>' - case TokenKind.LessThen: return '<' - case TokenKind.GreaterThen: return '>' + case TokenKind.LessThan: return '<' + case TokenKind.GreaterThan: return '>' case TokenKind.And: return 'and' case TokenKind.Or: return 'or' case TokenKind.Not: return 'not' @@ -164,8 +164,8 @@ const single_token_map: Map = new Map([ ['|', TokenKind.BitOr], ['~', TokenKind.BitXOrNot], - ['<', TokenKind.LessThen], - ['>', TokenKind.GreaterThen], + ['<', TokenKind.LessThan], + ['>', TokenKind.GreaterThan], ['=', TokenKind.Assign], [';', TokenKind.Semicolon], @@ -176,8 +176,8 @@ const single_token_map: Map = new Map([ const double_token_map: Map = new Map([ ['==', TokenKind.Equals], - ['<=', TokenKind.LessThenEquals], - ['>=', TokenKind.GreaterThenEquals], + ['<=', TokenKind.LessThanEquals], + ['>=', TokenKind.GreaterThanEquals], ['~=', TokenKind.NotEquals], ['..', TokenKind.Concat], ['//', TokenKind.FloorDivision], @@ -255,7 +255,7 @@ export class TokenStream return this.column += 1 - if (this.processing_stream.shift() == '\n') + if (this.processing_stream.shift() === '\n') { this.line += 1 this.column = 1 @@ -273,7 +273,7 @@ export class TokenStream private initial() { - if (this.processing_stream.length == 0) + if (this.processing_stream.length === 0) { this.peek_queue.push({ data: '', @@ -294,13 +294,13 @@ export class TokenStream if (this.processing_stream.length > 1) { const double = c + this.processing_stream[1] - if (double == '--') + if (double === '--') { this.state = State.Comment return } - if (double == '[[') + if (double === '[[') { this.state = State.MultiLineString this.start_token() @@ -310,7 +310,7 @@ export class TokenStream } const dobule_token_type = double_token_map.get(double) - if (dobule_token_type != undefined) + if (dobule_token_type !== undefined) { this.peek_queue.push({ data: double, @@ -327,7 +327,7 @@ export class TokenStream } const single_token_type = single_token_map.get(c) - if (single_token_type != undefined) + if (single_token_type !== undefined) { this.peek_queue.push({ data: c, @@ -341,7 +341,7 @@ export class TokenStream return } - if (c == '"') + if (c === '"') { this.start_token() this.consume() @@ -369,7 +369,7 @@ export class TokenStream const c = this.current() this.consume() - if (c == '"') + if (c === '"') { this.peek_queue.push({ data: this.buffer, @@ -381,7 +381,7 @@ export class TokenStream return } - if (c == '\\') + if (c === '\\') { this.state = State.StringLiteralEscape return @@ -413,7 +413,7 @@ export class TokenStream const c = this.current() ?? '\0' this.consume() - if (c + this.current() == ']]') + if (c + this.current() === ']]') { this.peek_queue.push({ data: this.buffer, @@ -459,7 +459,7 @@ export class TokenStream return } - if (c == '.') + if (c === '.') { this.buffer += c this.consume() @@ -467,7 +467,7 @@ export class TokenStream return } - if (c == 'e' || c == 'E') + if (c === 'e' || c === 'E') { this.buffer += c this.consume() @@ -475,9 +475,9 @@ export class TokenStream return } - if (c == 'x') + if (c === 'x') { - if (this.buffer != '0') + if (this.buffer !== '0') { this.peek_queue.push({ data: this.buffer, @@ -512,7 +512,7 @@ export class TokenStream return } - if (c == 'e' || c == 'E') + if (c === 'e' || c === 'E') { this.buffer += c this.state = State.NumberLiteralExpSign @@ -588,17 +588,17 @@ export class TokenStream const c = this.current() this.consume() - if (c == '\n') + if (c === '\n') this.state = State.Initial } private on_char() { - if (this.current() == undefined) + if (this.current() === undefined) { this.peek_queue.push({ data: '', - kind: this.state == State.Initial + kind: this.state === State.Initial ? TokenKind.EOF : TokenKind.NotFinished, debug: { @@ -656,7 +656,7 @@ export class TokenStream next(): Token { - if (this.peek_queue.length == 0) + if (this.peek_queue.length === 0) this.peek() return this.peek_queue.shift() as Token diff --git a/src/lib.mts b/src/lib.mts index a127967..091d95a 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -1,34 +1,64 @@ -import type { Variable } from './runtime.mjs' +import type { Variable } from './variable/definition/type/variable.type.mjs' +import type { VariableValueType } from './variable/definition/type/variable-value.type.mjs' +import { VariableKind } from './variable/definition/enum/variable-kind.enum.mjs' +import { isVariableKind } from './variable/predicate/is-variable-kind.mjs' +import { assertVariableKind } from './variable/predicate/assert-variable-kind.mjs' +import { nil } from './variable/nil.mjs' import { Engine } from './engine.mjs' -import { DataType, make_boolean, make_number, make_string, nil } from './runtime.mjs' +import { make_boolean, make_number, make_string, make_variable } from './runtime.mjs' +import { assertArray, assertPopulatedArray, isCallable, isInteger, unary, ValidationError } from "@vitruvius-labs/ts-predicate" +import { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs" +import { assertVariable } from "./variable/predicate/assert-variable.mjs" +import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs" +import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs" +import { isNil } from "./variable/predicate/is-nil.mjs" +import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs" +import type { NativeFunction } from "./index.mjs" +import type { TableMap } from "./boundary/definition/type/table-map.type.mjs" -let rand = xoroshiro(create_seed()) +function optional_parameter( + expected_kind: K, + parameter: Variable | undefined +): VariableValueType | undefined +{ + if (isNil(parameter)) + return undefined + + assertVariableKind(parameter, expected_kind); + + const value: unknown = VariableUnwrapUtility.unwrap(parameter); -export function variable_size(value: Variable): number | undefined + return value as VariableValueType; +} + +export function table_size(value: VariableTable): number { - switch (value.data_type) + let size: number = 0; + + for (let i: number = 1; i < value.table.size; ++i) { - case DataType.String: return value.string?.length ?? 0 - case DataType.Table: - return ([...value.table?.keys() ?? []] - .filter(key => typeof key == 'number') as number[]) - .reduce((acc, key) => Math.max(acc, key), 0) - default: - return undefined + if (!value.table.has(i)) + { + return size; + } + + ++size; } + + return size; } export function variable_to_string(variable: Variable, tables_done: Variable[] = []): string { switch (variable.data_type) { - case DataType.Nil: return 'nil' - case DataType.Boolean: return variable.boolean ? 'true' : 'false' - case DataType.Number: return variable.number?.toString() ?? '0' - case DataType.String: return variable.string ?? '' - case DataType.Function: return `` - case DataType.NativeFunction: return `` - case DataType.Table: + case VariableKind.Nil: return 'nil' + case VariableKind.Boolean: return variable.boolean ? 'true' : 'false' + case VariableKind.Number: return variable.number?.toString() ?? '0' + case VariableKind.String: return variable.string ?? '' + case VariableKind.Function: return `` + case VariableKind.NativeFunction: return `` + case VariableKind.Table: { if (tables_done.includes(variable)) return '...' @@ -43,169 +73,207 @@ export function variable_to_string(variable: Variable, tables_done: Variable[] = } } -export function type_name(data_type: DataType): string -{ - switch (data_type) - { - case DataType.Nil: return 'nil' - case DataType.Boolean: return 'boolean' - case DataType.Number: return 'number' - case DataType.String: return 'string' - case DataType.Function: return 'function' - case DataType.NativeFunction: return 'function' - case DataType.Table: return 'table' - } -} - function print(_: Engine, ...args: Variable[]): Variable[] { console.log(...args.map(arg => variable_to_string(arg))) return [nil] } -function type(_: Engine, v: Variable): Variable[] +function type(_: Engine, variable: Variable): Variable[] { - return [make_string(type_name(v.data_type))] + return [make_string(variable.data_type)] } +function inext(_: Engine, table: Variable, previous_index: Variable): Variable[] +{ + assertVariableKind(table, VariableKind.Table); + assertVariableKind(previous_index, VariableKind.Number); + + const table_map = table.table; + const next_index: number = previous_index.number + 1; + + const next_value: Variable | undefined = table_map.get(next_index); + + if (next_value === undefined) + { + return [nil]; + } + + return [make_number(next_index), next_value]; +} + +const inext_binding: VariableNativeFunction = { + data_type: VariableKind.NativeFunction, + native_function: inext +}; + function ipairs(_: Engine, table: Variable): Variable[] { - if (table.data_type != DataType.Table || table.table == undefined) - return [nil] + assertVariableKind(table, VariableKind.Table); - let index = 1 - return [{ - data_type: DataType.NativeFunction, - native_function: () => - { - const key = key_variable(index) - const value = table.table?.get(index++) - if (value == undefined) - return [nil] + return [ + inext_binding, + table, + make_number(0) + ] +} - return [key, value] - }, - }] +function compare_keys(next_key: unknown, index: Variable): boolean +{ + if (isVariableKind(index, VariableKind.Boolean)) + return next_key === index.boolean + + if (isVariableKind(index, VariableKind.Number)) + return next_key === index.number + + if (isVariableKind(index, VariableKind.String)) + return next_key === index.string + + if (isVariableKind(index, VariableKind.Table)) + return next_key === index.table + + if (isVariableKind(index, VariableKind.Function)) + return next_key === index.function_id + + if (isVariableKind(index, VariableKind.NativeFunction)) + return next_key === index.native_function + + return false } -function next(_: Engine, { table }: Variable, index?: Variable): Variable[] +function next(_: Engine, variable: Variable, start_index?: Variable): Variable[] { - if (table == undefined) - return [nil] + assertVariableKind(variable, VariableKind.Table); - const keys = table.keys() - if (index == undefined || index.data_type == DataType.Nil) + if (isNil(start_index)) { - const first_key: number | string | undefined = keys.next().value - if (first_key == undefined) + const first_key: unknown = variable.table.keys().next().value; + const first_value: Variable | undefined = variable.table.get(first_key); + + if (isNil(first_value)) return [nil] - return [key_variable(first_key), table.get(first_key) ?? nil] + return [make_variable(first_key), first_value] } + let previous_key_found: boolean = false; - let next_key: number | string | undefined = keys.next().value - while (next_key != undefined && next_key != index.number && next_key != index.string) - next_key = keys.next().value + for (const [key, value] of variable.table.entries()) + { + if (previous_key_found) + { + return [key_variable(key), value]; + } - next_key = keys.next().value - if (next_key == undefined) - return [nil] + if (compare_keys(key, start_index)) + { + previous_key_found = true; + break; + } + } - return [key_variable(next_key), table.get(next_key) ?? nil] + return [nil]; } function pairs(_: Engine, table: Variable): Variable[] { - const next_func = { data_type: DataType.NativeFunction, native_function: next } + const next_func: VariableNativeFunction = { + data_type: VariableKind.NativeFunction, + native_function: next + } + return [next_func, table, nil] } function range(_: Engine, count: Variable): Variable[] { + assertVariableKind(count, VariableKind.Number); + let index = 0 return [{ - data_type: DataType.NativeFunction, + data_type: VariableKind.NativeFunction, native_function: () => { index += 1 if (index >= (count.number ?? 0)) return [nil] - return [{ data_type: DataType.Number, number: index }] + return [{ data_type: VariableKind.Number, number: index }] }, }] } -function random(_: Engine): Variable[] -{ - return [{ data_type: DataType.Number, number: Math.random() }] -} - function is_empty(_: Engine, table: Variable): Variable[] { - const len = table.table?.size ?? 0 - return [{ data_type: DataType.Boolean, boolean: len == 0 }] + assertVariableKind(table, VariableKind.Table); + const empty: boolean = table.table.size === 0; + + return [make_boolean(empty)]; } -function key_variable(key: number | string): Variable +function key_variable(key: unknown): Variable { - if (typeof key == 'number') - return make_number(key) - else if (typeof key == 'string') - return make_string(key) - else - return nil + if (isCallable(key)) + { + return { + data_type: VariableKind.NativeFunction, + native_function: key, + } + } + + return make_variable(key); } -function sort(engine: Engine, table: Variable, by: Variable): Variable[] +function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] { - const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] + assertVariableKind(table, VariableKind.Table); + const entries: Array<[unknown, Variable]> = [...table.table.entries()] entries.sort(([_, a], [__, b]) => { const result = engine.call(by, a, b) if (result instanceof Error) return 0 - return result.at(0)?.number ?? 0 + const comparison = result.at(0) + assertVariableKind(comparison, VariableKind.Number); + return comparison.number }) const numbered_entries = entries.map(([key, _], i) => [i + 1, key_variable(key)] as const) - return [{ data_type: DataType.Table, table: new Map(numbered_entries) }] + return [{ data_type: VariableKind.Table, table: new Map(numbered_entries) }] } function find(engine: Engine, table: Variable, matches: Variable): Variable[] { - const entries: [string | number, Variable][] = [...table.table?.entries() ?? []] - const found = entries.find(([_, a]) => - { - const result = engine.call(matches, a) - if (result instanceof Error) - return 0 + assertVariableKind(table, VariableKind.Table); + const entries: Array<[unknown, Variable]> = [...table.table.entries()] + const found: [unknown, Variable] | undefined = entries.find( + ([_, a]) => + { + const result = engine.call(matches, a) + if (result instanceof Error) + return 0 - return result.at(0)?.boolean ?? 0 - }) + const matching = result.at(0) + assertVariableKind(matching, VariableKind.Boolean); + return matching.boolean + } + ) - if (found == undefined) + if (found === undefined) return [nil] - const [key, _] = found - if (typeof key == 'number') - return [make_number(key)] - else if (typeof key == 'string') - return [make_string(key)] - else - return [nil] + const [key] = found + + return [make_variable(key)]; } function first(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] - + assertVariableKind(table, VariableKind.Table); const key = table.table.keys().next().value - if (typeof key == 'string') + if (typeof key === 'string') return [make_string(key)] - else if (typeof key == 'number') + else if (typeof key === 'number') return [make_number(key)] else return [nil] @@ -213,32 +281,31 @@ function first(_: Engine, table: Variable): Variable[] function keys(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] - + assertVariableKind(table, VariableKind.Table); const keys = [...table.table.keys()] const entries = keys.map((key, i) => [i + 1, key_variable(key)] as const) - return [{ data_type: DataType.Table, table: new Map(entries) }] + return [{ data_type: VariableKind.Table, table: new Map(entries) }] } function values(_: Engine, table: Variable): Variable[] { - if (table.table == undefined) - return [nil] - + assertVariableKind(table, VariableKind.Table); const values = [...table.table.values()] const entries = values.map((value, i) => [i + 1, value] as const) - return [{ data_type: DataType.Table, table: new Map(entries) }] + return [{ data_type: VariableKind.Table, table: new Map(entries) }] } function to_number(_: Engine, arg: Variable): Variable[] { switch (arg.data_type) { - case DataType.Number: + case VariableKind.Number: return [arg] - case DataType.String: - return [make_number(parseFloat(arg.string ?? '0'))] + case VariableKind.String: + const text: string = arg.string.trim(); + if (!/^-?\d+(\.\d+)?$/.test(text)) + return [nil] + return [make_number(parseFloat(text))] default: return [nil] } @@ -251,15 +318,17 @@ function to_string(_: Engine, arg: Variable): Variable[] function assert(engine: Engine, condition: Variable, message?: Variable): Variable[] { - if (!(condition.boolean ?? false)) - engine.raise_error(message?.string ?? 'assertion failed!') + if (isVariableKind(condition, VariableKind.Nil) || isVariableKind(condition, VariableKind.Boolean) && !condition.boolean) { + error(engine, message ?? make_string('assertion failed!')) + } return [nil] } function error(engine: Engine, message: Variable): Variable[] { - engine.raise_error(message?.string ?? '') + assertVariableKind(message, VariableKind.String); + engine.raise_error(message.string) return [nil] } @@ -268,7 +337,7 @@ function warn(_: Engine, ...messages: Variable[]): Variable[] { const message = messages[0] - if (message !== undefined) + if (isVariableKind(message, VariableKind.String)) { switch (message.string) { @@ -283,12 +352,13 @@ function warn(_: Engine, ...messages: Variable[]): Variable[] if (warnings_on) console.error('WARNING', messages.map(x => variable_to_string(x))) + return [nil] } function select(_: Engine, index: Variable, ...args: Variable[]): Variable[] { - if (index.number != undefined) + if (isVariableKind(index, VariableKind.Number)) { if (index.number > 0) return args.slice(index.number - 1) @@ -296,7 +366,7 @@ function select(_: Engine, index: Variable, ...args: Variable[]): Variable[] return args.slice(args.length + index.number - 1) } - if (index.string == '#') + if (isVariableKind(index, VariableKind.String) && index.string === '#') return [make_number(args.length)] return [nil] @@ -304,94 +374,118 @@ function select(_: Engine, index: Variable, ...args: Variable[]): Variable[] function string_byte(_: Engine, s: Variable, i?: Variable, j?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + assertVariableKind(s, VariableKind.String); + + let start: number = 1; + + if (!isNil(i)) + { + assertVariableKind(i, VariableKind.Number); + start = i.number; + } + + let end: number = start; + + if (!isNil(j)) + { + assertVariableKind(j, VariableKind.Number); + end = j.number; + } - const start = i?.number ?? 1 - const end = j?.number ?? start const bytes: Variable[] = [] + for (let index = start - 1; index <= end - 1; index++) bytes.push(make_number(s.string.charCodeAt(index))) + return bytes } function string_char(_: Engine, ...chars: Variable[]): Variable[] { - const s = String.fromCharCode(...chars - .filter(x => x.number != undefined) - .map(c => c.number ?? 0)) + assertArray(chars, unary(assertVariableKind, VariableKind.Number)); + + const s = String.fromCharCode(...chars.map(c => c.number)) + return [make_string(s)] } +function string_format_helper(char: string, args_iterator: IterableIterator): string +{ + switch (char) + { + case 'd': + { + const arg = args_iterator.next().value + assertVariableKind(arg, VariableKind.Number); + return Math.floor(arg.number).toString() + } + case 'f': + { + const arg = args_iterator.next().value + assertVariableKind(arg, VariableKind.Number); + return arg.number.toString() + } + case 's': + { + const arg = args_iterator.next().value + assertVariable(arg); + return variable_to_string(arg) + } + case '%': + return '%' + default: + throw new Error(`Invalid format specifier: %${ char }`) + } +} + function string_format(_: Engine, format: Variable, ...args: Variable[]): Variable[] { - if (format.string == undefined) - return [nil] + assertVariableKind(format, VariableKind.String); + + const args_iterator = args[Symbol.iterator](); let result = '' let is_format = false - let arg_index = 0 for (const char of format.string) { if (is_format) { - switch (char) - { - case 'd': - { - const arg = args[arg_index] - ++arg_index - result += Math.floor(arg?.number ?? 0).toString() - break - } - case 'f': - { - const arg = args[arg_index] - ++arg_index - result += arg?.number?.toString() - break - } - case 's': - { - const arg = args[arg_index] - ++arg_index - - if (arg !== undefined) - result += variable_to_string(arg) - - break - } - default: - result += `%${ char }` - } + result += string_format_helper(char, args_iterator) is_format = false + continue; } - else + + if (char === '%') { - if (char == '%') - is_format = true - else - result += char + is_format = true + continue; } + + result += char } + if (is_format) + throw new Error(`Invalid format specifier: %`) + return [make_string(result)] } function string_find(_: Engine, s: Variable, pattern: Variable, init?: Variable, plain?: Variable): Variable[] { - if (s.string == undefined || pattern.string == undefined) - return [nil] + assertVariableKind(s, VariableKind.String); + assertVariableKind(pattern, VariableKind.String); - const offset = init?.number ?? 1 + const offset: number = optional_parameter(VariableKind.Number, init) ?? 1 const str = s.string.slice(offset - 1) - if (plain?.boolean == true) + const plain_param = optional_parameter(VariableKind.Boolean, plain) ?? false + + if (plain_param) return [make_number(str.indexOf(pattern.string) + 1)] const results = RegExp(pattern.string).exec(str) - if (results == null || results.length == 0) + if (results === null || results.length === 0) return [nil] const index = s.string.indexOf(results[0]) @@ -400,91 +494,105 @@ function string_find(_: Engine, s: Variable, pattern: Variable, init?: Variable, function string_len(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_number(s.string.length)] + assertVariableKind(s, VariableKind.String); + return [make_number(s.string.length)] } function string_lower(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_string(s.string.toLowerCase())] + assertVariableKind(s, VariableKind.String); + return [make_string(s.string.toLowerCase())] } function string_rep(_: Engine, s: Variable, n: Variable, sep?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + assertVariableKind(s, VariableKind.String); + assertVariableKind(n, VariableKind.Number); + + const separator: string = optional_parameter(VariableKind.String, sep) ?? '' - return [make_string(new Array(n.number ?? 0) - .fill(s.string) - .join(sep?.string ?? ''))] + return [make_string(new Array(n.number).fill(s.string).join(separator))] } function string_sub(_: Engine, s: Variable, i: Variable, j?: Variable): Variable[] { - if (s.string == undefined) - return [nil] + assertVariableKind(s, VariableKind.String); + assertVariableKind(i, VariableKind.Number); + + const end: number | undefined = optional_parameter(VariableKind.Number, j) const start = i.number ?? 1 - return [make_string(s.string.slice(start - 1, j?.number))] + return [make_string(s.string.slice(start - 1, end))] } function string_upper(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : make_string(s.string.toUpperCase())] + assertVariableKind(s, VariableKind.String); + return [make_string(s.string.toUpperCase())] } function string_reverse(_: Engine, s: Variable): Variable[] { - return [s.string == undefined ? nil : - make_string(s.string.split('').reverse().join(''))] + assertVariableKind(s, VariableKind.String); + return [make_string(s.string.split('').reverse().join(''))] } function table_concat(_: Engine, list: Variable, sep?: Variable, i?: Variable, j?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + assertVariableKind(list, VariableKind.Table); + + const separator = optional_parameter(VariableKind.String, sep) ?? '' + const start = optional_parameter(VariableKind.Number, i) ?? 1 + const end = optional_parameter(VariableKind.Number, j) - const seporator = sep?.string ?? '' - const start = i?.number ?? 1 const result = [...list.table.values()] - .slice(start - 1, j?.number) + .slice(start - 1, end) .map(item => variable_to_string(item)) - .join(seporator) + .join(separator) return [make_string(result)] } -function table_insert(_: Engine, list: Variable, arg_a?: Variable, arg_b?: Variable): Variable[] +function table_insert(_: Engine, list: Variable, index?: Variable, value?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + assertVariableKind(list, VariableKind.Table); - const size = variable_size(list) ?? 0 - let pos = size + 1 - let value = arg_a ?? nil - if (arg_b != undefined) + const size = table_size(list) + + if (isNil(value)) { - pos = arg_a?.number ?? pos - value = arg_b ?? nil + return table_insert(_, list, make_number(size + 1), index) } - for (let i = size + 1; i > pos; --i) + assertVariableKind(index, VariableKind.Number); + + const position: number = index.number + + for (let i = size + 1; i > position; --i) list.table.set(i, list.table.get(i - 1) ?? nil) - list.table.set(pos, value) + + list.table.set(position, value) + return [nil] } function table_move(_: Engine, a1: Variable, f: Variable, e: Variable, t: Variable, a2?: Variable): Variable[] { - a2 = a2 ?? a1 - if (a1.table == undefined || a2.table == undefined) - return [nil] + if (isNil(a2)) + { + return table_move(_, a1, f, e, t, a1) + } - if (f.number == undefined || e.number == undefined || t.number == undefined) - return [nil] + assertVariableKind(a1, VariableKind.Table); + assertVariableKind(f, VariableKind.Number); + assertVariableKind(e, VariableKind.Number); + assertVariableKind(t, VariableKind.Number); + assertVariableKind(a2, VariableKind.Table); const src_start = f.number const src_end = e.number const dest_start = t.number const count = src_end - src_start + for (let index = 0; index <= count; index++) a2.table.set(dest_start + index, a1.table.get(src_start + index) ?? nil) @@ -497,22 +605,22 @@ function table_pack(_: Engine, ...args: Variable[]): Variable[] .map((item, i) => [i + 1, item] as [number | string, Variable]) return [{ - data_type: DataType.Table, + data_type: VariableKind.Table, table: new Map([...elements, ['n', make_number(args.length)]]), }] } function table_remove(_: Engine, list: Variable, pos?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + assertVariableKind(list, VariableKind.Table); - const size = variable_size(list) ?? 0 - const remove_index = pos?.number ?? (size + 1) + const size = table_size(list) + const remove_index = optional_parameter(VariableKind.Number, pos) ?? (size + 1) const deleted_value = list.table.get(remove_index) ?? nil - for (let index = remove_index; index < size; index++) + for (let index = remove_index; index < size; ++index) list.table.set(index, list.table.get(index + 1) ?? nil) + list.table.delete(size) return [deleted_value] @@ -520,194 +628,170 @@ function table_remove(_: Engine, list: Variable, pos?: Variable): Variable[] function table_unpack(_: Engine, list: Variable, i?: Variable, j?: Variable): Variable[] { - if (list.table == undefined) - return [nil] + assertVariableKind(list, VariableKind.Table); - const size = variable_size(list) ?? 0 - const start = i?.number ?? 1 - const end = j?.number ?? size + const size = table_size(list) ?? 0 + const start = optional_parameter(VariableKind.Number, i) ?? 1 + const end = optional_parameter(VariableKind.Number, j) ?? size return [...list.table.values()].splice(start - 1, end) } function math_abs(_: Engine, x: Variable): Variable[] { - return [make_number(Math.abs(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.abs(x.number))] } function math_acos(_: Engine, x: Variable): Variable[] { - return [make_number(Math.acos(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.acos(x.number))] } function math_asin(_: Engine, x: Variable): Variable[] { - return [make_number(Math.asin(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.asin(x.number))] } function math_atan(_: Engine, x: Variable): Variable[] { - return [make_number(Math.atan(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.atan(x.number))] } function math_ceil(_: Engine, x: Variable): Variable[] { - return [make_number(Math.ceil(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.ceil(x.number))] } function math_cos(_: Engine, x: Variable): Variable[] { - return [make_number(Math.cos(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.cos(x.number))] } function math_deg(_: Engine, x: Variable): Variable[] { - return [make_number((x.number ?? 0.0) * (180.0/Math.PI))] + assertVariableKind(x, VariableKind.Number); + return [make_number(x.number * (180 / Math.PI))] } function math_exp(_: Engine, x: Variable): Variable[] { - return [make_number(Math.exp(x.number ?? 0.0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.exp(x.number))] } function math_floor(_: Engine, x: Variable): Variable[] { - return [make_number(Math.floor(x.number ?? 0.0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.floor(x.number))] } function math_fmod(_: Engine, x: Variable, y: Variable): Variable[] { - return [make_number((x.number ?? 0) % (y.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + assertVariableKind(y, VariableKind.Number); + return [make_number(x.number % y.number)] } function math_log(_: Engine, x: Variable, base: Variable): Variable[] { - return [make_number(Math.log(x.number ?? 0) / Math.log(base.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + assertVariableKind(base, VariableKind.Number); + return [make_number(Math.log(x.number) / Math.log(base.number))] } function math_max(_: Engine, ...args: Variable[]): Variable[] { - const max = args.reduce((acc, x) => - x.number == undefined ? acc : Math.max(acc, x.number), -Infinity) + assertPopulatedArray(args, unary(assertVariableKind, VariableKind.Number)); + const max = args.reduce((acc, x) => Math.max(acc, x.number), Number.NEGATIVE_INFINITY) return [make_number(max)] } function math_min(_: Engine, ...args: Variable[]): Variable[] { - const min = args.reduce((acc, x) => - x.number == undefined ? acc : Math.min(acc, x.number), Infinity) + assertPopulatedArray(args, unary(assertVariableKind, VariableKind.Number)); + const min = args.reduce((acc, x) => Math.min(acc, x.number), Number.POSITIVE_INFINITY) return [make_number(min)] } function math_modf(_: Engine, x: Variable): Variable[] { - const n = x.number ?? 0 + assertVariableKind(x, VariableKind.Number); + const n = x.number const integral = Math.trunc(n) return [make_number(integral), make_number(n - integral)] } function math_rad(_: Engine, x: Variable): Variable[] { - return [make_number((x.number ?? 0.0) * (Math.PI/180.0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(x.number * Math.PI / 180)] } +/** + * no args: [0;1) + * 1 arg : [1; m] + * 2 args: [m; n] +*/ function math_random(_: Engine, m?: Variable, n?: Variable): Variable[] { - if (m == undefined && n == undefined) - return [make_number(rand() / 0xFFFFFFFF)] + if (isNil(m)) + return [make_number(Math.random())] - if (n == undefined) - { - const max = m?.number ?? 0 - if (max == 0) - return [make_number(rand())] - - return [make_number(Math.floor(rand() % max) + 1)] - } + if (isNil(n)) + return math_random(_, make_number(1), m) - const min = m?.number ?? 1 - const max = (n?.number ?? 1) - min - return [make_number(Math.floor(rand() % max) + min)] -} + assertVariableKind(m, VariableKind.Number); + assertVariableKind(n, VariableKind.Number); -function create_seed(): [bigint, bigint, bigint, bigint] -{ - const random_bigint = (): bigint => - BigInt(Math.floor(Math.random() * 100)) + const min = m.number + const max = n.number - const seed: [bigint, bigint, bigint, bigint] = [ - random_bigint(), - random_bigint(), - random_bigint(), - random_bigint(), - ] + if (max === 0) + throw new ValidationError('math.random: upper bound must be greater than 0.'); - return seed -} + if (min > max) + throw new ValidationError('math.random: upper bound must be greater than or equal to lower bound.'); -function xoroshiro(seed: [bigint, bigint, bigint, bigint]): () => number -{ - const rotl = (x: bigint, k: bigint) => - BigInt.asUintN(64, (x << k) | (x >> (BigInt(64) - k))) + const result: number = Math.floor(Math.random() * (max + 1 - min) + min) - return () => - { - const result = rotl(BigInt.asUintN(64, seed[0] + seed[3]), BigInt(23)) + seed[0] - const t = BigInt.asUintN(64, seed[1] << BigInt(17)) - seed[2] ^= seed[0] - seed[3] ^= seed[1] - seed[1] ^= seed[2] - seed[0] ^= seed[3] - seed[2] ^= t - seed[3] = rotl(seed[3], BigInt(45)) - - return Number(BigInt.asUintN(64, result) & BigInt(0xFFFFFFFF)) - } -} - -function math_randomseed(_: Engine, x?: Variable, y?: Variable): Variable[] -{ - const seed = create_seed() - - if (x?.number != undefined) - { - seed[0] = BigInt(x.number & 0xFFFFFFFF) - seed[1] = BigInt((x.number << 32) & 0xFFFFFFFF) - } - - if (y?.number != undefined) - { - seed[2] = BigInt(y.number & 0xFFFFFFFF) - seed[3] = BigInt((y.number << 32) & 0xFFFFFFFF) - } - - rand = xoroshiro(seed) - - return [nil] + return [make_number(result)] } function math_sin(_: Engine, x: Variable): Variable[] { - return [make_number(Math.sin(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.sin(x.number))] } function math_sqrt(_: Engine, x: Variable): Variable[] { - return [make_number(Math.sqrt(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.sqrt(x.number))] } function math_tan(_: Engine, x: Variable): Variable[] { - return [make_number(Math.tan(x.number ?? 0))] + assertVariableKind(x, VariableKind.Number); + return [make_number(Math.tan(x.number))] } function math_tointeger(_: Engine, x: Variable): Variable[] { + assertVariable(x) switch (x.data_type) { - case DataType.Number: - return [make_number(Math.floor(x.number ?? 0))] - case DataType.String: - return [make_number(Math.floor(parseFloat(x.string ?? '0')))] + case VariableKind.Number: + return [x] + case VariableKind.String: + const text: string = x.string.trim(); + if (!/^-?\d+$/.test(text)) + return [nil] + return [make_number(parseInt(text))] default: return [nil] } @@ -715,100 +799,147 @@ function math_tointeger(_: Engine, x: Variable): Variable[] function math_type(_: Engine, x: Variable): Variable[] { - if (x.number == undefined) + assertVariable(x) + + if (!isVariableKind(x, VariableKind.Number)) return [nil] - if (Math.floor(x.number) == x.number) + if (isInteger(x.number)) return [make_string('integer')] - else - return [make_string('float')] + + return [make_string('float')] } function math_ult(_: Engine, m: Variable, n: Variable): Variable[] { + assertVariableKind(m, VariableKind.Number); + assertVariableKind(n, VariableKind.Number); + const buffer = Buffer.from(new ArrayBuffer(8)) - buffer.writeInt32LE(m.number ?? 0) - buffer.writeInt32LE(n.number ?? 0, 4) + buffer.writeInt32LE(m.number) + buffer.writeInt32LE(n.number, 4) const m_unsigned = buffer.readUInt32LE() const n_unsigned = buffer.readUInt32LE(4) + return [make_boolean(m_unsigned < n_unsigned)] } -export function std_lib(): Map -{ - const global: Map = new Map([ - ['print', { data_type: DataType.NativeFunction, native_function: print }], - ['type', { data_type: DataType.NativeFunction, native_function: type }], - ['ipairs', { data_type: DataType.NativeFunction, native_function: ipairs }], - ['pairs', { data_type: DataType.NativeFunction, native_function: pairs }], - ['next', { data_type: DataType.NativeFunction, native_function: next }], - ['range', { data_type: DataType.NativeFunction, native_function: range }], - ['random', { data_type: DataType.NativeFunction, native_function: random }], - ['isempty', { data_type: DataType.NativeFunction, native_function: is_empty }], - ['sort', { data_type: DataType.NativeFunction, native_function: sort }], - ['find', { data_type: DataType.NativeFunction, native_function: find }], - ['first', { data_type: DataType.NativeFunction, native_function: first }], - ['keys', { data_type: DataType.NativeFunction, native_function: keys }], - ['values', { data_type: DataType.NativeFunction, native_function: values }], - ['tonumber', { data_type: DataType.NativeFunction, native_function: to_number }], - ['tostring', { data_type: DataType.NativeFunction, native_function: to_string }], - ['assert', { data_type: DataType.NativeFunction, native_function: assert }], - ['error', { data_type: DataType.NativeFunction, native_function: error }], - ['warn', { data_type: DataType.NativeFunction, native_function: warn }], - ['select', { data_type: DataType.NativeFunction, native_function: select }], - ['string', { data_type: DataType.Table, table: new Map([ - ['byte', { data_type: DataType.NativeFunction, native_function: string_byte }], - ['char', { data_type: DataType.NativeFunction, native_function: string_char }], - ['format', { data_type: DataType.NativeFunction, native_function: string_format }], - ['find', { data_type: DataType.NativeFunction, native_function: string_find }], - ['len', { data_type: DataType.NativeFunction, native_function: string_len }], - ['lower', { data_type: DataType.NativeFunction, native_function: string_lower }], - ['rep', { data_type: DataType.NativeFunction, native_function: string_rep }], - ['reverse', { data_type: DataType.NativeFunction, native_function: string_reverse }], - ['sub', { data_type: DataType.NativeFunction, native_function: string_sub }], - ['upper', { data_type: DataType.NativeFunction, native_function: string_upper }], - ]) }], - ['table', { data_type: DataType.Table, table: new Map([ - ['concat', { data_type: DataType.NativeFunction, native_function: table_concat }], - ['insert', { data_type: DataType.NativeFunction, native_function: table_insert }], - ['move', { data_type: DataType.NativeFunction, native_function: table_move }], - ['pack', { data_type: DataType.NativeFunction, native_function: table_pack }], - ['remove', { data_type: DataType.NativeFunction, native_function: table_remove }], - ['unpack', { data_type: DataType.NativeFunction, native_function: table_unpack }], - ]) }], - ['math', { data_type: DataType.Table, table: new Map([ - ['abs', { data_type: DataType.NativeFunction, native_function: math_abs }], - ['acos', { data_type: DataType.NativeFunction, native_function: math_acos }], - ['asin', { data_type: DataType.NativeFunction, native_function: math_asin }], - ['atan', { data_type: DataType.NativeFunction, native_function: math_atan }], - ['ceil', { data_type: DataType.NativeFunction, native_function: math_ceil }], - ['cos', { data_type: DataType.NativeFunction, native_function: math_cos }], - ['deg', { data_type: DataType.NativeFunction, native_function: math_deg }], - ['exp', { data_type: DataType.NativeFunction, native_function: math_exp }], - ['floor', { data_type: DataType.NativeFunction, native_function: math_floor }], - ['fmod', { data_type: DataType.NativeFunction, native_function: math_fmod }], - ['log', { data_type: DataType.NativeFunction, native_function: math_log }], - ['max', { data_type: DataType.NativeFunction, native_function: math_max }], - ['min', { data_type: DataType.NativeFunction, native_function: math_min }], - ['modf', { data_type: DataType.NativeFunction, native_function: math_modf }], - ['rad', { data_type: DataType.NativeFunction, native_function: math_rad }], - ['random', { data_type: DataType.NativeFunction, native_function: math_random }], - ['randomseed', { data_type: DataType.NativeFunction, native_function: math_randomseed }], - ['sin', { data_type: DataType.NativeFunction, native_function: math_sin }], - ['sqrt', { data_type: DataType.NativeFunction, native_function: math_sqrt }], - ['tan', { data_type: DataType.NativeFunction, native_function: math_tan }], - ['tointeger', { data_type: DataType.NativeFunction, native_function: math_tointeger }], - ['type', { data_type: DataType.NativeFunction, native_function: math_type }], - ['ult', { data_type: DataType.NativeFunction, native_function: math_ult }], - - ['pi', { data_type: DataType.Number, number: Math.PI }], - ['maxinteger', { data_type: DataType.Number, number: 0xFFFFFFFF }], - ['mininteger', { data_type: DataType.Number, number: -(0xFFFFFFFF - 1) }], - ['huge', { data_type: DataType.Number, number: Infinity }], - ]) }], - ]) - - global.set('_G', { data_type: DataType.Table, table: global }) - return global +function fwrap(fn: NativeFunction): VariableNativeFunction +{ + return { + data_type: VariableKind.NativeFunction, + native_function: fn, + }; +} + +function twrap(content: Array<[unknown, Variable]>): VariableTable +{ + return { + data_type: VariableKind.Table, + table: new Map(content), + }; +} + +export function std_lib(): TableMap +{ + const string_mapping: VariableTable = twrap([ + ['byte', fwrap(string_byte)], + ['char', fwrap(string_char)], + // dump + ['format', fwrap(string_format)], + ['find', fwrap(string_find)], + // gfind + // gsub + ['len', fwrap(string_len)], + ['lower', fwrap(string_lower)], + ['rep', fwrap(string_rep)], + ['reverse', fwrap(string_reverse)], + ['sub', fwrap(string_sub)], + ['upper', fwrap(string_upper)], + ]); + + const table_mapping: VariableTable = twrap([ + ['concat', { data_type: VariableKind.NativeFunction, native_function: table_concat }], + // foreach + // foreachi + // getn + ['insert', fwrap(table_insert)], + ['move', fwrap(table_move)], + ['pack', fwrap(table_pack)], + ['remove', fwrap(table_remove)], + // setn + ['sort', fwrap(table_sort)], + ['unpack', fwrap(table_unpack)], + ]); + + const math_mapping: VariableTable = twrap([ + ['abs', fwrap(math_abs)], + ['acos', fwrap(math_acos)], + ['asin', fwrap(math_asin)], + ['atan', fwrap(math_atan)], + // atan2 + ['ceil', fwrap(math_ceil)], + ['cos', fwrap(math_cos)], + ['deg', fwrap(math_deg)], + ['exp', fwrap(math_exp)], + ['floor', fwrap(math_floor)], + ['fmod', fwrap(math_fmod)], + ['log', fwrap(math_log)], + // log10 + ['max', fwrap(math_max)], + ['min', fwrap(math_min)], + // mod + ['modf', fwrap(math_modf)], + // pow + ['rad', fwrap(math_rad)], + ['random', fwrap(math_random)], + ['sin', fwrap(math_sin)], + ['sqrt', fwrap(math_sqrt)], + ['tan', fwrap(math_tan)], + // frexp + // ldexp + ['tointeger', fwrap(math_tointeger)], + ['type', fwrap(math_type)], + ['ult', fwrap(math_ult)], + // Constants + ['pi', make_number(Math.PI)], + ['maxinteger', make_number(Number.MAX_SAFE_INTEGER)], + ['mininteger', make_number(Number.MIN_SAFE_INTEGER)], + ['huge', make_number(Number.POSITIVE_INFINITY)], + ]); + + const global: VariableTable = twrap([ + ['assert', fwrap(assert)], + ['error', fwrap(error)], + ['find', fwrap(find)], + ['first', fwrap(first)], + // getmetatable + ['ipairs', fwrap(ipairs)], + ['isempty', fwrap(is_empty)], + ['keys', fwrap(keys)], + ['math', math_mapping], + ['next', fwrap(next)], + ['pairs', fwrap(pairs)], + // pcall + ['print', fwrap(print)], + ['range', fwrap(range)], + // rawequal + // rawget + // rawset + ['select', fwrap(select)], + // setmetatable + ['string', string_mapping], + ['table', table_mapping], + ['tonumber', fwrap(to_number)], + ['tostring', fwrap(to_string)], + ['type', fwrap(type)], + ['values', fwrap(values)], + ['warn', fwrap(warn)], + // xpcall + ]); + + global.table.set('_G', global) + + return global.table; } diff --git a/src/opcode.mts b/src/opcode.mts index a18a40b..37f8949 100644 --- a/src/opcode.mts +++ b/src/opcode.mts @@ -1,4 +1,4 @@ -import type { Variable } from './runtime.mjs' +import type { Variable } from './variable/definition/type/variable.type.mjs' import type { Debug } from './lexer.mjs' export enum OpCode { @@ -35,10 +35,10 @@ export enum OpCode { Equals, NotEquals, - LessThen, - LessThenEquals, - GreaterThen, - GreaterThenEquals, + LessThan, + LessThanEquals, + GreaterThan, + GreaterThanEquals, And, Or, @@ -94,10 +94,10 @@ export function op_code_name(op_code: OpCode): string case OpCode.BitShiftRight: return 'BitShiftRight' case OpCode.Equals: return 'Equals' case OpCode.NotEquals: return 'NotEquals' - case OpCode.LessThen: return 'LessThen' - case OpCode.LessThenEquals: return 'LessThenEquals' - case OpCode.GreaterThen: return 'GreaterThen' - case OpCode.GreaterThenEquals: return 'GreaterThenEquals' + case OpCode.LessThan: return 'LessThan' + case OpCode.LessThanEquals: return 'LessThanEquals' + case OpCode.GreaterThan: return 'GreaterThan' + case OpCode.GreaterThanEquals: return 'GreaterThanEquals' case OpCode.And: return 'And' case OpCode.Or: return 'Or' case OpCode.Not: return 'Not' diff --git a/src/optimizer.mts b/src/optimizer.mts index 874dc6f..7a31967 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -10,7 +10,7 @@ const CONSTANT_VALUES = [ ValueKind.StringLiteral, ] -function compule_arithmetic_operation( +function compute_arithmetic_operation( expression: Expression, operation: (a: number, b: number) => number, constants: Map @@ -18,7 +18,7 @@ function compule_arithmetic_operation( { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) + if (lhs === undefined || rhs === undefined) return undefined return { @@ -28,7 +28,7 @@ function compule_arithmetic_operation( } } -function compule_comparison_operation( +function compute_comparison_operation( expression: Expression, operation: (a: number, b: number) => boolean, constants: Map @@ -36,7 +36,8 @@ function compule_comparison_operation( { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) + + if (lhs === undefined || rhs === undefined) return undefined return { @@ -46,22 +47,26 @@ function compule_comparison_operation( } } -function compule_logical_operation( +function compute_logical_operation( expression: Expression, - operation: (a: boolean, b: boolean) => boolean, + operation: ExpressionKind.And | ExpressionKind.Or, constants: Map ): Value | undefined { const lhs = compute_constant_expression(expression.lhs, constants) const rhs = compute_constant_expression(expression.rhs, constants) - if (lhs == undefined || rhs == undefined) + + if (lhs === undefined || rhs === undefined) return undefined - return { - kind: ValueKind.BooleanLiteral, - boolean: operation((lhs?.boolean ?? false), (rhs?.boolean ?? false)), - token: expression.token, + const lhs_falsy: boolean = lhs.kind === ValueKind.NilLiteral || lhs.kind === ValueKind.BooleanLiteral && lhs.boolean === false; + + if (operation === ExpressionKind.And) + { + return lhs_falsy ? lhs : rhs; } + + return lhs_falsy ? rhs : lhs; } function compute_constant_expression( @@ -69,53 +74,75 @@ function compute_constant_expression( constants: Map ): Value | undefined { - if (expression == undefined) + if (expression === undefined) return undefined switch (expression.kind) { case ExpressionKind.Value: { - if (expression.value == undefined) + if (expression.value === undefined) return undefined const value = expression.value if (CONSTANT_VALUES.includes(value.kind)) return value - if (value.kind == ValueKind.Variable && constants.has(value.identifier ?? '')) + if (value.kind === ValueKind.Variable && constants.has(value.identifier ?? '')) return constants.get(value.identifier ?? '') return undefined } - case ExpressionKind.Addition: return compule_arithmetic_operation(expression, (a, b) => a + b, constants) - case ExpressionKind.Subtract: return compule_arithmetic_operation(expression, (a, b) => a - b, constants) - case ExpressionKind.Multiplication: return compule_arithmetic_operation(expression, (a, b) => a * b, constants) - case ExpressionKind.Division: return compule_arithmetic_operation(expression, (a, b) => a / b, constants) - case ExpressionKind.FloorDivision: return compule_arithmetic_operation(expression, (a, b) => Math.floor(a / b), constants) - case ExpressionKind.Modulo: return compule_arithmetic_operation(expression, (a, b) => a % b, constants) - case ExpressionKind.Exponent: return compule_arithmetic_operation(expression, (a, b) => Math.pow(a, b), constants) - case ExpressionKind.Concat: return undefined - - case ExpressionKind.BitAnd: return compule_arithmetic_operation(expression, (a, b) => a & b, constants) - case ExpressionKind.BitOr: return compule_arithmetic_operation(expression, (a, b) => a | b, constants) - case ExpressionKind.BitXOr: return compule_arithmetic_operation(expression, (a, b) => a ^ b, constants) - case ExpressionKind.BitShiftLeft: return compule_arithmetic_operation(expression, (a, b) => a << b, constants) - case ExpressionKind.BitShiftRight: return compule_arithmetic_operation(expression, (a, b) => a >> b, constants) - case ExpressionKind.BitNot: return undefined - - case ExpressionKind.Equals: return compule_comparison_operation(expression, (a, b) => a == b, constants) - case ExpressionKind.NotEquals: return compule_comparison_operation(expression, (a, b) => a != b, constants) - case ExpressionKind.LessThen: return compule_comparison_operation(expression, (a, b) => a < b, constants) - case ExpressionKind.LessThenEquals: return compule_comparison_operation(expression, (a, b) => a <= b, constants) - case ExpressionKind.GreaterThen: return compule_comparison_operation(expression, (a, b) => a > b, constants) - case ExpressionKind.GreaterThenEquals: return compule_comparison_operation(expression, (a, b) => a >= b, constants) - case ExpressionKind.And: return compule_logical_operation(expression, (a, b) => a && b, constants) - case ExpressionKind.Or: return compule_logical_operation(expression, (a, b) => a || b, constants) + case ExpressionKind.Addition: + return compute_arithmetic_operation(expression, (a, b) => a + b, constants) + case ExpressionKind.Subtract: + return compute_arithmetic_operation(expression, (a, b) => a - b, constants) + case ExpressionKind.Multiplication: + return compute_arithmetic_operation(expression, (a, b) => a * b, constants) + case ExpressionKind.Division: + return compute_arithmetic_operation(expression, (a, b) => a / b, constants) + case ExpressionKind.FloorDivision: + return compute_arithmetic_operation(expression, (a, b) => Math.floor(a / b), constants) + case ExpressionKind.Modulo: + return compute_arithmetic_operation(expression, (a, b) => a % b, constants) + case ExpressionKind.Exponent: + return compute_arithmetic_operation(expression, (a, b) => Math.pow(a, b), constants) + case ExpressionKind.Concat: + return undefined + + case ExpressionKind.BitAnd: + return compute_arithmetic_operation(expression, (a, b) => a & b, constants) + case ExpressionKind.BitOr: + return compute_arithmetic_operation(expression, (a, b) => a | b, constants) + case ExpressionKind.BitXOr: + return compute_arithmetic_operation(expression, (a, b) => a ^ b, constants) + case ExpressionKind.BitShiftLeft: + return compute_arithmetic_operation(expression, (a, b) => a << b, constants) + case ExpressionKind.BitShiftRight: + return compute_arithmetic_operation(expression, (a, b) => a >> b, constants) + case ExpressionKind.BitNot: + return undefined + + case ExpressionKind.Equals: + return compute_comparison_operation(expression, (a, b) => a === b, constants) + case ExpressionKind.NotEquals: + return compute_comparison_operation(expression, (a, b) => a !== b, constants) + case ExpressionKind.LessThan: + return compute_comparison_operation(expression, (a, b) => a < b, constants) + case ExpressionKind.LessThanEquals: + return compute_comparison_operation(expression, (a, b) => a <= b, constants) + case ExpressionKind.GreaterThan: + return compute_comparison_operation(expression, (a, b) => a > b, constants) + case ExpressionKind.GreaterThanEquals: + return compute_comparison_operation(expression, (a, b) => a >= b, constants) + case ExpressionKind.And: + return compute_logical_operation(expression, ExpressionKind.And, constants) + case ExpressionKind.Or: + return compute_logical_operation(expression, ExpressionKind.Or, constants) case ExpressionKind.Not: { const lhs = compute_constant_expression(expression.lhs, constants) - if (lhs == undefined) + if (lhs === undefined) return undefined return { @@ -128,7 +155,7 @@ function compute_constant_expression( case ExpressionKind.Negate: { const lhs = compute_constant_expression(expression.lhs, constants) - if (lhs == undefined) + if (lhs === undefined) return undefined return { @@ -151,17 +178,17 @@ function optimize_expression( constants: Map ): void { - if (expression == undefined) + if (expression === undefined) return - if (expression.value?.function != undefined) + if (expression.value?.function !== undefined) { optimize_chunk(expression.value.function.body, constants) return } const value = compute_constant_expression(expression, constants) - if (value != undefined) + if (value !== undefined) { expression.kind = ExpressionKind.Value expression.value = value @@ -189,16 +216,16 @@ function mark_local_constants(assignment: Assignment, constants: Map ): void { - if (assignment == undefined) + if (assignment === undefined) return if (assignment.local) @@ -243,14 +270,14 @@ function remove_constant_local_assignments( { for (const statement of chunk.statements) { - if (statement.assignment == undefined || !statement.assignment.local) + if (statement.assignment === undefined || !statement.assignment.local) continue const assignment = statement.assignment for (const name of constants.keys()) { const index = assignment.lhs.findIndex( - x => x.value?.identifier == name) + x => x.value?.identifier === name) if (index < 0) continue @@ -260,12 +287,12 @@ function remove_constant_local_assignments( } chunk.statements = chunk.statements - .filter(x => x.assignment == undefined || x.assignment.lhs.length > 0) + .filter(x => x.assignment === undefined || x.assignment.lhs.length > 0) } function optimize_if(if_block: IfBlock | undefined, constants: Map): void { - if (if_block == undefined) + if (if_block === undefined) return optimize_expression(if_block.condition, constants) @@ -274,7 +301,7 @@ function optimize_if(if_block: IfBlock | undefined, constants: Map): void { - if (while_block == undefined) + if (while_block === undefined) return optimize_expression(while_block.condition, constants) @@ -283,7 +310,7 @@ function optimize_while(while_block: While | undefined, constants: Map): void { - if (for_block == undefined) + if (for_block === undefined) return optimize_expression(for_block.iterator, constants) @@ -292,7 +319,7 @@ function optimize_for(for_block: For | undefined, constants: Map) function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map): void { - if (numeric_for_block == undefined) + if (numeric_for_block === undefined) return optimize_expression(numeric_for_block.start, constants) @@ -303,7 +330,7 @@ function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constan function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void { - if (repeat_block == undefined) + if (repeat_block === undefined) return optimize_expression(repeat_block.condition, constants) @@ -346,7 +373,7 @@ export function optimize_chunk(chunk: Chunk, parent_constants?: Map = new Map() let current_numeric_key = 1 - while (stream.peek().kind != TokenKind.SquiglyClose) + while (stream.peek().kind !== TokenKind.SquiglyClose) { const element = parse_table_key(stream) if (element instanceof Error) @@ -164,7 +164,7 @@ function parse_value(stream: TokenStream): Value | Error case TokenKind.NumberLiteral: return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } case TokenKind.BooleanLiteral: - return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data == 'true' } + return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data === 'true' } case TokenKind.StringLiteral: return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } case TokenKind.NilLiteral: @@ -246,7 +246,7 @@ function parse_call(func: Expression, stream: TokenStream): Expression | Error { const open_brace = stream.next() const args: Expression[] = [] - while (stream.peek().kind != TokenKind.CloseBrace) + while (stream.peek().kind !== TokenKind.CloseBrace) { const argument = parse_expression(stream) if (argument instanceof Error) @@ -364,10 +364,10 @@ function operation_type_to_expression_kind( case TokenKind.BitXOrNot: return ExpressionKind.BitXOr case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight - case TokenKind.LessThen: return ExpressionKind.LessThen - case TokenKind.LessThenEquals: return ExpressionKind.LessThenEquals - case TokenKind.GreaterThen: return ExpressionKind.GreaterThen - case TokenKind.GreaterThenEquals: return ExpressionKind.GreaterThenEquals + case TokenKind.LessThan: return ExpressionKind.LessThan + case TokenKind.LessThanEquals: return ExpressionKind.LessThanEquals + case TokenKind.GreaterThan: return ExpressionKind.GreaterThan + case TokenKind.GreaterThanEquals: return ExpressionKind.GreaterThanEquals case TokenKind.Equals: return ExpressionKind.Equals case TokenKind.NotEquals: return ExpressionKind.NotEquals case TokenKind.And: return ExpressionKind.And @@ -418,7 +418,7 @@ function parse_operation( function parse_expression(stream: TokenStream): Expression | Error { - if (stream.peek().kind == TokenKind.BitXOrNot) + if (stream.peek().kind === TokenKind.BitXOrNot) return parse_unary_operator(stream) return parse_operation(stream, 0) @@ -430,7 +430,7 @@ function parse_local_statement(local: Token, values: Expression[]): Statement | for (const expression of values) { const value = expression.value - if (value == undefined || value.kind != ValueKind.Variable) + if (value === undefined || value.kind !== ValueKind.Variable) return error(expression.token, 'Invalid local name') names.push(value.token) } @@ -448,7 +448,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error { const local = expect(stream, TokenKind.Local) const lhs: Expression[] = [] - while (lhs.length == 0 || consume(stream, TokenKind.Comma)) + while (lhs.length === 0 || consume(stream, TokenKind.Comma)) { const lvalue = parse_expression(stream) if (lvalue instanceof Error) @@ -467,7 +467,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error } const rhs: Expression[] = [] - while (rhs.length == 0 || consume(stream, TokenKind.Comma)) + while (rhs.length === 0 || consume(stream, TokenKind.Comma)) { const rvalue = parse_expression(stream) if (rvalue instanceof Error) @@ -493,7 +493,7 @@ function parse_return(stream: TokenStream): Statement | Error return ret const values: Expression[] = [] - while (values.length == 0 || consume(stream, TokenKind.Comma)) + while (values.length === 0 || consume(stream, TokenKind.Comma)) { const value = parse_expression(stream) if (value instanceof Error) @@ -506,7 +506,7 @@ function parse_return(stream: TokenStream): Statement | Error values.push(value) } - if (values.length == 0) + if (values.length === 0) { values.push({ kind: ExpressionKind.Value, @@ -684,7 +684,7 @@ function parse_for(stream: TokenStream): Statement | Error return for_token const items: Token[] = [] - while (items.length == 0 || consume(stream, TokenKind.Comma)) + while (items.length === 0 || consume(stream, TokenKind.Comma)) { const item = expect(stream, TokenKind.Identifier) if (item instanceof Error) @@ -789,7 +789,7 @@ function parse_function_params(stream: TokenStream): Token[] | Error return open_brace const params: Token[] = [] - while (stream.peek().kind != TokenKind.CloseBrace) + while (stream.peek().kind !== TokenKind.CloseBrace) { const param = expect(stream, TokenKind.Identifier) if (param instanceof Error) @@ -963,13 +963,13 @@ function parse_statement(stream: TokenStream, end_tokens: TokenKind[]): Statemen export function parse(stream: TokenStream, ...end_tokens: TokenKind[]): Chunk | Error { const chunk: Chunk = { statements: [] } - if (end_tokens.length == 0) + if (end_tokens.length === 0) end_tokens.push(TokenKind.EOF) while (true) { const statement = parse_statement(stream, end_tokens) - if (statement == undefined) + if (statement === undefined) break if (statement instanceof Error) return statement diff --git a/src/runtime-error.mts b/src/runtime-error.mts new file mode 100644 index 0000000..42c71a3 --- /dev/null +++ b/src/runtime-error.mts @@ -0,0 +1,38 @@ +import type { Debug } from "./lexer.mjs"; + +class RuntimeError extends Error +{ + protected readonly line: number | undefined = undefined; + protected readonly column: number | undefined = undefined; + + public constructor(message: string, options?: ErrorOptions, debug?: Debug) + { + super(message, options); + + this.name = "RuntimeError"; + this.line = debug?.line; + this.column = debug?.column; + } + + public getLine(): number | undefined + { + return this.line; + } + + public getColumn(): number | undefined + { + return this.column; + } + + public getMessage(): string + { + if (this.line === undefined || this.column === undefined) + { + return this.message; + } + + return `${ this.line }:${ this.column }: ${ this.message }` + } +} + +export { RuntimeError }; diff --git a/src/runtime.mts b/src/runtime.mts index 2b06f0e..50f14c2 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -1,42 +1,130 @@ -import { Engine } from './engine.mjs' - -export enum DataType { - Nil, - Boolean, - Number, - String, - Function, - NativeFunction, - Table, -} - -export type NativeFunction = (engine: Engine, ...args: Array) => Array | Error +import { + isArray, + isBoolean, + isCallable, + isInstanceOf, + isNullish, + isNumber, + isString, +} from "@vitruvius-labs/ts-predicate"; -export interface Variable { - data_type: DataType, - boolean?: boolean, - number?: number, - string?: string, - native_function?: NativeFunction, - table?: Map, +import type { Variable } from "./variable/definition/type/variable.type.mjs"; +import type { VariableBoolean } from "./variable/definition/interface/variable-boolean.interface.mjs"; +import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; +import type { VariableString } from "./variable/definition/interface/variable-string.interface.mjs"; +import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; +import { isVariable } from "./variable/predicate/is-variable.mjs"; +import { nil } from "./variable/nil.mjs"; +import type { TableInputType } from "./boundary/definition/type/table-input.type.mjs"; +import { isTableInputType } from "./boundary/predicate/is-table-input-type.mjs"; +import { isTableKey } from "./boundary/predicate/is-table-key.mjs"; - function_id?: number, - locals?: Map[], +export function make_boolean(boolean: boolean): VariableBoolean +{ + return { data_type: VariableKind.Boolean, boolean: boolean } } -export const nil: Variable = { data_type: DataType.Nil } +export function make_number(number: number): VariableNumber +{ + return { data_type: VariableKind.Number, number: number } +} -export function make_boolean(boolean: boolean): Variable +export function make_string(string: string): VariableString { - return { data_type: DataType.Boolean, boolean: boolean } + return { data_type: VariableKind.String, string: string } } -export function make_number(number: number): Variable +export function make_table(input?: TableInputType): VariableTable { - return { data_type: DataType.Number, number: number } + const table_content: Map = new Map(); + + const table_variable: Variable = { + data_type: VariableKind.Table, + table: table_content, + }; + + if (input === undefined) + { + return table_variable; + } + + if (isArray(input)) + { + for (let i = 0; i < input.length; ++i) + { + table_content.set(i + 1, make_variable(input.at(i))); + } + + return table_variable; + } + + if (isInstanceOf(input, Map)) + { + for (const [key, value] of input.entries()) + { + if (isTableKey(key)) + { + table_content.set(key, make_variable(value)); + } + } + + return table_variable; + } + + for (const [key, value] of Object.entries(input)) + { + const variable: Variable = make_variable(value); + const numeric_key: number = Number(key); + + if (isNumber(numeric_key)) + { + table_content.set(numeric_key, variable); + continue; + } + + table_content.set(key, variable); + } + + return table_variable; } -export function make_string(string: string): Variable +export function make_variable(input: unknown): Variable { - return { data_type: DataType.String, string: string } + if (isVariable(input)) + { + return input; + } + + if (isNullish(input)) + { + return nil; + } + + if (isBoolean(input)) + { + return make_boolean(input); + } + + if (isNumber(input)) + { + return make_number(input); + } + + if (isString(input)) + { + return make_string(input); + } + + if (isTableInputType(input)) + { + return make_table(input); + } + + if (isCallable(input)) + { + throw new Error("Functions cannot be converted into a variable automatically, please use make_function instead"); + } + + throw new Error("Cannot be converted into a variable"); } diff --git a/src/variable/_index.mts b/src/variable/_index.mts new file mode 100644 index 0000000..b756c99 --- /dev/null +++ b/src/variable/_index.mts @@ -0,0 +1,5 @@ +export * from "./definition/_index.mjs"; +export * from "./predicate/_index.mjs"; +export * from "./nil.mjs"; +export * from "./equals.mjs"; +export * from "./unwrap-variable.mjs"; diff --git a/src/variable/definition/_index.mts b/src/variable/definition/_index.mts new file mode 100644 index 0000000..b0660d6 --- /dev/null +++ b/src/variable/definition/_index.mts @@ -0,0 +1,3 @@ +export type * from "./interface/_index.mjs"; +export type * from "./type/_index.mjs"; +export * from "./enum/_index.mjs"; diff --git a/src/variable/definition/enum/_index.mts b/src/variable/definition/enum/_index.mts new file mode 100644 index 0000000..df1d95e --- /dev/null +++ b/src/variable/definition/enum/_index.mts @@ -0,0 +1 @@ +export * from "./variable-kind.enum.mjs"; diff --git a/src/variable/definition/enum/variable-kind.enum.mts b/src/variable/definition/enum/variable-kind.enum.mts new file mode 100644 index 0000000..81117cd --- /dev/null +++ b/src/variable/definition/enum/variable-kind.enum.mts @@ -0,0 +1,12 @@ +const enum VariableKind +{ + Nil = "nil", + Boolean = "boolean", + Number = "number", + String = "string", + Table = "table", + Function = "function", + NativeFunction = "native-function", +} + +export { VariableKind }; diff --git a/src/variable/definition/interface/_index.mts b/src/variable/definition/interface/_index.mts new file mode 100644 index 0000000..a54a875 --- /dev/null +++ b/src/variable/definition/interface/_index.mts @@ -0,0 +1,8 @@ +export type * from "./base-variable.interface.mjs"; +export type * from "./variable-boolean.interface.mjs"; +export type * from "./variable-function.interface.mjs"; +export type * from "./variable-native-function.interface.mjs"; +export type * from "./variable-nil.interface.mjs"; +export type * from "./variable-number.interface.mjs"; +export type * from "./variable-string.interface.mjs"; +export type * from "./variable-table.interface.mjs"; diff --git a/src/variable/definition/interface/base-variable.interface.mts b/src/variable/definition/interface/base-variable.interface.mts new file mode 100644 index 0000000..8487ec9 --- /dev/null +++ b/src/variable/definition/interface/base-variable.interface.mts @@ -0,0 +1,8 @@ +import type { Variable } from "../type/variable.type.mjs"; + +interface BaseVariable +{ + locals?: Array>; +} + +export type { BaseVariable }; diff --git a/src/variable/definition/interface/variable-boolean.interface.mts b/src/variable/definition/interface/variable-boolean.interface.mts new file mode 100644 index 0000000..bd427bf --- /dev/null +++ b/src/variable/definition/interface/variable-boolean.interface.mts @@ -0,0 +1,10 @@ +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableBoolean extends BaseVariable +{ + data_type: VariableKind.Boolean; + boolean: boolean; +} + +export type { VariableBoolean }; diff --git a/src/variable/definition/interface/variable-function.interface.mts b/src/variable/definition/interface/variable-function.interface.mts new file mode 100644 index 0000000..14d5e66 --- /dev/null +++ b/src/variable/definition/interface/variable-function.interface.mts @@ -0,0 +1,10 @@ +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableFunction extends BaseVariable +{ + data_type: VariableKind.Function; + function_id?: number; +} + +export type { VariableFunction }; diff --git a/src/variable/definition/interface/variable-native-function.interface.mts b/src/variable/definition/interface/variable-native-function.interface.mts new file mode 100644 index 0000000..3420319 --- /dev/null +++ b/src/variable/definition/interface/variable-native-function.interface.mts @@ -0,0 +1,11 @@ +import type { NativeFunction } from "../../../boundary/definition/type/native-function.type.mjs"; +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableNativeFunction extends BaseVariable +{ + data_type: VariableKind.NativeFunction; + native_function: NativeFunction; +} + +export type { VariableNativeFunction }; diff --git a/src/variable/definition/interface/variable-nil.interface.mts b/src/variable/definition/interface/variable-nil.interface.mts new file mode 100644 index 0000000..23a40c2 --- /dev/null +++ b/src/variable/definition/interface/variable-nil.interface.mts @@ -0,0 +1,9 @@ +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableNil extends BaseVariable +{ + data_type: VariableKind.Nil; +} + +export type { VariableNil }; diff --git a/src/variable/definition/interface/variable-number.interface.mts b/src/variable/definition/interface/variable-number.interface.mts new file mode 100644 index 0000000..c9baf5e --- /dev/null +++ b/src/variable/definition/interface/variable-number.interface.mts @@ -0,0 +1,10 @@ +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableNumber extends BaseVariable +{ + data_type: VariableKind.Number; + number: number; +} + +export type { VariableNumber }; diff --git a/src/variable/definition/interface/variable-string.interface.mts b/src/variable/definition/interface/variable-string.interface.mts new file mode 100644 index 0000000..de0e344 --- /dev/null +++ b/src/variable/definition/interface/variable-string.interface.mts @@ -0,0 +1,10 @@ +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableString extends BaseVariable +{ + data_type: VariableKind.String; + string: string; +} + +export type { VariableString }; diff --git a/src/variable/definition/interface/variable-table.interface.mts b/src/variable/definition/interface/variable-table.interface.mts new file mode 100644 index 0000000..beddcc0 --- /dev/null +++ b/src/variable/definition/interface/variable-table.interface.mts @@ -0,0 +1,11 @@ +import type { TableMap } from "../../../boundary/definition/type/table-map.type.mjs"; +import type { BaseVariable } from "./base-variable.interface.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +interface VariableTable extends BaseVariable +{ + data_type: VariableKind.Table; + table: TableMap; +} + +export type { VariableTable }; diff --git a/src/variable/definition/type/_index.mts b/src/variable/definition/type/_index.mts new file mode 100644 index 0000000..abe3457 --- /dev/null +++ b/src/variable/definition/type/_index.mts @@ -0,0 +1,2 @@ +export type * from "./variable.type.mjs"; +export type * from "./variable-value.type.mjs"; diff --git a/src/variable/definition/type/variable-value.type.mts b/src/variable/definition/type/variable-value.type.mts new file mode 100644 index 0000000..ce40968 --- /dev/null +++ b/src/variable/definition/type/variable-value.type.mts @@ -0,0 +1,16 @@ +import type { NativeFunction } from "../../../boundary/definition/type/native-function.type.mjs"; +import type { TableMap } from "../../../boundary/definition/type/table-map.type.mjs"; +import type { VariableKind } from "../enum/variable-kind.enum.mjs"; + +type VariableValueType = ( + K extends VariableKind.Nil ? undefined : + K extends VariableKind.Boolean ? boolean : + K extends VariableKind.Number ? number : + K extends VariableKind.String ? string : + K extends VariableKind.Table ? TableMap : + K extends VariableKind.Function ? number : + K extends VariableKind.NativeFunction ? NativeFunction : + never +) + +export type { VariableValueType }; diff --git a/src/variable/definition/type/variable.type.mts b/src/variable/definition/type/variable.type.mts new file mode 100644 index 0000000..8cc327c --- /dev/null +++ b/src/variable/definition/type/variable.type.mts @@ -0,0 +1,19 @@ +import type { VariableBoolean } from "../interface/variable-boolean.interface.mjs"; +import type { VariableFunction } from "../interface/variable-function.interface.mjs"; +import type { VariableNativeFunction } from "../interface/variable-native-function.interface.mjs"; +import type { VariableNil } from "../interface/variable-nil.interface.mjs"; +import type { VariableNumber } from "../interface/variable-number.interface.mjs"; +import type { VariableString } from "../interface/variable-string.interface.mjs"; +import type { VariableTable } from "../interface/variable-table.interface.mjs"; + +type Variable = ( + | VariableNil + | VariableBoolean + | VariableNumber + | VariableString + | VariableTable + | VariableFunction + | VariableNativeFunction +); + +export type { Variable }; diff --git a/src/variable/equals.mts b/src/variable/equals.mts new file mode 100644 index 0000000..510e5bb --- /dev/null +++ b/src/variable/equals.mts @@ -0,0 +1,47 @@ +import { Variable } from "./definition/type/variable.type.mjs"; +import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; +import { isVariableKind } from "./predicate/is-variable-kind.mjs"; +import { isNullish } from "@vitruvius-labs/ts-predicate"; + +function equals(a: Variable | undefined, b: Variable | undefined): boolean +{ + if (a === b) + return true; + + if (isNullish(a) || isNullish(b)) + return false; + + switch (a.data_type) + { + case VariableKind.Nil: + return true; + case VariableKind.Boolean: + return isVariableKind(b, VariableKind.Boolean) && a.boolean === b.boolean; + case VariableKind.Number: + return isVariableKind(b, VariableKind.Number) && a.number === b.number; + case VariableKind.String: + return isVariableKind(b, VariableKind.String) && a.string === b.string; + case VariableKind.Function: + return isVariableKind(b, VariableKind.Function) && a.function_id === b.function_id; + case VariableKind.NativeFunction: + return isVariableKind(b, VariableKind.NativeFunction) && a.native_function === b.native_function; + case VariableKind.Table: + { + if (!isVariableKind(b, VariableKind.Table)) + return false + + if (a.table.size !== b.table.size) + return false + + for (const key of a.table.keys()) + { + if (!equals(a.table.get(key), b.table.get(key))) + return false + } + + return true + } + } +} + +export { equals }; diff --git a/src/variable/nil.mts b/src/variable/nil.mts new file mode 100644 index 0000000..465195a --- /dev/null +++ b/src/variable/nil.mts @@ -0,0 +1,6 @@ +import type { VariableNil } from "./definition/interface/variable-nil.interface.mjs"; +import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; + +const nil: VariableNil = { data_type: VariableKind.Nil }; + +export { nil }; diff --git a/src/variable/predicate/_index.mts b/src/variable/predicate/_index.mts new file mode 100644 index 0000000..deafca5 --- /dev/null +++ b/src/variable/predicate/_index.mts @@ -0,0 +1,6 @@ +export * from "./is-variable-kind-enum.mjs"; +export * from "./is-variable.mjs"; +export * from "./is-variable-kind.mjs"; +export * from "./is-nil.mjs"; +export * from "./assert-variable.mjs"; +export * from "./assert-variable-kind.mjs"; diff --git a/src/variable/predicate/assert-variable-kind.mts b/src/variable/predicate/assert-variable-kind.mts new file mode 100644 index 0000000..55030ca --- /dev/null +++ b/src/variable/predicate/assert-variable-kind.mts @@ -0,0 +1,16 @@ +import { ValidationError } from "@vitruvius-labs/ts-predicate"; +import type { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import type { Variable } from "../definition/type/variable.type.mjs"; +import { assertVariable } from "./assert-variable.mjs"; + +function assertVariableKind(variable: unknown, kind: K): asserts variable is Variable & { data_type: K } +{ + assertVariable(variable); + + if (variable.data_type !== kind) + { + throw new ValidationError(`Expected ${ kind }, got ${ variable.data_type }`); + } +} + +export { assertVariableKind }; diff --git a/src/variable/predicate/assert-variable.mts b/src/variable/predicate/assert-variable.mts new file mode 100644 index 0000000..e9a4f59 --- /dev/null +++ b/src/variable/predicate/assert-variable.mts @@ -0,0 +1,13 @@ +import { ValidationError } from "@vitruvius-labs/ts-predicate"; +import type { Variable } from "../definition/type/variable.type.mjs"; +import { isVariable } from "./is-variable.mjs"; + +function assertVariable(variable: unknown): asserts variable is Variable +{ + if (!isVariable(variable)) + { + throw new ValidationError(`Expected Variable`) + } +} + +export { assertVariable }; diff --git a/src/variable/predicate/is-nil.mts b/src/variable/predicate/is-nil.mts new file mode 100644 index 0000000..56814aa --- /dev/null +++ b/src/variable/predicate/is-nil.mts @@ -0,0 +1,12 @@ +import type { Variable } from "../definition/type/variable.type.mjs"; +import type { VariableNil } from "../definition/interface/variable-nil.interface.mjs"; +import { isVariableKind } from "./is-variable-kind.mjs"; +import { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import { isNullish } from "@vitruvius-labs/ts-predicate"; + +function isNil(parameter: Variable | undefined): parameter is undefined | VariableNil +{ + return isNullish(parameter) || isVariableKind(parameter, VariableKind.Nil); +} + +export { isNil }; diff --git a/src/variable/predicate/is-variable-kind-enum.mts b/src/variable/predicate/is-variable-kind-enum.mts new file mode 100644 index 0000000..6ec5151 --- /dev/null +++ b/src/variable/predicate/is-variable-kind-enum.mts @@ -0,0 +1,17 @@ +import { isEnumValue } from "@vitruvius-labs/ts-predicate"; +import { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; + +function isVariableKindEnum(value: unknown): value is VariableKind +{ + return isEnumValue(value, [ + VariableKind.Nil, + VariableKind.Boolean, + VariableKind.Number, + VariableKind.String, + VariableKind.Table, + VariableKind.Function, + VariableKind.NativeFunction, + ]); +} + +export { isVariableKindEnum }; diff --git a/src/variable/predicate/is-variable-kind.mts b/src/variable/predicate/is-variable-kind.mts new file mode 100644 index 0000000..ecb3c85 --- /dev/null +++ b/src/variable/predicate/is-variable-kind.mts @@ -0,0 +1,10 @@ +import type { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import type { Variable } from "../definition/type/variable.type.mjs"; +import { isVariable } from "./is-variable.mjs"; + +function isVariableKind(variable: unknown, kind: K): variable is Variable & { data_type: K } +{ + return isVariable(variable) && variable.data_type === kind; +} + +export { isVariableKind }; diff --git a/src/variable/predicate/is-variable.mts b/src/variable/predicate/is-variable.mts new file mode 100644 index 0000000..70cdce2 --- /dev/null +++ b/src/variable/predicate/is-variable.mts @@ -0,0 +1,18 @@ +import { isStructuredData } from "@vitruvius-labs/ts-predicate"; +import type { Variable } from "../definition/type/variable.type.mjs"; +import { isVariableKindEnum } from "./is-variable-kind-enum.mjs"; + +function isVariable(value: unknown): value is Variable +{ + return isStructuredData( + value, + { + data_type: isVariableKindEnum, + }, + { + allowExtraneousProperties: true, + } + ); +} + +export { isVariable }; diff --git a/src/variable/unwrap-variable.mts b/src/variable/unwrap-variable.mts new file mode 100644 index 0000000..3692139 --- /dev/null +++ b/src/variable/unwrap-variable.mts @@ -0,0 +1,82 @@ +import type { Variable } from "./definition/type/variable.type.mjs"; +import type { VariableBoolean } from "./definition/interface/variable-boolean.interface.mjs"; +import type { VariableFunction } from "./definition/interface/variable-function.interface.mjs"; +import type { VariableNativeFunction } from "./definition/interface/variable-native-function.interface.mjs"; +import type { VariableNil } from "./definition/interface/variable-nil.interface.mjs"; +import type { VariableNumber } from "./definition/interface/variable-number.interface.mjs"; +import type { VariableString } from "./definition/interface/variable-string.interface.mjs"; +import type { VariableTable } from "./definition/interface/variable-table.interface.mjs"; +import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; +import { isCallable } from "@vitruvius-labs/ts-predicate"; + +class VariableUnwrapUtility +{ + public static unwrap(this: void, input: VariableNil): undefined; + public static unwrap(this: void, input: VariableBoolean): boolean; + public static unwrap(this: void, input: VariableNumber): number; + public static unwrap(this: void, input: VariableString): string; + public static unwrap(this: void, input: VariableTable): Array | Map; + public static unwrap(this: void, input: VariableFunction): { function_id: number }; + public static unwrap(this: void, input: VariableNativeFunction): Function; + public static unwrap(this: void, input: Variable): unknown; + + public static unwrap(this: void, input: Variable): unknown + { + switch (input.data_type) + { + case VariableKind.Nil: + return undefined; + case VariableKind.Boolean: + return input.boolean; + case VariableKind.Number: + return input.number; + case VariableKind.String: + return input.string; + case VariableKind.Table: + return VariableUnwrapUtility.unwrapTable(input); + case VariableKind.Function: + return { function_id: input.function_id }; + case VariableKind.NativeFunction: + return input.native_function; + } + } + + public static unwrapTable(this: void, input: VariableTable): Array | Map + { + const output: Array = []; + + for (let i = 1; i <= input.table.size; ++i) + { + const item: Variable | undefined = input.table.get(i); + + if (item === undefined) + { + // Not a table that can be unwrapped as an array + return VariableUnwrapUtility.unwrapTableGeneric(input); + } + + output.push(VariableUnwrapUtility.unwrap(item)); + } + + return output; + } + + protected static unwrapTableGeneric(this: void, input: VariableTable): Map + { + const output: Map = new Map(); + + for (const [key, value] of input.table.entries()) + { + if (isCallable(key)) + { + continue; + } + + output.set(key, VariableUnwrapUtility.unwrap(value)); + } + + return output; + } +} + +export { VariableUnwrapUtility }; From 6199e9d97fc7e2072b96def17f0ea733fd6f81f0 Mon Sep 17 00:00:00 2001 From: Zamralik Date: Tue, 20 Jan 2026 13:50:19 +0100 Subject: [PATCH 06/71] feat: asynchronous bindings --- .../definition/type/native-function.type.mts | 3 +- src/create-binding.mts | 10 +++-- src/engine.mts | 28 +++++++------- src/lib.mts | 38 +++++++++++-------- tsconfig.json | 2 +- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/boundary/definition/type/native-function.type.mts b/src/boundary/definition/type/native-function.type.mts index db1f948..9f4ef37 100644 --- a/src/boundary/definition/type/native-function.type.mts +++ b/src/boundary/definition/type/native-function.type.mts @@ -1,6 +1,7 @@ +import type { Awaitable } from "@vitruvius-labs/ts-predicate"; import type { Engine } from "../../../engine.mjs"; import type { Variable } from "../../../variable/definition/type/variable.type.mjs"; -type NativeFunction = (engine: Engine, ...args: Array) => Array +type NativeFunction = (engine: Engine, ...args: Array) => Awaitable>; export type { NativeFunction }; diff --git a/src/create-binding.mts b/src/create-binding.mts index a234020..57d4e90 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -1,4 +1,4 @@ -import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError } from "@vitruvius-labs/ts-predicate"; +import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError, type Awaitable } from "@vitruvius-labs/ts-predicate"; import type { Engine } from "./engine.mjs"; import { make_variable } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; @@ -6,6 +6,7 @@ import type { Variable } from "./variable/definition/type/variable.type.mjs"; import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; import { RuntimeError } from "./runtime-error.mjs"; import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; +import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; export const enum ParameterOptionEnum { @@ -81,14 +82,15 @@ function handle_error(error: unknown, callable: Function): never throw new RuntimeError(`An error occurred during native function ${callable.name} execution.`, { cause: error }); } -export function make_function(callable: Function, parameters_descriptor: Array): Variable +export function make_function(callable: Function, parameters_descriptor: Array): VariableNativeFunction { - const proxy_function: NativeFunction = (_: Engine, ...args: Array): Array => { + const proxy_function: NativeFunction = async (_: Engine, ...args: Array): Promise> => + { try { const unwrapped_args: Array = args.map(VariableUnwrapUtility.unwrap); const sanitized_args: Array = sanitize_parameters(unwrapped_args, parameters_descriptor); - const result: unknown = callable(...sanitized_args); + const result: unknown = await callable(...sanitized_args); const variable: Variable = make_variable(result); return [variable]; diff --git a/src/engine.mts b/src/engine.mts index fdfd2d0..51b9dfc 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -157,10 +157,10 @@ export class Engine this.locals_stack.push(new Map()) } - call(func: Variable, ...args: Variable[]): Variable[] | Error + async call(func: Variable, ...args: Variable[]): Promise { if (isVariableKind(func, VariableKind.NativeFunction)) - return this.call_native_function(func.native_function, ...args) + return await this.call_native_function(func.native_function, ...args) assertUnion(func, [unary(assertVariableKind, VariableKind.Function), unary(assertVariableKind, VariableKind.NativeFunction)]); @@ -179,7 +179,7 @@ export class Engine this.stack.push(make_number(args.length)) this.locals_stack.push(new Map()) - const result = this.run() + const result = await this.run() const return_values = this.stack this.stack = old_stack this.call_stack = old_call_stack @@ -192,7 +192,7 @@ export class Engine return return_values } - run_for_steps(steps: number, options?: LuaOptions): Variable | Error | undefined + async run_for_steps(steps: number, options?: LuaOptions): Promise { if (this.error !== undefined) return this.error @@ -203,7 +203,7 @@ export class Engine let step_count = 0 while (this.ip < this.program.length) { - const result = this.step(options) + const result = await this.step(options) if (result !== undefined) return result @@ -216,9 +216,9 @@ export class Engine return this.stack_get(0); } - run(options?: LuaOptions): Variable | Error + async run(options?: LuaOptions): Promise { - const result = this.run_for_steps(1000, options) + const result = await this.run_for_steps(1000, options) if (result === undefined) return new Error('Program ran for too long') @@ -265,9 +265,9 @@ export class Engine return value; } - private call_native_function(native_function: NativeFunction, ...args: Array): Array | Error + private async call_native_function(native_function: NativeFunction, ...args: Array): Promise | Error> { - const results = native_function(this, ...args) + const results = await native_function(this, ...args) if (this.error !== undefined) { @@ -313,7 +313,7 @@ export class Engine throw new RuntimeError(message, {}, op.debug) } - private run_instruction(op: Op): Error | undefined + private async run_instruction(op: Op): Promise { const { code, arg } = op @@ -355,7 +355,7 @@ export class Engine if (isVariableKind(iter, VariableKind.NativeFunction)) { - const result = this.call_native_function(iter.native_function, control, state) + const result = await this.call_native_function(iter.native_function, control, state) if (result instanceof Error) return result @@ -663,7 +663,7 @@ export class Engine const args = this.stack.splice(this.stack.length - count, count) if (func_var.native_function !== undefined) { - const result = this.call_native_function(func_var.native_function, ...args) + const result = await this.call_native_function(func_var.native_function, ...args) if (result instanceof Error) return result @@ -726,7 +726,7 @@ export class Engine return undefined } - step(options?: LuaOptions): Error | undefined + async step(options?: LuaOptions): Promise { if (this.error !== undefined) return this.error @@ -746,7 +746,7 @@ export class Engine console.log(this.ip - 1, op_code_name(op.code), arg) } - const result = this.run_instruction(op) + const result = await this.run_instruction(op) if (result !== undefined) return result diff --git a/src/lib.mts b/src/lib.mts index 091d95a..2f6f3ee 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -15,6 +15,7 @@ import { isNil } from "./variable/predicate/is-nil.mjs" import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs" import type { NativeFunction } from "./index.mjs" import type { TableMap } from "./boundary/definition/type/table-map.type.mjs" +import { RuntimeError } from "./runtime-error.mjs" function optional_parameter( expected_kind: K, @@ -223,10 +224,17 @@ function key_variable(key: unknown): Variable return make_variable(key); } +// @ts-expect-error: unimplemented function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] { assertVariableKind(table, VariableKind.Table); + + throw new RuntimeError("Unimplemented: table.sort"); + + /* + const entries: Array<[unknown, Variable]> = [...table.table.entries()] + entries.sort(([_, a], [__, b]) => { const result = engine.call(by, a, b) @@ -240,31 +248,29 @@ function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] const numbered_entries = entries.map(([key, _], i) => [i + 1, key_variable(key)] as const) return [{ data_type: VariableKind.Table, table: new Map(numbered_entries) }] + + */ } -function find(engine: Engine, table: Variable, matches: Variable): Variable[] +async function find(engine: Engine, table: Variable, matches: Variable): Promise { assertVariableKind(table, VariableKind.Table); const entries: Array<[unknown, Variable]> = [...table.table.entries()] - const found: [unknown, Variable] | undefined = entries.find( - ([_, a]) => - { - const result = engine.call(matches, a) - if (result instanceof Error) - return 0 - const matching = result.at(0) - assertVariableKind(matching, VariableKind.Boolean); - return matching.boolean - } - ) + for (const [key, value] of entries) + { + const result = await engine.call(matches, value) - if (found === undefined) - return [nil] + if (result instanceof Error) + throw result; - const [key] = found + const matching: Variable | undefined = result.at(0); + assertVariableKind(matching, VariableKind.Boolean); - return [make_variable(key)]; + return [make_variable(key)]; + } + + return [nil]; } function first(_: Engine, table: Variable): Variable[] diff --git a/tsconfig.json b/tsconfig.json index 87df1a2..550f3cf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,7 +45,7 @@ "useUnknownInCatchVariables": true }, "include": [ - "./src/index.mts" + "./src" ], "exclude": [ "./node_modules" From 610b17b03f259fa74114da125c4177f04d3171b5 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Tue, 20 Jan 2026 14:47:45 +0000 Subject: [PATCH 07/71] chore: Added CI skeleton. --- .eslint/configuration/_index.mjs | 19 + .eslint/configuration/disabled/config.mjs | 12 + .eslint/configuration/disabled/rules.mjs | 258 +++ .../configuration/file-specific/config.mjs | 14 + .eslint/configuration/file-specific/rules.mjs | 10 + .eslint/configuration/global/default.mjs | 27 + .eslint/configuration/global/ignores.mjs | 19 + .eslint/configuration/strict/config.mjs | 12 + .eslint/configuration/strict/rules.mjs | 884 ++++++++ .eslint/configuration/style/config.mjs | 12 + .eslint/configuration/style/rules.mjs | 796 +++++++ .eslint/configuration/test/config.mjs | 14 + .eslint/configuration/test/rules.mjs | 45 + .eslintrc.json | 77 - .github/dependabot.yml | 9 + .github/workflows/node.js.yml | 21 - .github/workflows/pipeline.yml | 189 ++ .swcrc | 20 + .vscode/cspell.json | 45 + eslint.config.mjs | 3 + package.json | 36 +- pnpm-lock.yaml | 1967 ++++++++++++++++- src/create-binding.mts | 2 +- tsconfig.build.json | 18 + 24 files changed, 4316 insertions(+), 193 deletions(-) create mode 100644 .eslint/configuration/_index.mjs create mode 100644 .eslint/configuration/disabled/config.mjs create mode 100644 .eslint/configuration/disabled/rules.mjs create mode 100644 .eslint/configuration/file-specific/config.mjs create mode 100644 .eslint/configuration/file-specific/rules.mjs create mode 100644 .eslint/configuration/global/default.mjs create mode 100644 .eslint/configuration/global/ignores.mjs create mode 100644 .eslint/configuration/strict/config.mjs create mode 100644 .eslint/configuration/strict/rules.mjs create mode 100644 .eslint/configuration/style/config.mjs create mode 100644 .eslint/configuration/style/rules.mjs create mode 100644 .eslint/configuration/test/config.mjs create mode 100644 .eslint/configuration/test/rules.mjs delete mode 100644 .eslintrc.json create mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/node.js.yml create mode 100644 .github/workflows/pipeline.yml create mode 100644 .swcrc create mode 100644 .vscode/cspell.json create mode 100644 eslint.config.mjs create mode 100644 tsconfig.build.json diff --git a/.eslint/configuration/_index.mjs b/.eslint/configuration/_index.mjs new file mode 100644 index 0000000..b0e73ae --- /dev/null +++ b/.eslint/configuration/_index.mjs @@ -0,0 +1,19 @@ +import { configuration as disabled_configuration } from "./disabled/config.mjs"; +import { configuration as default_configuration } from "./global/default.mjs"; +import { configuration as ignores_configuration } from "./global/ignores.mjs"; +import { configuration as strict_configuration } from "./strict/config.mjs"; +import { configuration as style_configuration } from "./style/config.mjs"; +import { configuration as test_configuration } from "./test/config.mjs"; +import { configurations as file_specific_configurations } from "./file-specific/config.mjs"; + +const CONFIGURATIONS = [ + disabled_configuration, + default_configuration, + ignores_configuration, + strict_configuration, + style_configuration, + test_configuration, + ...file_specific_configurations, +]; + +export { CONFIGURATIONS as configurations }; diff --git a/.eslint/configuration/disabled/config.mjs b/.eslint/configuration/disabled/config.mjs new file mode 100644 index 0000000..de7428d --- /dev/null +++ b/.eslint/configuration/disabled/config.mjs @@ -0,0 +1,12 @@ +import { rules } from "./rules.mjs"; + +const CONFIGURATION = { + name: "disabled", + files: [ + ".disabled", + ], + ignores: [], + rules: rules, +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/disabled/rules.mjs b/.eslint/configuration/disabled/rules.mjs new file mode 100644 index 0000000..3e33ea9 --- /dev/null +++ b/.eslint/configuration/disabled/rules.mjs @@ -0,0 +1,258 @@ +/* + * List disabled rules to help with configuration inspection + * It's easier to see the new rules in the unused rules list +**/ +const RULES = { + // Buggy rule + "@style/indent": "off", + // Buggy rule + "@style/indent-binary-ops": "off", + // We don't do JSX + "@style/jsx-child-element-spacing": "off", + // We don't do JSX + "@style/jsx-closing-bracket-location": "off", + // We don't do JSX + "@style/jsx-closing-tag-location": "off", + // We don't do JSX + "@style/jsx-curly-brace-presence": "off", + // We don't do JSX + "@style/jsx-curly-newline": "off", + // We don't do JSX + "@style/jsx-curly-spacing": "off", + // We don't do JSX + "@style/jsx-equals-spacing": "off", + // We don't do JSX + "@style/jsx-first-prop-new-line": "off", + // We don't do JSX + "@style/jsx-function-call-newline": "off", + // We don't do JSX + "@style/jsx-indent-props": "off", + // We don't do JSX + "@style/jsx-max-props-per-line": "off", + // We don't do JSX + "@style/jsx-newline": "off", + // We don't do JSX + "@style/jsx-one-expression-per-line": "off", + // We don't do JSX + "@style/jsx-pascal-case": "off", + // We don't do JSX + "@style/jsx-props-no-multi-spaces": "off", + // We don't do JSX + "@style/jsx-self-closing-comp": "off", + // We don't do JSX + "@style/jsx-sort-props": "off", + // We don't do JSX + "@style/jsx-tag-spacing": "off", + // We don't do JSX + "@style/jsx-wrap-multilines": "off", + // We don't do JSX + "@style/wrap-regex": "off", + // Free choice + "@style/lines-around-comment": "off", + // Case by case + "@style/multiline-comment-style": "off", + // The @ts/typedef rule make this rule obsolete + "@style/no-confusing-arrow": "off", + // Shouldn't be an issue with TypeScript + "@style/no-mixed-operators": "off", + // The curly rule make this rule obsolete + "@style/nonblock-statement-body-position": "off", + // The one-var rule make this rule obsolete + "@style/one-var-declaration-per-line": "off", + // The @style/comma-spacing rule make this rule obsolete + "@style/type-generic-spacing": "off", + // TypeScript prevent such confusion + "@style/wrap-regexp": "off", + // Handled by TypeScript option noImplicitReturns + "@ts/consistent-return": "off", + // Not formally defined + "@ts/naming-convention": "off", + // Handled by TypeScript + "@ts/no-dupe-class-members": "off", + // Incompatible with the @ts/typedef rule + "@ts/no-inferrable-types": "off", + // Handled by TypeScript option noImplicitThis + "@ts/no-invalid-this": "off", + // Mixed enums are good for holding configuration values + "@ts/no-mixed-enums": "off", + // Handled by TypeScript + "@ts/no-redeclare": "off", + // It was not necessary so far + "@ts/no-restricted-imports": "off", + // Not needed so far + "@ts/no-restricted-types": "off", + // The @ts/parameter-properties rule make this rule obsolete + "@ts/no-unnecessary-parameter-property-assignment": "off", + // Explicit qualifiers improve readability + "@ts/no-unnecessary-qualifier": "off", + // Being explicit is never bad + "@ts/no-unnecessary-type-arguments": "off", + // Being explicit is never bad + "@ts/no-unnecessary-type-parameters": "off", + // Enum comparison is necessary for comparing values with magic numbers or string constants + "@ts/no-unsafe-enum-comparison": "off", + // Type assertions are forbidden + "@ts/no-unsafe-type-assertion": "off", + // Traditional type assertion is more explicit + "@ts/non-nullable-type-assertion-style": "off", + // Destructuring is not always the best choice + "@ts/prefer-destructuring": "off", + // The @ts/no-namespace rule make this rule obsolete + "@ts/prefer-namespace-keyword": "off", + // Conflict with the no-param-reassign rule props option + "@ts/prefer-readonly-parameter-types": "off", + // The type is often implied by the callable return type + "@ts/prefer-reduce-type-parameter": "off", + // Distinct signatures can improve readability + "@ts/unified-signatures": "off", + // The no-var rule make this rule obsolete + "block-scoped-var": "off", + // Conflict with some dependencies and APIs + "camelcase": "off", + // Some comments may need to begin with a lowercase + "capitalized-comments": "off", + // Redefined as @ts/class-methods-use-this + "class-methods-use-this": "off", + // Redefined as @ts/consistent-return + "consistent-return": "off", + // The prefer-arrow-callback rule make this rule obsolete + "consistent-this": "off", + // Handled by TypeScript + "constructor-super": "off", + // Conflict with @ts/switch-exhaustiveness-check option allowDefaultCaseForExhaustiveSwitch + "default-case": "off", + // Redefined as @ts/default-param-last + "default-param-last": "off", + // Redefined as @ts/dot-notation + "dot-notation": "off", + // The prefer-arrow-callback rule make this rule obsolete + "func-name-matching": "off", + // Handled by TypeScript + "getter-return": "off", + // Handled by TypeScript + "guard-for-in": "off", + // The rules id-denylist and id-length make this rule obsolete + "id-match": "off", + // Redefined as @ts/init-declarations + "init-declarations": "off", + // Redefined as @ts/max-params + "max-params": "off", + // All variables follow the same naming convention + "new-cap": "off", + // The rule is only for a browser environment + "no-alert": "off", + // Redefined as @ts/no-array-constructor + "no-array-constructor": "off", + // It is not always desirable to perform multiple asynchronous operations in parallel + "no-await-in-loop": "off", + // Bitwise operations are useful in some cases + "no-bitwise": "off", + // Handled by TypeScript + "no-const-assign": "off", + // Early interruption improve readability + "no-continue": "off", + // Using ASCII or unicode code points in RegExp patterns is not bad + "no-control-regex": "off", + // TypeScript prevent such confusion + "no-div-regex": "off", + // Handled by TypeScript + "no-dupe-args": "off", + // Redefined as @ts/no-dupe-class-members + "no-dupe-class-members": "off", + // Handled by TypeScript + "no-dupe-keys": "off", + // Redefined as @ts/no-empty-function + "no-empty-function": "off", + // The eqeqeq rule make this rule obsolete + "no-eq-null": "off", + // The no-labels rule make this rule obsolete + "no-extra-label": "off", + // Handled by TypeScript + "no-func-assign": "off", + // Doesn't apply to modules + "no-implicit-globals": "off", + // Redefined as @ts/no-implied-eval + "no-implied-eval": "off", + // Handled by TypeScript + "no-import-assign": "off", + // Inline comments may improve readability in some cases + "no-inline-comments": "off", + // Not an issue in modules and strict mode + "no-inner-declarations": "off", + // Redefined as @ts/no-invalid-this + "no-invalid-this": "off", + // The no-labels rule make this rule obsolete + "no-label-var": "off", + // Not an issue in modules and strict mode + "no-lone-blocks": "off", + // Redefined as @ts/no-loop-func + "no-loop-func": "off", + // Redefined as @ts/no-magic-numbers + "no-magic-numbers": "off", + // Handled by TypeScript + "no-obj-calls": "off", + // Increment and decrement operators are good + "no-plusplus": "off", + // Redefined as @ts/no-redeclare + "no-redeclare": "off", + // The rules id-denylist and id-length are enough so far + "no-restricted-exports": "off", + // Not needed so far + "no-restricted-globals": "off", + // Redefined as @ts/no-restricted-imports + "no-restricted-imports": "off", + // Handled by TypeScript + "no-setter-return": "off", + // Redefined as @ts/no-shadow + "no-shadow": "off", + // Ternary can be useful in some cases + "no-ternary": "off", + // Handled by TypeScript + "no-this-before-super": "off", + // Redefined as @ts/only-throw-error + "no-throw-literal": "off", + // Handled by TypeScript + "no-undef": "off", + // Incompatible with @ts/init-declarations + "no-undef-init": "off", + // The no-shadow-restricted-names rule make this rule obsolete + "no-undefined": "off", + // Handled by TypeScript + "no-unreachable": "off", + // Handled by TypeScript + "no-unsafe-negation": "off", + // Redefined as @ts/no-unused-expressions + "no-unused-expressions": "off", + // The no-labels rule make this rule obsolete + "no-unused-labels": "off", + // Redefined as @ts/no-unused-vars + "no-unused-vars": "off", + // Redefined as @ts/no-use-before-define + "no-use-before-define": "off", + // Incompatible with @ts/init-declarations + "no-useless-assignment": "off", + // Redefined as @ts/no-useless-constructor + "no-useless-constructor": "off", + // Redefined as @ts/prefer-destructuring + "prefer-destructuring": "off", + // Math.pow() can improve readability + "prefer-exponentiation-operator": "off", + // Redefined as @ts/prefer-promise-reject-errors + "prefer-promise-reject-errors": "off", + // Redefined as @ts/require-await + "require-await": "off", + // Simple RegExp do not need the unicode flag + "require-unicode-regexp": "off", + // Grouping keys logically improve readability + "sort-keys": "off", + // The one-var rule make this rule obsolete + "sort-vars": "off", + // Handled by TypeScript + "use-isnan": "off", + // Handled by TypeScript + "valid-typeof": "off", + // The no-var rule make this rule obsolete + "vars-on-top": "off", +}; + +export { RULES as rules }; diff --git a/.eslint/configuration/file-specific/config.mjs b/.eslint/configuration/file-specific/config.mjs new file mode 100644 index 0000000..70ff8d2 --- /dev/null +++ b/.eslint/configuration/file-specific/config.mjs @@ -0,0 +1,14 @@ +import { rules } from "./rules.mjs"; + +const CONFIGURATIONS = [ + { + name: "ts-consistent-type-exports", + files: [ + "**/_index.mts", + ], + ignores: [], + rules: rules, + } +]; + +export { CONFIGURATIONS as configurations }; diff --git a/.eslint/configuration/file-specific/rules.mjs b/.eslint/configuration/file-specific/rules.mjs new file mode 100644 index 0000000..272bb0b --- /dev/null +++ b/.eslint/configuration/file-specific/rules.mjs @@ -0,0 +1,10 @@ +/* + * List disabled rules to help with configuration inspection + * It's easier to see the new rules in the unused rules list +**/ +const RULES = { + // Currently buggy for files purely exporting types. + "@ts/consistent-type-exports": "off", +}; + +export { RULES as rules }; diff --git a/.eslint/configuration/global/default.mjs b/.eslint/configuration/global/default.mjs new file mode 100644 index 0000000..a75e70a --- /dev/null +++ b/.eslint/configuration/global/default.mjs @@ -0,0 +1,27 @@ +import typescript_eslint from "typescript-eslint"; +import stylistic from "@stylistic/eslint-plugin"; + +/* Default configuration with plugins */ +const CONFIGURATION = { + name: "default", + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: {}, + parser: typescript_eslint.parser, + parserOptions: { + project: true, + }, + }, + linterOptions: { + noInlineConfig: false, + reportUnusedDisableDirectives: "warn", + }, + plugins: { + "@ts": typescript_eslint.plugin, + "@style": stylistic, + }, + settings: {}, +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/global/ignores.mjs b/.eslint/configuration/global/ignores.mjs new file mode 100644 index 0000000..4d0542a --- /dev/null +++ b/.eslint/configuration/global/ignores.mjs @@ -0,0 +1,19 @@ +/* Ignored files for all configurations */ +const CONFIGURATION = { + ignores: [ + "**/*.js", + "**/*.cjs", + "**/*.mjs", + "**/*.d.ts", + "**/*.d.cts", + "**/*.d.mts", + "build", + "reports", + "coverage", + "tmp", + ".stryker-tmp", + "**/test.mts" + ], +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/strict/config.mjs b/.eslint/configuration/strict/config.mjs new file mode 100644 index 0000000..68eb648 --- /dev/null +++ b/.eslint/configuration/strict/config.mjs @@ -0,0 +1,12 @@ +import { rules } from "./rules.mjs"; + +const CONFIGURATION = { + name: "strict", + files: [ + "**/*.mts", + ], + ignores: [], + rules: rules, +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/strict/rules.mjs b/.eslint/configuration/strict/rules.mjs new file mode 100644 index 0000000..2e9adb5 --- /dev/null +++ b/.eslint/configuration/strict/rules.mjs @@ -0,0 +1,884 @@ +const RULES = { + "@ts/array-type": [ + "error", + { + "default": "generic", + "readonly": "generic" + } + ], + "@ts/await-thenable": "error", + "@ts/ban-ts-comment": [ + "error", + { + "ts-expect-error": "allow-with-description", + "ts-ignore": false, + "ts-nocheck": false, + "ts-check": false, + "minimumDescriptionLength": 10 + } + ], + "@ts/ban-tslint-comment": "error", + "@ts/class-methods-use-this": [ + "error", + { + "enforceForClassFields": false, + "ignoreClassesThatImplementAnInterface": true, + "exceptMethods": [ + "execute" + ], + } + ], + "@ts/consistent-generic-constructors": [ + "error", + "type-annotation" + ], + "@ts/consistent-type-assertions": [ + "error", + { + "assertionStyle": "as", + "objectLiteralTypeAssertions": "never" + } + ], + "@ts/consistent-type-exports": [ + "error", + { + "fixMixedExportsWithInlineTypeSpecifier": true + } + ], + "@ts/consistent-type-imports": [ + "error", + { + "prefer": "type-imports", + "fixStyle": "inline-type-imports", + "disallowTypeAnnotations": true + } + ], + "@ts/default-param-last": "error", + "@ts/explicit-function-return-type": [ + "error", + { + "allowExpressions": false, + "allowTypedFunctionExpressions": false, + "allowHigherOrderFunctions": true, + "allowDirectConstAssertionInArrowFunctions": false, + "allowConciseArrowFunctionExpressionsStartingWithVoid": false, + "allowFunctionsWithoutTypeParameters": false, + "allowedNames": [], + "allowIIFEs": false + } + ], + "@ts/explicit-member-accessibility": [ + "error", + { + "accessibility": "explicit", + "ignoredMethodNames": [], + "overrides": { + "accessors": "explicit", + "constructors": "explicit", + "methods": "explicit", + "parameterProperties": "explicit", + "properties": "explicit" + } + } + ], + "@ts/explicit-module-boundary-types": [ + "error", + { + "allowArgumentsExplicitlyTypedAsAny": false, + "allowDirectConstAssertionInArrowFunctions": false, + "allowHigherOrderFunctions": false, + "allowTypedFunctionExpressions": false, + "allowedNames": [] + } + ], + "@ts/init-declarations": [ + "error", + "always" + ], + "@ts/max-params": [ + "error", + { + "max": 3 + } + ], + "@ts/no-array-constructor": "error", + "@ts/no-array-delete": "error", + "@ts/no-base-to-string": "error", + "@ts/no-confusing-non-null-assertion": "error", + "@ts/no-confusing-void-expression": [ + "error", + { + "ignoreArrowShorthand": false, + "ignoreVoidOperator": false + } + ], + "@ts/no-deprecated": "error", + "@ts/no-duplicate-enum-values": "error", + "@ts/no-duplicate-type-constituents": [ + "error", + { + "ignoreIntersections": false, + "ignoreUnions": false + } + ], + "@ts/no-dynamic-delete": "error", + "@ts/no-empty-function": [ + "error", + { + "allow": [ + "private-constructors", + "protected-constructors", + "decoratedFunctions" + ] + } + ], + "@ts/no-empty-object-type": [ + "error", + { + "allowInterfaces": "with-single-extends", + "allowObjectTypes": "never" + } + ], + "@ts/no-explicit-any": [ + "error", + { + "fixToUnknown": true, + "ignoreRestArgs": false + } + ], + "@ts/no-extra-non-null-assertion": "error", + "@ts/no-extraneous-class": [ + "error", + { + "allowConstructorOnly": false, + "allowEmpty": false, + "allowStaticOnly": true, + "allowWithDecorator": false + } + ], + "@ts/no-floating-promises": [ + "error", + { + "ignoreIIFE": false, + "ignoreVoid": false + } + ], + "@ts/no-for-in-array": "error", + "@ts/no-implied-eval": "error", + "@ts/no-import-type-side-effects": "error", + "@ts/no-invalid-void-type": [ + "error", + { + "allowAsThisParameter": true, + "allowInGenericTypeArguments": [ + "Promise", + "PromiseWithResolvers", + "Generator" + ] + } + ], + "@ts/no-loop-func": "error", + "@ts/no-magic-numbers": [ + "error", + { + "enforceConst": true, + "detectObjects": true, + "ignoreArrayIndexes": false, + "ignoreDefaultValues": false, + "ignoreEnums": true, + "ignoreNumericLiteralTypes": false, + "ignoreReadonlyClassProperties": false, + "ignoreClassFieldInitialValues": false, + "ignoreTypeIndexes": false, + "ignore": [ + 0, + 1, + -1 + ] + } + ], + "@ts/no-meaningless-void-operator": [ + "error", + { + "checkNever": true + } + ], + "@ts/no-misused-new": "error", + "@ts/no-misused-promises": [ + "error", + { + "checksConditionals": true, + "checksVoidReturn": true, + "checksSpreads": true + } + ], + "@ts/no-namespace": [ + "error", + { + "allowDeclarations": false, + "allowDefinitionFiles": false + } + ], + "@ts/no-non-null-asserted-nullish-coalescing": "error", + "@ts/no-non-null-asserted-optional-chain": "error", + "@ts/no-non-null-assertion": "error", + "@ts/no-redundant-type-constituents": "error", + "@ts/no-require-imports": "error", + "@ts/no-shadow": [ + "error", + { + "allow": [], + "hoist": "all", + "builtinGlobals": true, + "ignoreOnInitialization": false, + "ignoreTypeValueShadow": false, + "ignoreFunctionTypeParameterNameValueShadow": false + } + ], + "@ts/no-this-alias": [ + "error", + { + "allowDestructuring": false, + "allowedNames": [] + } + ], + "@ts/no-unnecessary-boolean-literal-compare": [ + "error", + { + "allowComparingNullableBooleansToTrue": false, + "allowComparingNullableBooleansToFalse": false + } + ], + "@ts/no-unnecessary-condition": [ + "error", + { + "allowConstantLoopConditions": false, + "allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing": false + } + ], + "@ts/no-unnecessary-template-expression": "error", + "@ts/no-unnecessary-type-assertion": "error", + "@ts/no-unnecessary-type-constraint": "error", + "@ts/no-unsafe-argument": "error", + "@ts/no-unsafe-assignment": "error", + "@ts/no-unsafe-call": "error", + "@ts/no-unsafe-declaration-merging": "error", + "@ts/no-unsafe-function-type": "error", + "@ts/no-unsafe-member-access": "error", + "@ts/no-unsafe-return": "error", + "@ts/no-unsafe-unary-minus": "error", + "@ts/no-unused-expressions": [ + "error", + { + "allowShortCircuit": false, + "allowTernary": false, + "allowTaggedTemplates": false, + "enforceForJSX": true + } + ], + "@ts/no-unused-vars": [ + "error", + { + "vars": "all", + "args": "after-used", + "caughtErrors": "all", + "ignoreRestSiblings": false + /* "varsIgnorePattern": undefined, */ + /* "argsIgnorePattern": undefined, */ + } + ], + "@ts/no-use-before-define": [ + "error", + { + "functions": true, + "classes": true, + "variables": true, + "enums": true, + "typedefs": true, + "ignoreTypeReferences": false, + "allowNamedExports": false + } + ], + "@ts/no-useless-constructor": "error", + "@ts/no-useless-empty-export": "error", + "@ts/no-wrapper-object-types": "error", + "@ts/only-throw-error": [ + "error", + { + "allowThrowingAny": false, + "allowThrowingUnknown": false + } + ], + "@ts/parameter-properties": [ + "error", + { + "prefer": "class-property", + "allow": [] + } + ], + "@ts/prefer-as-const": "error", + "@ts/prefer-enum-initializers": "error", + "@ts/prefer-find": "error", + "@ts/prefer-includes": "error", + "@ts/prefer-literal-enum-member": [ + "error", + { + "allowBitwiseExpressions": false + } + ], + "@ts/prefer-nullish-coalescing": [ + "error", + { + "ignoreTernaryTests": false, + "ignoreIfStatements": true, + "ignoreConditionalTests": false, + "ignoreMixedLogicalExpressions": false, + "ignoreBooleanCoercion": false, + "ignorePrimitives": { + "bigint": false, + "boolean": false, + "number": false, + "string": false + } + } + ], + "@ts/prefer-optional-chain": [ + "error", + { + "checkAny": true, + "checkUnknown": true, + "checkString": true, + "checkNumber": true, + "checkBoolean": true, + "checkBigInt": true, + "requireNullish": true, + "allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing": false + } + ], + "@ts/prefer-promise-reject-errors": [ + "error", + { + "allowEmptyReject": false + } + ], + "@ts/prefer-readonly": [ + "error", + { + "onlyInlineLambdas": false + } + ], + "@ts/prefer-regexp-exec": "error", + "@ts/prefer-return-this-type": "error", + "@ts/prefer-string-starts-ends-with": "error", + "@ts/promise-function-async": [ + "error", + { + "allowAny": true, + "allowedPromiseNames": [], + "checkArrowFunctions": true, + "checkFunctionDeclarations": true, + "checkFunctionExpressions": true, + "checkMethodDeclarations": true + } + ], + "@ts/related-getter-setter-pairs": "error", + "@ts/require-array-sort-compare": [ + "error", + { + "ignoreStringArrays": false + } + ], + "@ts/require-await": "error", + "@ts/restrict-plus-operands": [ + "error", + { + "allowAny": false, + "allowBoolean": false, + "allowNullish": false, + "allowRegExp": false, + "allowNumberAndString": false, + "skipCompoundAssignments": false + } + ], + "@ts/restrict-template-expressions": [ + "error", + { + "allowAny": false, + "allowBoolean": false, + "allowNullish": false, + "allowNumber": false, + "allowRegExp": false + } + ], + "@ts/return-await": [ + "error", + "always" + ], + "@ts/strict-boolean-expressions": [ + "error", + { + "allowString": false, + "allowNumber": false, + "allowNullableObject": false, + "allowNullableBoolean": false, + "allowNullableString": false, + "allowNullableNumber": false, + "allowNullableEnum": false, + "allowAny": false, + "allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing": false + } + ], + "@ts/switch-exhaustiveness-check": [ + "error", + { + "allowDefaultCaseForExhaustiveSwitch": false, + "requireDefaultForNonUnion": false + } + ], + "@ts/triple-slash-reference": [ + "error", + { + "lib": "never", + "path": "never", + "types": "never" + } + ], + "@ts/typedef": [ + "error", + { + "arrayDestructuring": true, + "arrowParameter": true, + "memberVariableDeclaration": true, + "objectDestructuring": true, + "parameter": true, + "propertyDeclaration": true, + "variableDeclaration": true, + "variableDeclarationIgnoreFunction": true + } + ], + "@ts/unbound-method": [ + "error", + { + "ignoreStatic": false + } + ], + "@ts/use-unknown-in-catch-callback-variable": "error", + "accessor-pairs": [ + "warn", + { + "setWithoutGet": true, + "getWithoutSet": true, + "enforceForClassMembers": true + } + ], + "array-callback-return": [ + "error", + { + "allowImplicit": false, + "checkForEach": true, + "allowVoid": false + } + ], + "complexity": [ + "error", + { + "max": 30 + } + ], + "default-case-last": "error", + "eqeqeq": [ + "error", + "always", + { + "null": "ignore" + } + ], + "for-direction": "error", + "func-style": [ + "error", + "declaration", + { + "allowArrowFunctions": true + } + ], + "max-classes-per-file": [ + "error", + { + "max": 1, + "ignoreExpressions": false + } + ], + "max-depth": [ + "error", + { + "max": 7 + } + ], + "max-lines": [ + "error", + { + "max": 1000, + "skipBlankLines": true, + "skipComments": true + } + ], + "max-lines-per-function": [ + "error", + { + "max": 100, + "skipBlankLines": true, + "skipComments": true, + "IIFEs": true + } + ], + "max-nested-callbacks": [ + "error", + { + "max": 4 + } + ], + "max-statements": [ + "error", + 50, + { + "ignoreTopLevelFunctions": false + } + ], + "no-async-promise-executor": "error", + "no-caller": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": [ + "error", + "always" + ], + "no-console": "warn", + "no-constant-binary-expression": "error", + "no-constant-condition": [ + "error", + { + "checkLoops": true + } + ], + "no-constructor-return": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-else-if": "error", + "no-duplicate-case": "error", + "no-duplicate-imports": [ + "error", + { + "includeExports": true + } + ], + "no-else-return": [ + "error", + { + "allowElseIf": false + } + ], + "no-empty": [ + "error", + { + "allowEmptyCatch": false + } + ], + "no-empty-character-class": "error", + "no-empty-pattern": [ + "error", + { + "allowObjectPatternsAsParameters": false + } + ], + "no-empty-static-block": "error", + "no-eval": [ + "error", + { + "allowIndirect": false + } + ], + "no-ex-assign": "error", + "no-extend-native": [ + "error", + { + "exceptions": [] + } + ], + "no-extra-bind": "error", + "no-extra-boolean-cast": [ + "error", + { + "enforceForLogicalOperands": true + } + ], + "no-fallthrough": [ + "error", + { + "allowEmptyCase": false, + "commentPattern": "no break|break.*?omitted|falls? ?through" + } + ], + "no-global-assign": [ + "error", + { + "exceptions": [] + } + ], + "no-implicit-coercion": [ + "error", + { + "boolean": true, + "number": true, + "string": true, + "disallowTemplateShorthand": true, + "allow": [] + } + ], + "no-invalid-regexp": [ + "error", + { + "allowConstructorFlags": [ + "d", + "g", + "i", + "m", + "s", + "u", + "v", + "y", + ] + } + ], + "no-irregular-whitespace": [ + "error", + { + "skipStrings": false, + "skipComments": false, + "skipRegExps": false, + "skipTemplates": false, + "skipJSXText": false + } + ], + "no-iterator": "error", + "no-labels": [ + "error", + { + "allowLoop": false, + "allowSwitch": false + } + ], + "no-lonely-if": "error", + "no-loss-of-precision": "error", + "no-misleading-character-class": [ + "error", + { + "allowEscape": false + } + ], + "no-multi-assign": [ + "error", + { + "ignoreNonDeclaration": false + } + ], + "no-multi-str": "error", + "no-negated-condition": "error", + "no-nested-ternary": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-native-nonconstructor": "error", + "no-new-wrappers": "error", + "no-nonoctal-decimal-escape": "error", + "no-object-constructor": "error", + "no-octal": "error", + "no-octal-escape": "error", + "no-param-reassign": [ + "error", + { + "props": false + } + ], + "no-promise-executor-return": [ + "error", + { + "allowVoid": false + } + ], + "no-proto": "error", + "no-prototype-builtins": "error", + "no-restricted-properties": [ + "error", + { + "property": "isPrototypeOf", + "message": "Use instanceof instead" + }, + { + "property": "propertyIsEnumerable", + "message": "Do not check property enumerability" + } + ], + "no-restricted-syntax": [ + "error", + { + "message": "IIFEs are not allowed", + "selector": "CallExpression[callee.type=FunctionExpression]" + }, + { + "message": "IIFEs are not allowed", + "selector": "CallExpression[callee.type=ArrowFunctionExpression]" + } + ], + "no-return-assign": [ + "error", + "always" + ], + "no-script-url": "error", + "no-self-assign": [ + "error", + { + "props": true + } + ], + "no-self-compare": "error", + "no-sequences": [ + "error", + { + "allowInParentheses": false + } + ], + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-template-curly-in-string": "error", + "no-underscore-dangle": [ + "error", + { + "allow": [], + "allowAfterThis": true, + "allowAfterSuper": false, + "allowAfterThisConstructor": true, + "enforceInMethodNames": true, + "enforceInClassFields": true, + "allowInArrayDestructuring": false, + "allowInObjectDestructuring": false, + "allowFunctionParams": false + } + ], + "no-unexpected-multiline": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": [ + "error", + { + "defaultAssignment": false + } + ], + "no-unreachable-loop": [ + "error", + { + "ignore": [] + } + ], + "no-unsafe-finally": "error", + "no-unsafe-optional-chaining": [ + "error", + { + "disallowArithmeticOperators": true + } + ], + "no-unused-private-class-members": "error", + "no-useless-backreference": "error", + "no-useless-call": "error", + "no-useless-catch": "error", + "no-useless-computed-key": [ + "error", + { + "enforceForClassMembers": true + } + ], + "no-useless-concat": "error", + "no-useless-escape": "error", + "no-useless-rename": [ + "error", + { + "ignoreDestructuring": false, + "ignoreImport": false, + "ignoreExport": false + } + ], + "no-useless-return": "error", + "no-var": "error", + "no-void": [ + "error", + { + "allowAsStatement": false + } + ], + "no-warning-comments": [ + "warn", + { + "terms": [ + "TODO", + "FIXME" + ], + "location": "anywhere" + } + ], + "no-with": "error", + "object-shorthand": [ + "error", + "consistent" + ], + "one-var": [ + "error", + "never" + ], + "prefer-arrow-callback": [ + "error", + { + "allowNamedFunctions": false, + "allowUnboundThis": false + } + ], + "prefer-const": [ + "error", + { + "destructuring": "any", + "ignoreReadBeforeAssign": true + } + ], + "prefer-named-capture-group": "error", + "prefer-numeric-literals": "error", + "prefer-object-has-own": "error", + "prefer-object-spread": "error", + "prefer-regex-literals": [ + "error", + { + "disallowRedundantWrapping": true + } + ], + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "error", + "radix": [ + "error", + "always" + ], + "require-atomic-updates": [ + "error", + { + "allowProperties": false + } + ], + "require-yield": "error", + "unicode-bom": [ + "error", + "never" + ], + "strict": [ + "error", + "never" + ], + "symbol-description": "error", +}; + +export { RULES as rules }; diff --git a/.eslint/configuration/style/config.mjs b/.eslint/configuration/style/config.mjs new file mode 100644 index 0000000..c197e7f --- /dev/null +++ b/.eslint/configuration/style/config.mjs @@ -0,0 +1,12 @@ +import { rules } from "./rules.mjs"; + +const CONFIGURATION = { + name: "stylistic", + files: [ + "**/*.mts", + ], + ignores: [], + rules: rules, +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/style/rules.mjs b/.eslint/configuration/style/rules.mjs new file mode 100644 index 0000000..10a9a0e --- /dev/null +++ b/.eslint/configuration/style/rules.mjs @@ -0,0 +1,796 @@ +const RULES = { + "@style/array-bracket-newline": [ + "error", + "consistent" + // unsupported combination + /* + { + "consistent": true, + "multiline": true + } + */ + ], + "@style/array-bracket-spacing": [ + "error", + "never", + { + "singleValue": false, + "objectsInArrays": false, + "arraysInArrays": false + } + ], + "@style/array-element-newline": [ + "error", + { + "consistent": true, + "multiline": true + } + ], + "@style/arrow-parens": [ + "error", + "always" + ], + "@style/arrow-spacing": [ + "error", + { + "before": true, + "after": true + } + ], + "@style/comma-style": [ + "error", + "last" + ], + "@style/computed-property-spacing": [ + "error", + "never" + ], + "@style/curly-newline": [ + "error", + { + "multiline": true, + "minElements": 1, + "consistent": true, + } + ], + "@style/dot-location": [ + "error", + "property" + ], + "@style/eol-last": [ + "error", + "always" + ], + "@style/function-call-argument-newline": [ + "error", + "consistent" + ], + "@style/func-call-spacing": [ + "error", + "never" + ], + "@style/function-paren-newline": [ + "error", + "multiline-arguments" + ], + "@style/generator-star-spacing": [ + "error", + { + "named": { + "before": false, + "after": true + }, + "anonymous": { + "before": false, + "after": false + }, + "method": { + "before": true, + "after": true + } + } + ], + "@style/jsx-quotes": [ + "error", + "prefer-double" + ], + "@style/linebreak-style": [ + "error", + "unix" + ], + "@style/line-comment-position": [ + "error", + { + "position": "above" + } + ], + "@style/max-len": [ + "error", + { + "code": 200, + "tabWidth": 4, + "comments": 300, + /*"ignorePattern": undefined*/ + "ignoreComments": false, + "ignoreTrailingComments": false, + "ignoreUrls": false, + "ignoreStrings": true, + "ignoreRegExpLiterals": true, + "ignoreTemplateLiterals": true + } + ], + "@style/max-statements-per-line": [ + "error", + { + "max": 1 + } + ], + "@style/multiline-ternary": [ + "error", + "always-multiline" + ], + "@style/new-parens": [ + "error", + "always" + ], + "@style/newline-per-chained-call": [ + "error", + { + /*"consistent": true,*/ + "ignoreChainWithDepth": 3 + } + ], + "@style/no-floating-decimal": "error", + "@style/no-mixed-spaces-and-tabs": [ + "error", + "smart-tabs" + ], + "@style/no-multi-spaces": [ + "error", + { + "includeTabs": false, + "ignoreEOLComments": false, + "exceptions": { + "Property": false, + "VariableDeclarator": false, + "ImportDeclaration": false + } + } + ], + "@style/no-multiple-empty-lines": [ + "error", + { + "max": 1, + "maxBOF": 0, + "maxEOF": 1 + } + ], + "@style/no-tabs": [ + "error", + { + "allowIndentationTabs": true + } + ], + "@style/no-trailing-spaces": [ + "error", + { + "skipBlankLines": false, + "ignoreComments": false + } + ], + "@style/no-whitespace-before-property": "error", + "@style/object-curly-newline": [ + "error", + { + "ObjectExpression": { + "consistent": true, + "multiline": true + }, + "ObjectPattern": { + "consistent": true, + "multiline": true + }, + "ImportDeclaration": { + "consistent": true, + "multiline": true + }, + "ExportDeclaration": { + "consistent": true, + "multiline": true + } + } + ], + "@style/object-property-newline": [ + "error", + { + "allowAllPropertiesOnSameLine": true + } + ], + "@style/operator-linebreak": [ + "error", + "before" + ], + "@style/padded-blocks": [ + "error", + "never" + ], + "@style/rest-spread-spacing": [ + "error", + "never" + ], + "@style/semi-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "@style/semi-style": [ + "error", + "last" + ], + "@style/space-in-parens": [ + "error", + "never" + ], + "@style/space-unary-ops": [ + "error", + { + "words": true, + "nonwords": false + } + ], + "@style/spaced-comment": [ + "error", + "always", + { + "line": { + "markers": [ + "/" + ], + "exceptions": [ + "-", + "=", + "*" + ] + }, + "block": { + "markers": [ + "*" + ], + "exceptions": [ + "*" + ], + "balanced": true + } + } + ], + "@style/switch-colon-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "@style/template-curly-spacing": [ + "error", + "never" + ], + "@style/template-tag-spacing": [ + "error", + "never" + ], + "@style/wrap-iife": [ + "error", + "inside", + { + "functionPrototypeMethods": true + } + ], + "@style/yield-star-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "@style/block-spacing": [ + // Possibly unnecessary + "error", + "always" + ], + "@style/brace-style": [ + "error", + "allman", + { + "allowSingleLine": true + } + ], + "@style/comma-dangle": [ + "error", + { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never", + "enums": "always-multiline", + "generics": "never", + "tuples": "always-multiline" + } + ], + "@style/comma-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "@style/function-call-spacing": [ + "error", + "never" + ], + "@style/implicit-arrow-linebreak": [ + "error", + "beside" + ], + "@style/key-spacing": [ + "error", + { + "beforeColon": false, + "afterColon": true, + "mode": "strict" + } + ], + "@style/keyword-spacing": [ + "error", + { + "before": true, + "after": true + /* + "before": false, + "after": true, + "overrides": { + "as": { + "before": true, + "after": true + }, + "in": { + "before": true, + "after": true + }, + "of": { + "before": true, + "after": true + } + } + */ + } + ], + "@style/lines-between-class-members": [ + "error", + "always", + { + "exceptAfterOverload": true, + "exceptAfterSingleLine": true + } + ], + "@style/member-delimiter-style": [ + "error", + { + "multilineDetection": "brackets", + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "semi", + "requireLast": false + } + } + ], + "@style/no-extra-parens": [ + "error", + "all", + { + "conditionalAssign": true, + "returnAssign": true, + "nestedBinaryExpressions": false, + "ternaryOperandBinaryExpressions": false, + "enforceForArrowConditionals": false, + "enforceForNewInMemberExpressions": false, + "enforceForFunctionPrototypeMethods": false, + /*"allowParensAfterCommentPattern": undefined,*/ + "ignoreJSX": "multi-line" + } + ], + "@style/no-extra-semi": "error", + "@style/object-curly-spacing": [ + "error", + "always", + { + "arraysInObjects": true, + "objectsInObjects": true + } + ], + "@style/padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": [ + "const", + "let" + ], + "next": "*" + }, + { + "blankLine": "any", + "prev": [ + "const", + "let" + ], + "next": [ + "const", + "let" + ] + }, + { + "blankLine": "always", + "prev": "*", + "next": [ + "type", + "interface", + "class", + "function", + "continue", + "return", + "throw", + "try", + "switch", + "case", + "default", + "if", + "do", + "while", + "export", + "multiline-expression", + "block", + "block-like", + "multiline-block-like" + ] + }, + { + "blankLine": "always", + "prev": [ + "type", + "interface", + "class", + "function", + "switch", + "require", + "import", + "multiline-expression", + "block", + "block-like", + "multiline-block-like" + ], + "next": "*" + }, + { + "blankLine": "any", + "prev": [ + "case" + ], + "next": [ + "case" + ] + }, + { + "blankLine": "any", + "prev": [ + "import" + ], + "next": [ + "import" + ] + }, + { + "blankLine": "any", + "prev": [ + "export" + ], + "next": [ + "export" + ] + } + ], + "@style/quote-props": [ + "error", + "consistent-as-needed" + ], + "@style/quotes": [ + "error", + "double", + { + "avoidEscape": true, + "allowTemplateLiterals": false + } + ], + "@style/semi": [ + "error", + "always", + { + "omitLastInOneLineBlock": false, + "omitLastInOneLineClassBody": false + } + ], + "@style/space-before-blocks": [ + // Possibly unnecessary + "error", + "always" + ], + "@style/space-before-function-paren": [ + "error", + { + "anonymous": "never", + "named": "never", + "asyncArrow": "always" + } + ], + "@style/space-infix-ops": [ + "error", + { + "int32Hint": false + } + ], + "@style/type-annotation-spacing": [ + "error", + { + "before": false, + "after": true, + "overrides": { + "arrow": { + "before": true, + "after": true + } + } + } + ], + "@style/type-named-tuple-spacing": "error", + "@ts/adjacent-overload-signatures": "error", + "@ts/class-literal-property-style": [ + "error", + "fields" + ], + "@ts/consistent-indexed-object-style": [ + "error", + "record" + ], + "@ts/consistent-type-definitions": [ + "error", + "interface" + ], + "@ts/dot-notation": [ + "error", + { + "allowKeywords": true, + /*"allowPattern": undefined,*/ + "allowPrivateClassPropertyAccess": false, + "allowProtectedClassPropertyAccess": false, + "allowIndexSignaturePropertyAccess": true + } + ], + "@ts/member-ordering": [ + "error", + { + "default": { + "order": "as-written", + "memberTypes": [ + // Signatures + "readonly-signature", + "signature", + "call-signature", + // Static fields + "public-static-readonly-field", + "public-static-field", + "protected-static-readonly-field", + "protected-static-field", + "private-static-readonly-field", + "private-static-field", + "#private-static-readonly-field", + "#private-static-field", + // Abstract fields + "public-abstract-readonly-field", + "public-abstract-field", + "protected-abstract-readonly-field", + "protected-abstract-field", + // Instance fields + "public-instance-readonly-field", + "public-instance-field", + "protected-instance-readonly-field", + "protected-instance-field", + "private-instance-readonly-field", + "private-instance-field", + // Decorated fields + "public-decorated-readonly-field", + "public-decorated-field", + "protected-decorated-readonly-field", + "protected-decorated-field", + "private-decorated-readonly-field", + "private-decorated-field", + // Default fields + "readonly-field", + "field", + // Static initialization + "static-initialization", + // Constructors + "public-constructor", + "protected-constructor", + "private-constructor", + "constructor", + // Static methods + "public-static-method", + "protected-static-method", + "private-static-method", + "#private-static-method", + // Abstract methods + "public-abstract-method", + "protected-abstract-method", + // Instance methods + "public-instance-method", + "protected-instance-method", + "private-instance-method", + // Decorated methods + "public-decorated-method", + "protected-decorated-method", + "private-decorated-method", + // Default methods + "method", + // Static getters and setters + [ + "public-static-get", + "public-static-set" + ], + [ + "protected-static-get", + "protected-static-set" + ], + [ + "private-static-get", + "private-static-set" + ], + // Abstract getters and setters + [ + "public-abstract-get", + "public-abstract-set" + ], + [ + "protected-abstract-get", + "protected-abstract-set" + ], + // Instance getters and setters + [ + "public-instance-get", + "public-instance-set" + ], + [ + "private-instance-get", + "private-instance-set" + ], + [ + "protected-instance-get", + "protected-instance-set" + ], + // Decorated getters and setters + [ + "public-decorated-get", + "public-decorated-set" + ], + [ + "protected-decorated-get", + "protected-decorated-set" + ], + [ + "private-decorated-get", + "private-decorated-set" + ], + // Default getters and setters + [ + "get", + "set" + ] + ] + } + } + ], + "@ts/method-signature-style": [ + "error", + "property" + ], + "@ts/prefer-for-of": "error", + "@ts/prefer-function-type": "error", + "arrow-body-style": [ + "error", + "always" + ], + "curly": [ + "error", + "all" + ], + "func-names": [ + "error", + "never", + { + "generators": "never" + } + ], + "grouped-accessor-pairs": [ + "error", + "getBeforeSet" + ], + "id-denylist": [ + "error", // enable rule + "cb", + "e", + "err", + "el", + "ev", + "ex", + "f", + "fn", + "fun", + "func", + "idx", + "k", + "o", + "obj", + "v", + "val" + ], + "id-length": [ + "error", + { + "min": 3, + "max": 100, + "properties": "always", + "exceptions": [ + "a", + "b", + "i", + "j", + "x", + "y", + "z", + "id", + "to", + "up", + "pH" + ] + } + ], + "logical-assignment-operators": [ + "error", + "never" + ], + "no-regex-spaces": "error", + "operator-assignment": [ + "error", + "never" + ], + "sort-imports": [ + "error", + { + "ignoreDeclarationSort": true + } + ], + "yoda": [ + "error", + "never", + { + "exceptRange": true, + "onlyEquality": false + } + ] +}; + +export { RULES as rules }; diff --git a/.eslint/configuration/test/config.mjs b/.eslint/configuration/test/config.mjs new file mode 100644 index 0000000..6f61c79 --- /dev/null +++ b/.eslint/configuration/test/config.mjs @@ -0,0 +1,14 @@ +import { rules } from "./rules.mjs"; + +const CONFIGURATION = { + name: "test", + files: [ + "*.spec.mts", + "test/**/*.mts", + "mock/**/*.mts", + ], + ignores: [], + rules: rules, +}; + +export { CONFIGURATION as configuration }; diff --git a/.eslint/configuration/test/rules.mjs b/.eslint/configuration/test/rules.mjs new file mode 100644 index 0000000..57e69d4 --- /dev/null +++ b/.eslint/configuration/test/rules.mjs @@ -0,0 +1,45 @@ +const RULES = { + "@style/generator-star-spacing": "off", + "@style/brace-style": "off", + "@style/curly-newline": "off", + "@style/max-statements-per-line": [ + "error", + { + "max": 2 + } + ], + "@ts/class-methods-use-this": "off", + "@ts/dot-notation": [ + "error", + { + "allowKeywords": true, + "allowPrivateClassPropertyAccess": true, + "allowProtectedClassPropertyAccess": true, + "allowIndexSignaturePropertyAccess": true + } + ], + "@ts/explicit-module-boundary-types": "off", + "@ts/no-confusing-void-expression": "off", + "@ts/no-empty-function": "off", + "@ts/no-extraneous-class": "off", + "@ts/no-floating-promises": "off", + "@ts/no-magic-numbers": "off", + "@ts/no-unsafe-assignment": "off", + "@ts/no-unsafe-argument": "off", + "@ts/no-unsafe-member-access": "off", + "@ts/no-unused-expressions": "off", + "@ts/no-unsafe-call": "off", + "@ts/only-throw-error": "off", + "@ts/prefer-promise-reject-errors": "off", + "@ts/unbound-method": "off", + "func-names": "off", + "func-style": "off", + "max-classes-per-file": "off", + "max-lines": "off", + "max-lines-per-function": "off", + "max-statements": "off", + "no-new": "off", + "symbol-description": "off", +}; + +export { RULES as rules }; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 567eac2..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true - }, - "extends": [ - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "indent": [ - "warn", - 4, - { - "SwitchCase": 1, - "FunctionExpression": { - "parameters": "first" - }, - "FunctionDeclaration": { - "parameters": "first" - } - } - ], - "linebreak-style": [ - "warn", - "unix" - ], - "quotes": [ - "warn", - "single" - ], - "semi": [ - "warn", - "never" - ], - "template-curly-spacing": [ - "warn", - "always" - ], - "brace-style": [ - "warn", - "allman", - { "allowSingleLine": true } - ], - "comma-dangle": [ - "warn", - "always-multiline" - ], - "key-spacing": [ - "warn", { - "beforeColon": false, - "afterColon": true, - "mode": "strict" - } - ], - "object-curly-spacing": [ - "warn", - "always", - { "objectsInObjects": true } - ], - "@typescript-eslint/no-unused-vars": [ - "warn", - { - "argsIgnorePattern": "^_", - "destructuredArrayIgnorePattern": "^_" - } - ] - } -} - diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4942844 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + groups: + dev-dependency: + dependency-type: "development" diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 2ae2af8..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Node.js CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js 16.x - uses: actions/setup-node@v3 - with: - node-version: 16.x - - run: npm install - - run: npm test diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 0000000..204b4d1 --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,189 @@ +name: Continuous Integration and Deployment + +run-name: Running CI/CD for event ${{github.event_name}} (Triggered by user ${{ github.triggering_actor }}) + +on: + push: + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + ts-files-changed: ${{ steps.parse-changed-files.outputs.ts-files-changed }} + require-spell-check: ${{ steps.parse-changed-files.outputs.require-spell-check }} + steps: + - name: Check out repository code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: PNPM cache configuration + uses: actions/cache@v4 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: | + pnpm install --frozen-lockfile + cd packages/mockingbird && pnpm install --frozen-lockfile && cd ../.. + + - name: Dependencies caching + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + spell-check-global: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - name: Dependencies cache unpacking + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Check spelling with CSpell + run: pnpm ci:spell:check + + ## Lua Engine + + lint-lua-engine: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - name: Dependencies cache unpacking + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Lint with ESlint + run: pnpm ci:lint + + typescript-syntax-check-lua-engine: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - name: Dependencies cache unpacking + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Check TypeScript syntax + run: pnpm ci:ts:check + + test-lua-engine: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - uses: actions/setup-node@v4 + with: + node-version: "22.x" + + - name: Dependencies cache unpacking + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Test with Node Test runner + run: pnpm ci:test:unit + + publish-lua-engine: + if: ${{ github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push' }} + # needs: + # [ + # spell-check-global, + # lint-lua-engine, + # typescript-syntax-check-lua-engine, + # ] + runs-on: ubuntu-latest + environment: npm + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22.x" + registry-url: "https://registry.npmjs.org" + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: false + + - name: Dependencies cache unpacking + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + + - name: Package build + run: pnpm ci:build + + - name: Publish package + id: package-version + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: pnpm ci:publish diff --git a/.swcrc b/.swcrc new file mode 100644 index 0000000..631d6ce --- /dev/null +++ b/.swcrc @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/swcrc.json", + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": false, + "decorators": false, + "dynamicImport": true + }, + "target": "esnext" + }, + "module": { + "type": "es6", + // These are defaults. + "strict": false, + "strictMode": true, + "lazy": false, + "noInterop": false + } +} diff --git a/.vscode/cspell.json b/.vscode/cspell.json new file mode 100644 index 0000000..27b6d09 --- /dev/null +++ b/.vscode/cspell.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "language": "en-GB", + "allowCompoundWords": true, + "dictionaries": [ + "softwareTerms", + "typescript", + "node", + "html", + "css", + "bash", + "npm" + ], + "words": [ + "Vitruvius", + "architectura", + ], + "ignoreWords": [ + "Lebacq", + "Zamralik" + ], + "flagWords": [ + "hte" + ], + "ignorePaths": [ + ".eslintrc.json", + ".git/**", + ".github/CODEOWNERS", + ".gitignore", + ".mocharc.json", + "**/*.log", + "**/node_modules/**", + "build/", + "CHANGELOG.md", + "package.json", + "pipeline.yml", + "pnpm-lock.yaml", + "reports/", + "tmp/", + "tsconfig.json", + "**/test.mts", + "**/coverage/**" + ] +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..33aa85d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,3 @@ +import { configurations } from "./.eslint/configuration/_index.mjs"; + +export default configurations; diff --git a/package.json b/package.json index 34d0f92..adfe685 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "lua-engine", - "version": "1.0.0", + "version": "0.1.0", "description": "A TypeScript implementation of a Lua interpreter", - "license": "ISC", + "license": "BSD-2-Clause", "author": { "name": "VitruviusLabs" }, @@ -29,18 +29,42 @@ "node": ">=18.0.0" }, "type": "module", + "exports": { + ".": { + "import": "./build/esm/_index.mjs", + "types": "./build/types/_index.d.mts" + } + }, "files": [ - "src/index.mts" + "build" ], "scripts": { - "ts:check": "tsc -p tsconfig.json --noEmit" + "clean": "rm -rf reports coverage build dist lib .eslintcache", + "compile": "tsc -p tsconfig.build.json", + "build": "pnpm clean && pnpm compile", + "eslint:check": "eslint", + "eslint:fix": "eslint --fix", + "ts:check": "tsc -p tsconfig.json --noEmit", + "spell:check": "cspell -c ./.vscode/cspell.json .", + "ci:lint": "pnpm eslint:check", + "ci:ts:check": "pnpm ts:check", + "ci:spell:check": "pnpm spell:check", + "ci:publish": "pnpm publish --access public", + "ci:publish:dry": "pnpm publish --access public --dry-run --no-git-checks", + "ci:build": "pnpm build" }, "dependencies": { "@vitruvius-labs/ts-predicate": "^7.0.0" }, "devDependencies": { - "@types/node": "^22.14.1", + "@stylistic/eslint-plugin": "^5.7.0", + "@types/node": "^25.0.9", + "@swc/core": "^1.11.21", + "cspell": "^9.6.0", + "eslint": "^9.25.0", + "eslint-plugin-perfectionist": "^5.3.1", "tsx": "^4.19.3", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "typescript-eslint": "^8.30.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dacb11e..4b9d94d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,18 +12,258 @@ importers: specifier: ^7.0.0 version: 7.0.0 devDependencies: + '@stylistic/eslint-plugin': + specifier: ^5.7.0 + version: 5.7.0(eslint@9.39.2) + '@swc/core': + specifier: ^1.11.21 + version: 1.15.10 '@types/node': - specifier: ^22.14.1 - version: 22.19.6 + specifier: ^25.0.9 + version: 25.0.9 + cspell: + specifier: ^9.6.0 + version: 9.6.0 + eslint: + specifier: ^9.25.0 + version: 9.39.2 + eslint-plugin-perfectionist: + specifier: ^5.3.1 + version: 5.3.1(eslint@9.39.2)(typescript@5.9.3) tsx: specifier: ^4.19.3 version: 4.21.0 typescript: specifier: ^5.8.3 version: 5.9.3 + typescript-eslint: + specifier: ^8.30.1 + version: 8.53.1(eslint@9.39.2)(typescript@5.9.3) packages: + '@cspell/cspell-bundled-dicts@9.6.0': + resolution: {integrity: sha512-gLNe9bB+5gMsTEhR9YPE0Wt122HR2EV+Q1j9W+MbwbeBJmpTWrgAP1ZdpvHOg+6LF6x/bD/EC9HfWdd/om8wXA==} + engines: {node: '>=20'} + + '@cspell/cspell-json-reporter@9.6.0': + resolution: {integrity: sha512-5sY1lgAXS5xEOsjT5rREMADj7pHIt56XOL7xR80nNl0TwlpZbeBHhoB2aH5sirVTeodJFN5iraXNbVOYPPupPw==} + engines: {node: '>=20'} + + '@cspell/cspell-pipe@9.6.0': + resolution: {integrity: sha512-YNuY8NNXfE+8Qzknm2ps6QbrZLZu6rSZTZr3dYW3K6TK7+IFVlJ6e2Z9iKJTqp6aZ4AGU/r9QYGmNX4Oq4gZ0A==} + engines: {node: '>=20'} + + '@cspell/cspell-resolver@9.6.0': + resolution: {integrity: sha512-Gb2UWNmRpTOQGpYL4Q/LMw+b50KcRZcf/wJg6w0Yl3IT+F/uDNhNh1f5rHuTyGsbMsMxHJhsb2AoP+73GlbIfw==} + engines: {node: '>=20'} + + '@cspell/cspell-service-bus@9.6.0': + resolution: {integrity: sha512-DCuKKkySTEB8MPLTdoPMdmakYcx7XCsHz1YEMbzOcLqJCxXsRlRZg4qE9kRBee/2QY7eYA2kaYNgn/TDMooa4g==} + engines: {node: '>=20'} + + '@cspell/cspell-types@9.6.0': + resolution: {integrity: sha512-JTqrD47tV+rWc1y2W8T0NTfWLQMlSWX4OF64/Jf3WbsOD+4UXVIfjRlzPry7+1Zekm6pa38+23jkDBytYpu8yw==} + engines: {node: '>=20'} + + '@cspell/dict-ada@4.1.1': + resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} + + '@cspell/dict-al@1.1.1': + resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} + + '@cspell/dict-aws@4.0.17': + resolution: {integrity: sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==} + + '@cspell/dict-bash@4.2.2': + resolution: {integrity: sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==} + + '@cspell/dict-companies@3.2.10': + resolution: {integrity: sha512-bJ1qnO1DkTn7JYGXvxp8FRQc4yq6tRXnrII+jbP8hHmq5TX5o1Wu+rdfpoUQaMWTl6balRvcMYiINDesnpR9Bw==} + + '@cspell/dict-cpp@7.0.2': + resolution: {integrity: sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==} + + '@cspell/dict-cryptocurrencies@5.0.5': + resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} + + '@cspell/dict-csharp@4.0.8': + resolution: {integrity: sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==} + + '@cspell/dict-css@4.0.19': + resolution: {integrity: sha512-VYHtPnZt/Zd/ATbW3rtexWpBnHUohUrQOHff/2JBhsVgxOrksAxJnLAO43Q1ayLJBJUUwNVo+RU0sx0aaysZfg==} + + '@cspell/dict-dart@2.3.2': + resolution: {integrity: sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==} + + '@cspell/dict-data-science@2.0.13': + resolution: {integrity: sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==} + + '@cspell/dict-django@4.1.6': + resolution: {integrity: sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==} + + '@cspell/dict-docker@1.1.17': + resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} + + '@cspell/dict-dotnet@5.0.11': + resolution: {integrity: sha512-LSVKhpFf/ASTWJcfYeS0Sykcl1gVMsv2Z5Eo0TnTMSTLV3738HH+66pIsjUTChqU6SF3gKPuCe6EOaRYqb/evA==} + + '@cspell/dict-elixir@4.0.8': + resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} + + '@cspell/dict-en-common-misspellings@2.1.11': + resolution: {integrity: sha512-2jcY494If1udvzd7MT2z/QH/RACUo/I02vIY4ttNdZhgYvUmRKhg8OBdrbzYo0lJOcc7XUb8rhIFQRHzxOSVeA==} + + '@cspell/dict-en-gb-mit@3.1.16': + resolution: {integrity: sha512-4PPdapCJslytxAVJu35Mv97qDyGmAQxtDE790T2bWNhcqN6gvRVAc/eTRaXkUIf21q1xCxxNNqpH4VfMup69rQ==} + + '@cspell/dict-en_us@4.4.27': + resolution: {integrity: sha512-0y4vH2i5cFmi8sxkc4OlD2IlnqDznOtKczm4h6jA288g5VVrm3bhkYK6vcB8b0CoRKtYWKet4VEmHBP1yI+Qfw==} + + '@cspell/dict-filetypes@3.0.15': + resolution: {integrity: sha512-uDMeqYlLlK476w/muEFQGBy9BdQWS0mQ7BJiy/iQv5XUWZxE2O54ZQd9nW8GyQMzAgoyg5SG4hf9l039Qt66oA==} + + '@cspell/dict-flutter@1.1.1': + resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} + + '@cspell/dict-fonts@4.0.5': + resolution: {integrity: sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==} + + '@cspell/dict-fsharp@1.1.1': + resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} + + '@cspell/dict-fullstack@3.2.7': + resolution: {integrity: sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==} + + '@cspell/dict-gaming-terms@1.1.2': + resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} + + '@cspell/dict-git@3.0.7': + resolution: {integrity: sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==} + + '@cspell/dict-golang@6.0.26': + resolution: {integrity: sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==} + + '@cspell/dict-google@1.0.9': + resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} + + '@cspell/dict-haskell@4.0.6': + resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} + + '@cspell/dict-html-symbol-entities@4.0.5': + resolution: {integrity: sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==} + + '@cspell/dict-html@4.0.14': + resolution: {integrity: sha512-2bf7n+kS92g+cMKV0wr9o/Oq9n8JzU7CcrB96gIh2GHgnF+0xDOqO2W/1KeFAqOfqosoOVE48t+4dnEMkkoJ2Q==} + + '@cspell/dict-java@5.0.12': + resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} + + '@cspell/dict-julia@1.1.1': + resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} + + '@cspell/dict-k8s@1.0.12': + resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} + + '@cspell/dict-kotlin@1.1.1': + resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} + + '@cspell/dict-latex@4.0.4': + resolution: {integrity: sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==} + + '@cspell/dict-lorem-ipsum@4.0.5': + resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} + + '@cspell/dict-lua@4.0.8': + resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} + + '@cspell/dict-makefile@1.0.5': + resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} + + '@cspell/dict-markdown@2.0.14': + resolution: {integrity: sha512-uLKPNJsUcumMQTsZZgAK9RgDLyQhUz/uvbQTEkvF/Q4XfC1i/BnA8XrOrd0+Vp6+tPOKyA+omI5LRWfMu5K/Lw==} + peerDependencies: + '@cspell/dict-css': ^4.0.19 + '@cspell/dict-html': ^4.0.14 + '@cspell/dict-html-symbol-entities': ^4.0.5 + '@cspell/dict-typescript': ^3.2.3 + + '@cspell/dict-monkeyc@1.0.12': + resolution: {integrity: sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==} + + '@cspell/dict-node@5.0.8': + resolution: {integrity: sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==} + + '@cspell/dict-npm@5.2.29': + resolution: {integrity: sha512-ZAef8JpYmbuHFT1zekj/YyImLPvZevjECw663EmG5GPePyNo4AfH8Dd2fFhaOyQ3P5I5LrkAhGwypnOfUxcssw==} + + '@cspell/dict-php@4.1.1': + resolution: {integrity: sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==} + + '@cspell/dict-powershell@5.0.15': + resolution: {integrity: sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==} + + '@cspell/dict-public-licenses@2.0.15': + resolution: {integrity: sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==} + + '@cspell/dict-python@4.2.25': + resolution: {integrity: sha512-hDdN0YhKgpbtZVRjQ2c8jk+n0wQdidAKj1Fk8w7KEHb3YlY5uPJ0mAKJk7AJKPNLOlILoUmN+HAVJz+cfSbWYg==} + + '@cspell/dict-r@2.1.1': + resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} + + '@cspell/dict-ruby@5.1.0': + resolution: {integrity: sha512-9PJQB3cfkBULrMLp5kSAcFPpzf8oz9vFN+QYZABhQwWkGbuzCIXSorHrmWSASlx4yejt3brjaWS57zZ/YL5ZQQ==} + + '@cspell/dict-rust@4.1.1': + resolution: {integrity: sha512-fXiXnZH0wOaEVTKFRNaz6TsUGhuB8dAT0ubYkDNzRQCaV5JGSOebGb1v2x5ZrOSVp+moxWM/vdBfiNU6KOEaFQ==} + + '@cspell/dict-scala@5.0.9': + resolution: {integrity: sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==} + + '@cspell/dict-shell@1.1.2': + resolution: {integrity: sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==} + + '@cspell/dict-software-terms@5.1.20': + resolution: {integrity: sha512-TEk1xHvetTI4pv7Vzje1D322m6QEjaH2P6ucOOf6q7EJCppQIdC0lZSXkgHJAFU5HGSvEXSzvnVeW2RHW86ziQ==} + + '@cspell/dict-sql@2.2.1': + resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} + + '@cspell/dict-svelte@1.0.7': + resolution: {integrity: sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==} + + '@cspell/dict-swift@2.0.6': + resolution: {integrity: sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==} + + '@cspell/dict-terraform@1.1.3': + resolution: {integrity: sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==} + + '@cspell/dict-typescript@3.2.3': + resolution: {integrity: sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==} + + '@cspell/dict-vue@3.0.5': + resolution: {integrity: sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==} + + '@cspell/dict-zig@1.0.0': + resolution: {integrity: sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==} + + '@cspell/dynamic-import@9.6.0': + resolution: {integrity: sha512-Lkn82wyGj2ltxeYfH2bEjshdes1fx3ouYUZxeW5i6SBBvEVJoSmr43AygI8A317UMIQxVj59qVBorrtGYcRI1w==} + engines: {node: '>=20'} + + '@cspell/filetypes@9.6.0': + resolution: {integrity: sha512-CaWyk5j20H6sr+HCArtUY95jUQb7A/6W0GC4B4umnqoWvgqwR72duowLFa+w1K2C7tZg3GoV4Wf2cUn9jjt5FA==} + engines: {node: '>=20'} + + '@cspell/strong-weak-map@9.6.0': + resolution: {integrity: sha512-9g8LCLv/2RrprGeGnFAaBETWq7ESnBcoMbvgNu+vZE58iF+pbFvP0qGgKvVeKEEpc2LZhNuHLsUH37MUS6DOQg==} + engines: {node: '>=20'} + + '@cspell/url@9.6.0': + resolution: {integrity: sha512-257WOxh9vOYHAVgBNXRCdLEd+ldzlVbzcc9u+6DYoCDCNGe0OvOWOGsAfnUbMc9xEw48XgBlDYgOlPbjWGLOTg==} + engines: {node: '>=20'} + '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} @@ -180,143 +420,1404 @@ packages: cpu: [x64] os: [win32] - '@types/node@22.19.6': - resolution: {integrity: sha512-qm+G8HuG6hOHQigsi7VGuLjUVu6TtBo/F05zvX04Mw2uCg9Dv0Qxy3Qw7j41SidlTcl5D/5yg0SEZqOB+EqZnQ==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@stylistic/eslint-plugin@5.7.0': + resolution: {integrity: sha512-PsSugIf9ip1H/mWKj4bi/BlEoerxXAda9ByRFsYuwsmr6af9NxJL0AaiNXs8Le7R21QR5KMiD/KdxZZ71LjAxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=9.0.0' + + '@swc/core-darwin-arm64@1.15.10': + resolution: {integrity: sha512-U72pGqmJYbjrLhMndIemZ7u9Q9owcJczGxwtfJlz/WwMaGYAV/g4nkGiUVk/+QSX8sFCAjanovcU1IUsP2YulA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.10': + resolution: {integrity: sha512-NZpDXtwHH083L40xdyj1sY31MIwLgOxKfZEAGCI8xHXdHa+GWvEiVdGiu4qhkJctoHFzAEc7ZX3GN5phuJcPuQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.10': + resolution: {integrity: sha512-ioieF5iuRziUF1HkH1gg1r93e055dAdeBAPGAk40VjqpL5/igPJ/WxFHGvc6WMLhUubSJI4S0AiZAAhEAp1jDg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.10': + resolution: {integrity: sha512-tD6BClOrxSsNus9cJL7Gxdv7z7Y2hlyvZd9l0NQz+YXzmTWqnfzLpg16ovEI7gknH2AgDBB5ywOsqu8hUgSeEQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.10': + resolution: {integrity: sha512-4uAHO3nbfbrTcmO/9YcVweTQdx5fN3l7ewwl5AEK4yoC4wXmoBTEPHAVdKNe4r9+xrTgd4BgyPsy0409OjjlMw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.10': + resolution: {integrity: sha512-W0h9ONNw1pVIA0cN7wtboOSTl4Jk3tHq+w2cMPQudu9/+3xoCxpFb9ZdehwCAk29IsvdWzGzY6P7dDVTyFwoqg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.10': + resolution: {integrity: sha512-XQNZlLZB62S8nAbw7pqoqwy91Ldy2RpaMRqdRN3T+tAg6Xg6FywXRKCsLh6IQOadr4p1+lGnqM/Wn35z5a/0Vw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.10': + resolution: {integrity: sha512-qnAGrRv5Nj/DATxAmCnJQRXXQqnJwR0trxLndhoHoxGci9MuguNIjWahS0gw8YZFjgTinbTxOwzatkoySihnmw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.10': + resolution: {integrity: sha512-i4X/q8QSvzVlaRtv1xfnfl+hVKpCfiJ+9th484rh937fiEZKxZGf51C+uO0lfKDP1FfnT6C1yBYwHy7FLBVXFw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.10': + resolution: {integrity: sha512-HvY8XUFuoTXn6lSccDLYFlXv1SU/PzYi4PyUqGT++WfTnbw/68N/7BdUZqglGRwiSqr0qhYt/EhmBpULj0J9rA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.10': + resolution: {integrity: sha512-udNofxftduMUEv7nqahl2nvodCiCDQ4Ge0ebzsEm6P8s0RC2tBM0Hqx0nNF5J/6t9uagFJyWIDjXy3IIWMHDJw==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@25.0.9': + resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==} + + '@typescript-eslint/eslint-plugin@8.53.1': + resolution: {integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.53.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.53.1': + resolution: {integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.53.1': + resolution: {integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.53.1': + resolution: {integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.53.1': + resolution: {integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.53.1': + resolution: {integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.53.1': + resolution: {integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.1': + resolution: {integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.1': + resolution: {integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.53.1': + resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitruvius-labs/ts-predicate@7.0.0': resolution: {integrity: sha512-lnWz0W2BeI/LZh6AGrOhC+SrQ74IeG2hUuT8BUXN9Q0nNj1cZ63e3a/uFd5/guqAqowQaNyHx4mP61CW2DybjQ==} engines: {node: '>=18.0.0'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk-template@1.1.2: + resolution: {integrity: sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==} + engines: {node: '>=14.16'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + clear-module@4.1.2: + resolution: {integrity: sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==} + engines: {node: '>=8'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + + comment-json@4.5.1: + resolution: {integrity: sha512-taEtr3ozUmOB7it68Jll7s0Pwm+aoiHyXKrEC8SEodL4rNpdfDLqa7PfBlrgFoCNNdR8ImL+muti5IGvktJAAg==} + engines: {node: '>= 6'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cspell-config-lib@9.6.0: + resolution: {integrity: sha512-5ztvheawkmFXNHGN82iOOntU3T5mmlQBP/plgoKdBZ6+lSYrOJLkOyqxYyi7MrUBDtWrXPzFllkBrPNRDlbX/A==} + engines: {node: '>=20'} + + cspell-dictionary@9.6.0: + resolution: {integrity: sha512-wW0m1kLrbK6bRY/GrLUGKUUJ1Z4ZUgIb8LD4zNaECcvGviv9V7VcR3mEwUip3ZjoHa3ClzEoWgQ9gXrtac80Wg==} + engines: {node: '>=20'} + + cspell-gitignore@9.6.0: + resolution: {integrity: sha512-8GfmJuRBBvibyPHnNE2wYJAiQ/ceDYLD1X1sUQaCyj6hPMR7ChJiVhFPtS11hMqkjZ46OBMYTMGWqO792L9fEQ==} + engines: {node: '>=20'} + hasBin: true + + cspell-glob@9.6.0: + resolution: {integrity: sha512-KmEbKN0qdEamsEYbkFu7zjLYfw3hMmn9kmeh94IHr2kq6vWq5vNP5l1BuqmrUeFZlbNd07vj08IKAZHYsoGheQ==} + engines: {node: '>=20'} + + cspell-grammar@9.6.0: + resolution: {integrity: sha512-jZVIM5/3eB9rWURDq+VXdYip+DmPuFzO+bqaRtzqT8w6YoOIGYbiIxdwvyyA9xdH7SmW8uqHJP5x4pzZju1lNQ==} + engines: {node: '>=20'} + hasBin: true + + cspell-io@9.6.0: + resolution: {integrity: sha512-wZuZzKOYIb698kVEINYjGaNFQu+AFZ945TORM3hapmPjez+vsHwl8m/pPpCHeGMpQtHMEDkX84AbQ7R55MRIwg==} + engines: {node: '>=20'} + + cspell-lib@9.6.0: + resolution: {integrity: sha512-m9rIv8hkQ3Dio4s80HQbM9cdxENcd6pS8j2AHWL50OSjJf3Xhw6/wMrIAGbwLHP15K6QZVU2eJ/abCzIJwjA4w==} + engines: {node: '>=20'} + + cspell-trie-lib@9.6.0: + resolution: {integrity: sha512-L7GSff5F9cF60QT78WsebVlb3sppi6jbvTHwsw7WF1jUN/ioAo7OzBYtYB7xkYeejcdVEpqfvf/ZOXPDp8x2Wg==} + engines: {node: '>=20'} + peerDependencies: + '@cspell/cspell-types': 9.6.0 + + cspell@9.6.0: + resolution: {integrity: sha512-Mpf0oT2KAHTIb3YPAXWhW64/4CZKW5Lka4j1YxCLK3jM3nenmIsY/ocrJvqCMF4+1eejRF0N55sT3XmrijI5YQ==} + engines: {node: '>=20'} + hasBin: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-perfectionist@5.3.1: + resolution: {integrity: sha512-v8kAP8TarQYqDC4kxr343ZNi++/oOlBnmWovsUZpbJ7A/pq1VHGlgsf/fDh4CdEvEstzkrc8NLvoVKtfpsC4oA==} + engines: {node: ^20.0.0 || >=22.0.0} + peerDependencies: + eslint: '>=8.45.0' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.0: + resolution: {integrity: sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.1.0: + resolution: {integrity: sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-equals@6.0.0: + resolution: {integrity: sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==} + engines: {node: '>=6.0.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensequence@8.0.8: + resolution: {integrity: sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==} + engines: {node: '>=20'} + get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} - tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} - engines: {node: '>=18.0.0'} - hasBin: true + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} -snapshots: + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - '@esbuild/aix-ppc64@0.27.2': - optional: true + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - '@esbuild/android-arm64@0.27.2': - optional: true + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - '@esbuild/android-arm@0.27.2': - optional: true + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} - '@esbuild/android-x64@0.27.2': - optional: true + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} - '@esbuild/darwin-arm64@0.27.2': - optional: true + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - '@esbuild/darwin-x64@0.27.2': - optional: true + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - '@esbuild/freebsd-arm64@0.27.2': - optional: true + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} - '@esbuild/freebsd-x64@0.27.2': - optional: true + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - '@esbuild/linux-arm64@0.27.2': - optional: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - '@esbuild/linux-arm@0.27.2': - optional: true + natural-orderby@5.0.0: + resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} + engines: {node: '>=18'} - '@esbuild/linux-ia32@0.27.2': - optional: true + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} - '@esbuild/linux-loong64@0.27.2': - optional: true + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} - '@esbuild/linux-mips64el@0.27.2': - optional: true + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} - '@esbuild/linux-ppc64@0.27.2': - optional: true + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} - '@esbuild/linux-riscv64@0.27.2': - optional: true + parent-module@2.0.0: + resolution: {integrity: sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==} + engines: {node: '>=8'} - '@esbuild/linux-s390x@0.27.2': - optional: true + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} - '@esbuild/linux-x64@0.27.2': - optional: true + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} - '@esbuild/netbsd-arm64@0.27.2': - optional: true + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} - '@esbuild/netbsd-x64@0.27.2': - optional: true + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} - '@esbuild/openbsd-arm64@0.27.2': - optional: true + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} - '@esbuild/openbsd-x64@0.27.2': - optional: true + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} - '@esbuild/openharmony-arm64@0.27.2': - optional: true + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} - '@esbuild/sunos-x64@0.27.2': - optional: true + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - '@esbuild/win32-arm64@0.27.2': - optional: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true - '@esbuild/win32-ia32@0.27.2': - optional: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} - '@esbuild/win32-x64@0.27.2': - optional: true + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} - '@types/node@22.19.6': - dependencies: - undici-types: 6.21.0 + smol-toml@1.6.0: + resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + engines: {node: '>= 18'} - '@vitruvius-labs/ts-predicate@7.0.0': {} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} - esbuild@0.27.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.53.1: + resolution: {integrity: sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@cspell/cspell-bundled-dicts@9.6.0': + dependencies: + '@cspell/dict-ada': 4.1.1 + '@cspell/dict-al': 1.1.1 + '@cspell/dict-aws': 4.0.17 + '@cspell/dict-bash': 4.2.2 + '@cspell/dict-companies': 3.2.10 + '@cspell/dict-cpp': 7.0.2 + '@cspell/dict-cryptocurrencies': 5.0.5 + '@cspell/dict-csharp': 4.0.8 + '@cspell/dict-css': 4.0.19 + '@cspell/dict-dart': 2.3.2 + '@cspell/dict-data-science': 2.0.13 + '@cspell/dict-django': 4.1.6 + '@cspell/dict-docker': 1.1.17 + '@cspell/dict-dotnet': 5.0.11 + '@cspell/dict-elixir': 4.0.8 + '@cspell/dict-en-common-misspellings': 2.1.11 + '@cspell/dict-en-gb-mit': 3.1.16 + '@cspell/dict-en_us': 4.4.27 + '@cspell/dict-filetypes': 3.0.15 + '@cspell/dict-flutter': 1.1.1 + '@cspell/dict-fonts': 4.0.5 + '@cspell/dict-fsharp': 1.1.1 + '@cspell/dict-fullstack': 3.2.7 + '@cspell/dict-gaming-terms': 1.1.2 + '@cspell/dict-git': 3.0.7 + '@cspell/dict-golang': 6.0.26 + '@cspell/dict-google': 1.0.9 + '@cspell/dict-haskell': 4.0.6 + '@cspell/dict-html': 4.0.14 + '@cspell/dict-html-symbol-entities': 4.0.5 + '@cspell/dict-java': 5.0.12 + '@cspell/dict-julia': 1.1.1 + '@cspell/dict-k8s': 1.0.12 + '@cspell/dict-kotlin': 1.1.1 + '@cspell/dict-latex': 4.0.4 + '@cspell/dict-lorem-ipsum': 4.0.5 + '@cspell/dict-lua': 4.0.8 + '@cspell/dict-makefile': 1.0.5 + '@cspell/dict-markdown': 2.0.14(@cspell/dict-css@4.0.19)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.14)(@cspell/dict-typescript@3.2.3) + '@cspell/dict-monkeyc': 1.0.12 + '@cspell/dict-node': 5.0.8 + '@cspell/dict-npm': 5.2.29 + '@cspell/dict-php': 4.1.1 + '@cspell/dict-powershell': 5.0.15 + '@cspell/dict-public-licenses': 2.0.15 + '@cspell/dict-python': 4.2.25 + '@cspell/dict-r': 2.1.1 + '@cspell/dict-ruby': 5.1.0 + '@cspell/dict-rust': 4.1.1 + '@cspell/dict-scala': 5.0.9 + '@cspell/dict-shell': 1.1.2 + '@cspell/dict-software-terms': 5.1.20 + '@cspell/dict-sql': 2.2.1 + '@cspell/dict-svelte': 1.0.7 + '@cspell/dict-swift': 2.0.6 + '@cspell/dict-terraform': 1.1.3 + '@cspell/dict-typescript': 3.2.3 + '@cspell/dict-vue': 3.0.5 + '@cspell/dict-zig': 1.0.0 + + '@cspell/cspell-json-reporter@9.6.0': + dependencies: + '@cspell/cspell-types': 9.6.0 + + '@cspell/cspell-pipe@9.6.0': {} + + '@cspell/cspell-resolver@9.6.0': + dependencies: + global-directory: 4.0.1 + + '@cspell/cspell-service-bus@9.6.0': {} + + '@cspell/cspell-types@9.6.0': {} + + '@cspell/dict-ada@4.1.1': {} + + '@cspell/dict-al@1.1.1': {} + + '@cspell/dict-aws@4.0.17': {} + + '@cspell/dict-bash@4.2.2': + dependencies: + '@cspell/dict-shell': 1.1.2 + + '@cspell/dict-companies@3.2.10': {} + + '@cspell/dict-cpp@7.0.2': {} + + '@cspell/dict-cryptocurrencies@5.0.5': {} + + '@cspell/dict-csharp@4.0.8': {} + + '@cspell/dict-css@4.0.19': {} + + '@cspell/dict-dart@2.3.2': {} + + '@cspell/dict-data-science@2.0.13': {} + + '@cspell/dict-django@4.1.6': {} + + '@cspell/dict-docker@1.1.17': {} + + '@cspell/dict-dotnet@5.0.11': {} + + '@cspell/dict-elixir@4.0.8': {} + + '@cspell/dict-en-common-misspellings@2.1.11': {} + + '@cspell/dict-en-gb-mit@3.1.16': {} + + '@cspell/dict-en_us@4.4.27': {} + + '@cspell/dict-filetypes@3.0.15': {} + + '@cspell/dict-flutter@1.1.1': {} + + '@cspell/dict-fonts@4.0.5': {} + + '@cspell/dict-fsharp@1.1.1': {} + + '@cspell/dict-fullstack@3.2.7': {} + + '@cspell/dict-gaming-terms@1.1.2': {} + + '@cspell/dict-git@3.0.7': {} + + '@cspell/dict-golang@6.0.26': {} + + '@cspell/dict-google@1.0.9': {} + + '@cspell/dict-haskell@4.0.6': {} + + '@cspell/dict-html-symbol-entities@4.0.5': {} + + '@cspell/dict-html@4.0.14': {} + + '@cspell/dict-java@5.0.12': {} + + '@cspell/dict-julia@1.1.1': {} + + '@cspell/dict-k8s@1.0.12': {} + + '@cspell/dict-kotlin@1.1.1': {} + + '@cspell/dict-latex@4.0.4': {} + + '@cspell/dict-lorem-ipsum@4.0.5': {} + + '@cspell/dict-lua@4.0.8': {} + + '@cspell/dict-makefile@1.0.5': {} + + '@cspell/dict-markdown@2.0.14(@cspell/dict-css@4.0.19)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.14)(@cspell/dict-typescript@3.2.3)': + dependencies: + '@cspell/dict-css': 4.0.19 + '@cspell/dict-html': 4.0.14 + '@cspell/dict-html-symbol-entities': 4.0.5 + '@cspell/dict-typescript': 3.2.3 + + '@cspell/dict-monkeyc@1.0.12': {} + + '@cspell/dict-node@5.0.8': {} + + '@cspell/dict-npm@5.2.29': {} + + '@cspell/dict-php@4.1.1': {} + + '@cspell/dict-powershell@5.0.15': {} + + '@cspell/dict-public-licenses@2.0.15': {} + + '@cspell/dict-python@4.2.25': + dependencies: + '@cspell/dict-data-science': 2.0.13 + + '@cspell/dict-r@2.1.1': {} + + '@cspell/dict-ruby@5.1.0': {} + + '@cspell/dict-rust@4.1.1': {} + + '@cspell/dict-scala@5.0.9': {} + + '@cspell/dict-shell@1.1.2': {} + + '@cspell/dict-software-terms@5.1.20': {} + + '@cspell/dict-sql@2.2.1': {} + + '@cspell/dict-svelte@1.0.7': {} + + '@cspell/dict-swift@2.0.6': {} + + '@cspell/dict-terraform@1.1.3': {} + + '@cspell/dict-typescript@3.2.3': {} + + '@cspell/dict-vue@3.0.5': {} + + '@cspell/dict-zig@1.0.0': {} + + '@cspell/dynamic-import@9.6.0': + dependencies: + '@cspell/url': 9.6.0 + import-meta-resolve: 4.2.0 + + '@cspell/filetypes@9.6.0': {} + + '@cspell/strong-weak-map@9.6.0': {} + + '@cspell/url@9.6.0': {} + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@stylistic/eslint-plugin@5.7.0(eslint@9.39.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/types': 8.53.1 + eslint: 9.39.2 + eslint-visitor-keys: 5.0.0 + espree: 11.1.0 + estraverse: 5.3.0 + picomatch: 4.0.3 + + '@swc/core-darwin-arm64@1.15.10': + optional: true + + '@swc/core-darwin-x64@1.15.10': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.10': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.10': + optional: true + + '@swc/core-linux-arm64-musl@1.15.10': + optional: true + + '@swc/core-linux-x64-gnu@1.15.10': + optional: true + + '@swc/core-linux-x64-musl@1.15.10': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.10': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.10': + optional: true + + '@swc/core-win32-x64-msvc@1.15.10': + optional: true + + '@swc/core@1.15.10': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.10 + '@swc/core-darwin-x64': 1.15.10 + '@swc/core-linux-arm-gnueabihf': 1.15.10 + '@swc/core-linux-arm64-gnu': 1.15.10 + '@swc/core-linux-arm64-musl': 1.15.10 + '@swc/core-linux-x64-gnu': 1.15.10 + '@swc/core-linux-x64-musl': 1.15.10 + '@swc/core-win32-arm64-msvc': 1.15.10 + '@swc/core-win32-ia32-msvc': 1.15.10 + '@swc/core-win32-x64-msvc': 1.15.10 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@25.0.9': + dependencies: + undici-types: 7.16.0 + + '@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/type-utils': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.1 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.1 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) + '@typescript-eslint/types': 8.53.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.1': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + + '@typescript-eslint/tsconfig-utils@8.53.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.53.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.1': {} + + '@typescript-eslint/typescript-estree@8.53.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/visitor-keys': 8.53.1 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.53.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.53.1 + '@typescript-eslint/types': 8.53.1 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.53.1': + dependencies: + '@typescript-eslint/types': 8.53.1 + eslint-visitor-keys: 4.2.1 + + '@vitruvius-labs/ts-predicate@7.0.0': {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-timsort@1.0.3: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + callsites@3.1.0: {} + + chalk-template@1.1.2: + dependencies: + chalk: 5.6.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + clear-module@4.1.2: + dependencies: + parent-module: 2.0.0 + resolve-from: 5.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@14.0.2: {} + + comment-json@4.5.1: + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + + concat-map@0.0.1: {} + + core-util-is@1.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cspell-config-lib@9.6.0: + dependencies: + '@cspell/cspell-types': 9.6.0 + comment-json: 4.5.1 + smol-toml: 1.6.0 + yaml: 2.8.2 + + cspell-dictionary@9.6.0: + dependencies: + '@cspell/cspell-pipe': 9.6.0 + '@cspell/cspell-types': 9.6.0 + cspell-trie-lib: 9.6.0(@cspell/cspell-types@9.6.0) + fast-equals: 6.0.0 + + cspell-gitignore@9.6.0: + dependencies: + '@cspell/url': 9.6.0 + cspell-glob: 9.6.0 + cspell-io: 9.6.0 + + cspell-glob@9.6.0: + dependencies: + '@cspell/url': 9.6.0 + picomatch: 4.0.3 + + cspell-grammar@9.6.0: + dependencies: + '@cspell/cspell-pipe': 9.6.0 + '@cspell/cspell-types': 9.6.0 + + cspell-io@9.6.0: + dependencies: + '@cspell/cspell-service-bus': 9.6.0 + '@cspell/url': 9.6.0 + + cspell-lib@9.6.0: + dependencies: + '@cspell/cspell-bundled-dicts': 9.6.0 + '@cspell/cspell-pipe': 9.6.0 + '@cspell/cspell-resolver': 9.6.0 + '@cspell/cspell-types': 9.6.0 + '@cspell/dynamic-import': 9.6.0 + '@cspell/filetypes': 9.6.0 + '@cspell/strong-weak-map': 9.6.0 + '@cspell/url': 9.6.0 + clear-module: 4.1.2 + cspell-config-lib: 9.6.0 + cspell-dictionary: 9.6.0 + cspell-glob: 9.6.0 + cspell-grammar: 9.6.0 + cspell-io: 9.6.0 + cspell-trie-lib: 9.6.0(@cspell/cspell-types@9.6.0) + env-paths: 3.0.0 + gensequence: 8.0.8 + import-fresh: 3.3.1 + resolve-from: 5.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + xdg-basedir: 5.1.0 + + cspell-trie-lib@9.6.0(@cspell/cspell-types@9.6.0): + dependencies: + '@cspell/cspell-types': 9.6.0 + + cspell@9.6.0: + dependencies: + '@cspell/cspell-json-reporter': 9.6.0 + '@cspell/cspell-pipe': 9.6.0 + '@cspell/cspell-types': 9.6.0 + '@cspell/dynamic-import': 9.6.0 + '@cspell/url': 9.6.0 + ansi-regex: 6.2.2 + chalk: 5.6.2 + chalk-template: 1.1.2 + commander: 14.0.2 + cspell-config-lib: 9.6.0 + cspell-dictionary: 9.6.0 + cspell-gitignore: 9.6.0 + cspell-glob: 9.6.0 + cspell-io: 9.6.0 + cspell-lib: 9.6.0 + fast-json-stable-stringify: 2.1.0 + flatted: 3.3.3 + semver: 7.7.3 + tinyglobby: 0.2.15 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + env-paths@3.0.0: {} + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 '@esbuild/linux-ppc64': 0.27.2 '@esbuild/linux-riscv64': 0.27.2 '@esbuild/linux-s390x': 0.27.2 @@ -331,15 +1832,270 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 + escape-string-regexp@4.0.0: {} + + eslint-plugin-perfectionist@5.3.1(eslint@9.39.2)(typescript@5.9.3): + dependencies: + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + natural-orderby: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.0: {} + + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + 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.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + espree@11.1.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 5.0.0 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@6.0.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + fsevents@2.3.3: optional: true + gensequence@8.0.8: {} + get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globals@14.0.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + ini@4.1.1: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + natural-orderby@5.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parent-module@2.0.0: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picomatch@4.0.3: {} + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + semver@7.7.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + smol-toml@1.6.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + tsx@4.21.0: dependencies: esbuild: 0.27.2 @@ -347,6 +2103,41 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.53.1(eslint@9.39.2)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} - undici-types@6.21.0: {} + undici-types@7.16.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-uri@3.1.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + xdg-basedir@5.1.0: {} + + yaml@2.8.2: {} + + yocto-queue@0.1.0: {} diff --git a/src/create-binding.mts b/src/create-binding.mts index 57d4e90..47474cc 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -1,4 +1,4 @@ -import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError, type Awaitable } from "@vitruvius-labs/ts-predicate"; +import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError } from "@vitruvius-labs/ts-predicate"; import type { Engine } from "./engine.mjs"; import { make_variable } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..093cc41 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./build/esm", + "declarationDir": "./build/types", + "rootDir": "./src", + "declaration": true, + "declarationMap": false, + "removeComments": false, + "sourceMap": false, + "noEmit": false, + "listFiles": false, + "listEmittedFiles": false + }, + "include": [ + "./src" + ] +} From 52c0cb01350f2d2db5c7a2455c69f914875bb5bc Mon Sep 17 00:00:00 2001 From: Zamralik Date: Wed, 21 Jan 2026 01:53:43 +0100 Subject: [PATCH 08/71] chore: update eslint rules --- .eslint/configuration/strict/rules.mjs | 13 ----------- .eslint/configuration/style/rules.mjs | 30 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.eslint/configuration/strict/rules.mjs b/.eslint/configuration/strict/rules.mjs index 2e9adb5..d6ab873 100644 --- a/.eslint/configuration/strict/rules.mjs +++ b/.eslint/configuration/strict/rules.mjs @@ -443,19 +443,6 @@ const RULES = { "types": "never" } ], - "@ts/typedef": [ - "error", - { - "arrayDestructuring": true, - "arrowParameter": true, - "memberVariableDeclaration": true, - "objectDestructuring": true, - "parameter": true, - "propertyDeclaration": true, - "variableDeclaration": true, - "variableDeclarationIgnoreFunction": true - } - ], "@ts/unbound-method": [ "error", { diff --git a/.eslint/configuration/style/rules.mjs b/.eslint/configuration/style/rules.mjs index 10a9a0e..4e565fb 100644 --- a/.eslint/configuration/style/rules.mjs +++ b/.eslint/configuration/style/rules.mjs @@ -65,7 +65,7 @@ const RULES = { "error", "consistent" ], - "@style/func-call-spacing": [ + "@style/function-call-spacing": [ "error", "never" ], @@ -397,11 +397,24 @@ const RULES = { "returnAssign": true, "nestedBinaryExpressions": false, "ternaryOperandBinaryExpressions": false, - "enforceForArrowConditionals": false, - "enforceForNewInMemberExpressions": false, + "enforceForSequenceExpressions": false, "enforceForFunctionPrototypeMethods": false, /*"allowParensAfterCommentPattern": undefined,*/ - "ignoreJSX": "multi-line" + "ignoreJSX": "multi-line", + "ignoredNodes": [ + // Allows () => (a ? b : c) instead of () => a ? b : c + "ArrowFunctionExpression[body.type=ConditionalExpression]", + // Allows (new Foo()).bar instead of new Foo().bar + "MemberExpression[object.type=NewExpression]", + // Allows ...(a ? b : c) instead of ...a ? b : c + "SpreadElement[argument.type=ConditionalExpression]", + // Allows ...(a && b) instead of ...a && b + "SpreadElement[argument.type=LogicalExpression]", + // Allows ...(await foo) instead of ...await foo + "SpreadElement[argument.type=AwaitExpression]", + // Allows type X = (A | B) instead of type X = A | B + "TSTypeAliasDeclaration[typeAnnotation.type=TSUnionType]", + ] } ], "@style/no-extra-semi": "error", @@ -512,8 +525,8 @@ const RULES = { "error", "double", { - "avoidEscape": true, - "allowTemplateLiterals": false + "allowTemplateLiterals": "avoidEscape", + "avoidEscape": true } ], "@style/semi": [ @@ -549,10 +562,7 @@ const RULES = { "before": false, "after": true, "overrides": { - "arrow": { - "before": true, - "after": true - } + "arrow": "ignore" } } ], From f291f4dd5b36bc6cc1c5fda983e87a672c3aff07 Mon Sep 17 00:00:00 2001 From: Zamralik Date: Thu, 22 Jan 2026 18:05:59 +0100 Subject: [PATCH 09/71] fix: replace 'const enum' to work as dependency and fix exports --- src/_index.mts | 31 +++++++++++ src/boundary/definition/type/_index.mts | 6 ++- .../type/function-reference.type.mts | 6 +++ .../definition/type/table-array.type.mts | 3 ++ .../definition/type/table-input.type.mts | 5 +- .../definition/type/table-map-key.type.mts | 3 ++ .../definition/type/table-map.type.mts | 6 +-- src/boundary/definition/type/table.type.mts | 6 +++ src/boundary/definition/type/value.type.mts | 15 ++++++ src/boundary/predicate/_index.mts | 2 +- src/boundary/predicate/is-table-key.mts | 8 --- .../predicate/is-table-map-key-type.mts | 9 ++++ src/create-binding.mts | 13 ++--- src/engine.mts | 52 +++++++++---------- src/index.mts | 40 -------------- src/lib.mts | 42 +++++++-------- src/runtime.mts | 16 +++--- .../definition/enum/variable-kind.enum.mts | 23 ++++---- .../interface/variable-boolean.interface.mts | 2 +- .../interface/variable-function.interface.mts | 2 +- .../variable-native-function.interface.mts | 2 +- .../interface/variable-nil.interface.mts | 2 +- .../interface/variable-number.interface.mts | 2 +- .../interface/variable-string.interface.mts | 2 +- .../interface/variable-table.interface.mts | 6 +-- src/variable/definition/type/_index.mts | 1 + .../type/variable-table-map.type.mts | 5 ++ .../definition/type/variable-value.type.mts | 23 ++++---- src/variable/nil.mts | 2 +- .../predicate/assert-variable-kind.mts | 6 +-- src/variable/predicate/assert-variable.mts | 2 +- .../predicate/is-variable-kind-enum.mts | 4 +- src/variable/predicate/is-variable-kind.mts | 4 +- src/variable/unwrap-variable.mts | 25 +++++---- 34 files changed, 211 insertions(+), 165 deletions(-) create mode 100644 src/_index.mts create mode 100644 src/boundary/definition/type/function-reference.type.mts create mode 100644 src/boundary/definition/type/table-array.type.mts create mode 100644 src/boundary/definition/type/table-map-key.type.mts create mode 100644 src/boundary/definition/type/table.type.mts create mode 100644 src/boundary/definition/type/value.type.mts delete mode 100644 src/boundary/predicate/is-table-key.mts create mode 100644 src/boundary/predicate/is-table-map-key-type.mts delete mode 100644 src/index.mts create mode 100644 src/variable/definition/type/variable-table-map.type.mts diff --git a/src/_index.mts b/src/_index.mts new file mode 100644 index 0000000..3a0c520 --- /dev/null +++ b/src/_index.mts @@ -0,0 +1,31 @@ +import { Engine } from "./engine.mjs"; +import { make_boolean, make_number, make_string, make_table, make_variable } from "./runtime.mjs"; +import { compile } from "./compiler.mjs"; +import { std_lib, variable_to_string } from "./lib.mjs"; + +export * as lexer from "./lexer.mjs"; +export * as parser from "./parser.mjs"; +export * as ast from "./ast.mjs"; +export * as opcode from "./opcode.mjs"; +export * as runtime from "./runtime.mjs"; + +export type * from "./boundary/definition/_index.mjs"; +export * from "./create-binding.mjs"; + +export type * from "./variable/definition/interface/_index.mjs"; +export type * from "./variable/definition/type/_index.mjs"; +export * from "./variable/definition/enum/variable-kind.enum.mjs"; +export * from "./variable/nil.mjs"; +export * from "./variable/unwrap-variable.mjs"; + +export { + Engine, + compile, + std_lib as std_global, + make_boolean, + make_number, + make_string, + make_table, + make_variable, + variable_to_string as to_string, +}; diff --git a/src/boundary/definition/type/_index.mts b/src/boundary/definition/type/_index.mts index 5f80059..d131d93 100644 --- a/src/boundary/definition/type/_index.mts +++ b/src/boundary/definition/type/_index.mts @@ -1,4 +1,8 @@ export type * from "./matrix2d.type.mjs"; export type * from "./native-function.type.mjs"; -export type * from "./table-map.type.mjs"; +export type * from "./table-array.type.mjs"; export type * from "./table-input.type.mjs"; +export type * from "./table-map-key.type.mjs"; +export type * from "./table-map.type.mjs"; +export type * from "./table.type.mjs"; +export type * from "./value.type.mjs"; diff --git a/src/boundary/definition/type/function-reference.type.mts b/src/boundary/definition/type/function-reference.type.mts new file mode 100644 index 0000000..18f7b06 --- /dev/null +++ b/src/boundary/definition/type/function-reference.type.mts @@ -0,0 +1,6 @@ +interface FunctionReferenceType +{ + function_id: number | undefined; +} + +export type { FunctionReferenceType }; diff --git a/src/boundary/definition/type/table-array.type.mts b/src/boundary/definition/type/table-array.type.mts new file mode 100644 index 0000000..9133034 --- /dev/null +++ b/src/boundary/definition/type/table-array.type.mts @@ -0,0 +1,3 @@ +type TableArrayType = Array; + +export type { TableArrayType }; diff --git a/src/boundary/definition/type/table-input.type.mts b/src/boundary/definition/type/table-input.type.mts index c3ff6dc..15c1bbd 100644 --- a/src/boundary/definition/type/table-input.type.mts +++ b/src/boundary/definition/type/table-input.type.mts @@ -1,3 +1,6 @@ -type TableInputType = Array | Record | Map; +import type { TableMapKeyType } from "./table-map-key.type.mjs"; +import type { TableMapType } from "./table-map.type.mjs"; + +type TableInputType = Array | Record | TableMapType; export type { TableInputType }; diff --git a/src/boundary/definition/type/table-map-key.type.mts b/src/boundary/definition/type/table-map-key.type.mts new file mode 100644 index 0000000..4d5ec2d --- /dev/null +++ b/src/boundary/definition/type/table-map-key.type.mts @@ -0,0 +1,3 @@ +type TableMapKeyType = number | string; + +export type { TableMapKeyType }; diff --git a/src/boundary/definition/type/table-map.type.mts b/src/boundary/definition/type/table-map.type.mts index 4595983..37b2a65 100644 --- a/src/boundary/definition/type/table-map.type.mts +++ b/src/boundary/definition/type/table-map.type.mts @@ -1,5 +1,5 @@ -import type { Variable } from "../../../variable/definition/type/variable.type.mjs"; +import type { TableMapKeyType } from "./table-map-key.type.mjs"; -type TableMap = Map; +type TableMapType = Map; -export type { TableMap }; +export type { TableMapType }; diff --git a/src/boundary/definition/type/table.type.mts b/src/boundary/definition/type/table.type.mts new file mode 100644 index 0000000..92e9cb3 --- /dev/null +++ b/src/boundary/definition/type/table.type.mts @@ -0,0 +1,6 @@ +import type { TableArrayType } from "./table-array.type.mjs"; +import type { TableMapType } from "./table-map.type.mjs"; + +type TableType = TableArrayType | TableMapType; + +export type { TableType }; diff --git a/src/boundary/definition/type/value.type.mts b/src/boundary/definition/type/value.type.mts new file mode 100644 index 0000000..fdf8d8f --- /dev/null +++ b/src/boundary/definition/type/value.type.mts @@ -0,0 +1,15 @@ +import type { TableType } from "./table.type.mjs"; +import type { NativeFunction } from "./native-function.type.mjs"; +import type { FunctionReferenceType } from "./function-reference.type.mjs"; + +type ValueType = ( + | undefined + | boolean + | number + | string + | TableType + | NativeFunction + | FunctionReferenceType +); + +export type { ValueType }; diff --git a/src/boundary/predicate/_index.mts b/src/boundary/predicate/_index.mts index f2b740b..b38ef4e 100644 --- a/src/boundary/predicate/_index.mts +++ b/src/boundary/predicate/_index.mts @@ -1,2 +1,2 @@ export * from "./is-table-input-type.mjs"; -export * from "./is-table-key.mjs"; +export * from "./is-table-map-key-type.mjs"; diff --git a/src/boundary/predicate/is-table-key.mts b/src/boundary/predicate/is-table-key.mts deleted file mode 100644 index 07ccdb7..0000000 --- a/src/boundary/predicate/is-table-key.mts +++ /dev/null @@ -1,8 +0,0 @@ -import { isNumber, isString, isUnion } from "@vitruvius-labs/ts-predicate"; - -function isTableKey(value: unknown): value is string | number -{ - return isUnion(value, [isNumber, isString]); -} - -export { isTableKey }; diff --git a/src/boundary/predicate/is-table-map-key-type.mts b/src/boundary/predicate/is-table-map-key-type.mts new file mode 100644 index 0000000..35daf95 --- /dev/null +++ b/src/boundary/predicate/is-table-map-key-type.mts @@ -0,0 +1,9 @@ +import { isNumber, isString, isUnion } from "@vitruvius-labs/ts-predicate"; +import type { TableMapKeyType } from "../definition/type/table-map-key.type.mjs"; + +function isTableMapKeyType(value: unknown): value is TableMapKeyType +{ + return isUnion(value, [isNumber, isString]); +} + +export { isTableMapKeyType }; diff --git a/src/create-binding.mts b/src/create-binding.mts index 47474cc..2671002 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -8,12 +8,13 @@ import { RuntimeError } from "./runtime-error.mjs"; import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; -export const enum ParameterOptionEnum -{ - REQUIRED = "required", - OPTIONAL = "optional", - VARIADIC = "variadic", -} +export const ParameterOptionEnum = { + REQUIRED: "required", + OPTIONAL: "optional", + VARIADIC: "variadic", +} as const satisfies Record; + +export type ParameterOptionEnum = typeof ParameterOptionEnum[keyof typeof ParameterOptionEnum]; export interface ParameterDescriptorInterface { diff --git a/src/engine.mts b/src/engine.mts index 51b9dfc..7ee549b 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,26 +1,26 @@ -import { assertUnion, unary } from "@vitruvius-labs/ts-predicate" - -import type { Op } from './opcode.mjs' -import { OpCode, op_code_name } from './opcode.mjs' -import { make_number, make_boolean, make_string, make_table } from './runtime.mjs' -import { TokenStream } from './lexer.mjs' -import { parse } from './parser.mjs' -import { compile } from './compiler.mjs' -import { optimize_chunk } from './optimizer.mjs' -import * as std from './lib.mjs' -import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" -import type { Variable } from "./variable/definition/type/variable.type.mjs" -import { RuntimeError } from "./runtime-error.mjs" -import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs" -import { isNil } from "./variable/predicate/is-nil.mjs" -import { nil } from "./variable/nil.mjs" -import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs" -import type { TableMap } from "./boundary/definition/type/table-map.type.mjs" +import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; + +import type { Op } from "./opcode.mjs"; +import { OpCode, op_code_name } from "./opcode.mjs"; +import { make_boolean, make_number, make_string, make_table } from "./runtime.mjs"; +import { TokenStream } from "./lexer.mjs"; +import { parse } from "./parser.mjs"; +import { compile } from "./compiler.mjs"; +import { optimize_chunk } from "./optimizer.mjs"; +import * as std from "./lib.mjs"; +import { VariableKind, type VariableKindEnum } from "./variable/definition/enum/variable-kind.enum.mjs"; +import type { Variable } from "./variable/definition/type/variable.type.mjs"; +import { RuntimeError } from "./runtime-error.mjs"; +import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs"; +import { isNil } from "./variable/predicate/is-nil.mjs"; +import { nil } from "./variable/nil.mjs"; +import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs"; +import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; -import type { VariableFunction } from "./variable/definition/interface/variable-function.interface.mjs" -import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs" -import { equals } from "./variable/equals.mjs" -import { assertVariable } from "./variable/predicate/assert-variable.mjs" +import type { VariableFunction } from "./variable/definition/interface/variable-function.interface.mjs"; +import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; +import { equals } from "./variable/equals.mjs"; +import { assertVariable } from "./variable/predicate/assert-variable.mjs"; function index(val: Variable | undefined): string | number | undefined { @@ -58,7 +58,7 @@ export interface LuaOptions export class Engine { private program: Op[]; - private globals: TableMap; + private globals: VariableTableMapType; private start_ip: number = 0; private ip: number = 0; @@ -72,7 +72,7 @@ export class Engine constructor( script?: string, - globals?: TableMap + globals?: VariableTableMapType ) { this.program = [] @@ -137,7 +137,7 @@ export class Engine }) } - define_table(name: string, table: Map): void + define_table(name: string, table: VariableTableMapType): void { this.globals.set(name, { data_type: VariableKind.Table, @@ -256,7 +256,7 @@ export class Engine return value; } - private stack_pop_kind(kind: K): Variable & { data_type: K } + private stack_pop_kind(kind: K): Variable & { data_type: K } { const value = this.stack.pop(); diff --git a/src/index.mts b/src/index.mts deleted file mode 100644 index c9adc17..0000000 --- a/src/index.mts +++ /dev/null @@ -1,40 +0,0 @@ -import { Engine } from './engine.mjs' -import { make_boolean, make_number, make_string, make_table, make_variable } from './runtime.mjs' -import { compile } from './compiler.mjs' -import { std_lib, variable_to_string } from './lib.mjs' -import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" -import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs" -import type { Variable } from "./variable/definition/type/variable.type.mjs" -import { nil } from "./variable/nil.mjs" -import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs" -export * as lexer from './lexer.mjs' -export * as parser from './parser.mjs' -export * as ast from './ast.mjs' -export * as opcode from './opcode.mjs' -export * as runtime from './runtime.mjs' -export * from './create-binding.mjs' - -export const Nil = VariableKind.Nil -export const Boolean = VariableKind.Boolean -export const Number = VariableKind.Number -export const String = VariableKind.String -export const Function = VariableKind.Function -export const NativeFunctionType = VariableKind.NativeFunction -export const Table = VariableKind.Table - -export { - std_lib as std_global, - nil, - make_boolean, - make_number, - make_string, - make_table, - make_variable, - variable_to_string as to_string, - Engine, - VariableKind, - Variable, - VariableUnwrapUtility, - NativeFunction, - compile, -} diff --git a/src/lib.mts b/src/lib.mts index 2f6f3ee..b7478f5 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -1,23 +1,23 @@ -import type { Variable } from './variable/definition/type/variable.type.mjs' -import type { VariableValueType } from './variable/definition/type/variable-value.type.mjs' -import { VariableKind } from './variable/definition/enum/variable-kind.enum.mjs' -import { isVariableKind } from './variable/predicate/is-variable-kind.mjs' -import { assertVariableKind } from './variable/predicate/assert-variable-kind.mjs' -import { nil } from './variable/nil.mjs' -import { Engine } from './engine.mjs' -import { make_boolean, make_number, make_string, make_variable } from './runtime.mjs' -import { assertArray, assertPopulatedArray, isCallable, isInteger, unary, ValidationError } from "@vitruvius-labs/ts-predicate" -import { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs" -import { assertVariable } from "./variable/predicate/assert-variable.mjs" -import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs" -import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs" -import { isNil } from "./variable/predicate/is-nil.mjs" -import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs" -import type { NativeFunction } from "./index.mjs" -import type { TableMap } from "./boundary/definition/type/table-map.type.mjs" -import { RuntimeError } from "./runtime-error.mjs" - -function optional_parameter( +import type { Variable } from "./variable/definition/type/variable.type.mjs"; +import type { VariableValueType } from "./variable/definition/type/variable-value.type.mjs"; +import { VariableKind, type VariableKindEnum } from "./variable/definition/enum/variable-kind.enum.mjs"; +import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs"; +import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs"; +import { nil } from "./variable/nil.mjs"; +import type { Engine } from "./engine.mjs"; +import { make_boolean, make_number, make_string, make_variable } from "./runtime.mjs"; +import { ValidationError, assertArray, assertPopulatedArray, isCallable, isInteger, unary } from "@vitruvius-labs/ts-predicate"; +import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; +import { assertVariable } from "./variable/predicate/assert-variable.mjs"; +import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; +import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; +import { isNil } from "./variable/predicate/is-nil.mjs"; +import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; +import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; +import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; +import { RuntimeError } from "./runtime-error.mjs"; + +function optional_parameter( expected_kind: K, parameter: Variable | undefined ): VariableValueType | undefined @@ -847,7 +847,7 @@ function twrap(content: Array<[unknown, Variable]>): VariableTable }; } -export function std_lib(): TableMap +export function std_lib(): VariableTableMapType { const string_mapping: VariableTable = twrap([ ['byte', fwrap(string_byte)], diff --git a/src/runtime.mts b/src/runtime.mts index 50f14c2..ee3166a 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -13,31 +13,32 @@ import type { VariableBoolean } from "./variable/definition/interface/variable-b import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import type { VariableString } from "./variable/definition/interface/variable-string.interface.mjs"; import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; +import type { TableInputType } from "./boundary/definition/type/table-input.type.mjs"; +import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { isVariable } from "./variable/predicate/is-variable.mjs"; import { nil } from "./variable/nil.mjs"; -import type { TableInputType } from "./boundary/definition/type/table-input.type.mjs"; import { isTableInputType } from "./boundary/predicate/is-table-input-type.mjs"; -import { isTableKey } from "./boundary/predicate/is-table-key.mjs"; +import { isTableMapKeyType } from "./boundary/predicate/is-table-map-key-type.mjs"; export function make_boolean(boolean: boolean): VariableBoolean { - return { data_type: VariableKind.Boolean, boolean: boolean } + return { data_type: VariableKind.Boolean, boolean: boolean }; } export function make_number(number: number): VariableNumber { - return { data_type: VariableKind.Number, number: number } + return { data_type: VariableKind.Number, number: number }; } export function make_string(string: string): VariableString { - return { data_type: VariableKind.String, string: string } + return { data_type: VariableKind.String, string: string }; } export function make_table(input?: TableInputType): VariableTable { - const table_content: Map = new Map(); + const table_content: VariableTableMapType = new Map(); const table_variable: Variable = { data_type: VariableKind.Table, @@ -63,7 +64,7 @@ export function make_table(input?: TableInputType): VariableTable { for (const [key, value] of input.entries()) { - if (isTableKey(key)) + if (isTableMapKeyType(key)) { table_content.set(key, make_variable(value)); } @@ -80,6 +81,7 @@ export function make_table(input?: TableInputType): VariableTable if (isNumber(numeric_key)) { table_content.set(numeric_key, variable); + continue; } diff --git a/src/variable/definition/enum/variable-kind.enum.mts b/src/variable/definition/enum/variable-kind.enum.mts index 81117cd..58bc4f6 100644 --- a/src/variable/definition/enum/variable-kind.enum.mts +++ b/src/variable/definition/enum/variable-kind.enum.mts @@ -1,12 +1,13 @@ -const enum VariableKind -{ - Nil = "nil", - Boolean = "boolean", - Number = "number", - String = "string", - Table = "table", - Function = "function", - NativeFunction = "native-function", -} +const VariableKind = { + Nil: "nil", + Boolean: "boolean", + Number: "number", + String: "string", + Table: "table", + Function: "function", + NativeFunction: "native-function", +} as const satisfies Record; -export { VariableKind }; +type VariableKindEnum = typeof VariableKind[keyof typeof VariableKind]; + +export { VariableKind, type VariableKindEnum }; diff --git a/src/variable/definition/interface/variable-boolean.interface.mts b/src/variable/definition/interface/variable-boolean.interface.mts index bd427bf..f476be1 100644 --- a/src/variable/definition/interface/variable-boolean.interface.mts +++ b/src/variable/definition/interface/variable-boolean.interface.mts @@ -3,7 +3,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableBoolean extends BaseVariable { - data_type: VariableKind.Boolean; + data_type: typeof VariableKind.Boolean; boolean: boolean; } diff --git a/src/variable/definition/interface/variable-function.interface.mts b/src/variable/definition/interface/variable-function.interface.mts index 14d5e66..48cbef1 100644 --- a/src/variable/definition/interface/variable-function.interface.mts +++ b/src/variable/definition/interface/variable-function.interface.mts @@ -3,7 +3,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableFunction extends BaseVariable { - data_type: VariableKind.Function; + data_type: typeof VariableKind.Function; function_id?: number; } diff --git a/src/variable/definition/interface/variable-native-function.interface.mts b/src/variable/definition/interface/variable-native-function.interface.mts index 3420319..2f6e1d2 100644 --- a/src/variable/definition/interface/variable-native-function.interface.mts +++ b/src/variable/definition/interface/variable-native-function.interface.mts @@ -4,7 +4,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableNativeFunction extends BaseVariable { - data_type: VariableKind.NativeFunction; + data_type: typeof VariableKind.NativeFunction; native_function: NativeFunction; } diff --git a/src/variable/definition/interface/variable-nil.interface.mts b/src/variable/definition/interface/variable-nil.interface.mts index 23a40c2..646e824 100644 --- a/src/variable/definition/interface/variable-nil.interface.mts +++ b/src/variable/definition/interface/variable-nil.interface.mts @@ -3,7 +3,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableNil extends BaseVariable { - data_type: VariableKind.Nil; + data_type: typeof VariableKind.Nil; } export type { VariableNil }; diff --git a/src/variable/definition/interface/variable-number.interface.mts b/src/variable/definition/interface/variable-number.interface.mts index c9baf5e..e3efdd0 100644 --- a/src/variable/definition/interface/variable-number.interface.mts +++ b/src/variable/definition/interface/variable-number.interface.mts @@ -3,7 +3,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableNumber extends BaseVariable { - data_type: VariableKind.Number; + data_type: typeof VariableKind.Number; number: number; } diff --git a/src/variable/definition/interface/variable-string.interface.mts b/src/variable/definition/interface/variable-string.interface.mts index de0e344..b0939dd 100644 --- a/src/variable/definition/interface/variable-string.interface.mts +++ b/src/variable/definition/interface/variable-string.interface.mts @@ -3,7 +3,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableString extends BaseVariable { - data_type: VariableKind.String; + data_type: typeof VariableKind.String; string: string; } diff --git a/src/variable/definition/interface/variable-table.interface.mts b/src/variable/definition/interface/variable-table.interface.mts index beddcc0..57ffd74 100644 --- a/src/variable/definition/interface/variable-table.interface.mts +++ b/src/variable/definition/interface/variable-table.interface.mts @@ -1,11 +1,11 @@ -import type { TableMap } from "../../../boundary/definition/type/table-map.type.mjs"; +import type { VariableTableMapType } from "../type/variable-table-map.type.mjs"; import type { BaseVariable } from "./base-variable.interface.mjs"; import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableTable extends BaseVariable { - data_type: VariableKind.Table; - table: TableMap; + data_type: typeof VariableKind.Table; + table: VariableTableMapType; } export type { VariableTable }; diff --git a/src/variable/definition/type/_index.mts b/src/variable/definition/type/_index.mts index abe3457..24f36f5 100644 --- a/src/variable/definition/type/_index.mts +++ b/src/variable/definition/type/_index.mts @@ -1,2 +1,3 @@ +export type * from "./variable-table-map.type.mjs"; export type * from "./variable.type.mjs"; export type * from "./variable-value.type.mjs"; diff --git a/src/variable/definition/type/variable-table-map.type.mts b/src/variable/definition/type/variable-table-map.type.mts new file mode 100644 index 0000000..a83c2d2 --- /dev/null +++ b/src/variable/definition/type/variable-table-map.type.mts @@ -0,0 +1,5 @@ +import type { Variable } from "./variable.type.mjs"; + +type VariableTableMapType = Map; + +export type { VariableTableMapType }; diff --git a/src/variable/definition/type/variable-value.type.mts b/src/variable/definition/type/variable-value.type.mts index ce40968..eb1c23d 100644 --- a/src/variable/definition/type/variable-value.type.mts +++ b/src/variable/definition/type/variable-value.type.mts @@ -1,16 +1,15 @@ import type { NativeFunction } from "../../../boundary/definition/type/native-function.type.mjs"; -import type { TableMap } from "../../../boundary/definition/type/table-map.type.mjs"; -import type { VariableKind } from "../enum/variable-kind.enum.mjs"; +import type { VariableTableMapType } from "./variable-table-map.type.mjs"; +import type { VariableKind, VariableKindEnum } from "../enum/variable-kind.enum.mjs"; -type VariableValueType = ( - K extends VariableKind.Nil ? undefined : - K extends VariableKind.Boolean ? boolean : - K extends VariableKind.Number ? number : - K extends VariableKind.String ? string : - K extends VariableKind.Table ? TableMap : - K extends VariableKind.Function ? number : - K extends VariableKind.NativeFunction ? NativeFunction : - never -) +type VariableValueType + = K extends typeof VariableKind.Nil ? undefined + : K extends typeof VariableKind.Boolean ? boolean + : K extends typeof VariableKind.Number ? number + : K extends typeof VariableKind.String ? string + : K extends typeof VariableKind.Table ? VariableTableMapType + : K extends typeof VariableKind.Function ? number + : K extends typeof VariableKind.NativeFunction ? NativeFunction + : never; export type { VariableValueType }; diff --git a/src/variable/nil.mts b/src/variable/nil.mts index 465195a..3b910e5 100644 --- a/src/variable/nil.mts +++ b/src/variable/nil.mts @@ -1,6 +1,6 @@ import type { VariableNil } from "./definition/interface/variable-nil.interface.mjs"; import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; -const nil: VariableNil = { data_type: VariableKind.Nil }; +const nil: VariableNil = Object.freeze({ data_type: VariableKind.Nil }); export { nil }; diff --git a/src/variable/predicate/assert-variable-kind.mts b/src/variable/predicate/assert-variable-kind.mts index 55030ca..40e0d9f 100644 --- a/src/variable/predicate/assert-variable-kind.mts +++ b/src/variable/predicate/assert-variable-kind.mts @@ -1,15 +1,15 @@ import { ValidationError } from "@vitruvius-labs/ts-predicate"; -import type { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import type { VariableKindEnum } from "../definition/enum/variable-kind.enum.mjs"; import type { Variable } from "../definition/type/variable.type.mjs"; import { assertVariable } from "./assert-variable.mjs"; -function assertVariableKind(variable: unknown, kind: K): asserts variable is Variable & { data_type: K } +function assertVariableKind(variable: unknown, kind: K): asserts variable is Variable & { data_type: K } { assertVariable(variable); if (variable.data_type !== kind) { - throw new ValidationError(`Expected ${ kind }, got ${ variable.data_type }`); + throw new ValidationError(`Expected ${kind}, got ${variable.data_type}`); } } diff --git a/src/variable/predicate/assert-variable.mts b/src/variable/predicate/assert-variable.mts index e9a4f59..70deed3 100644 --- a/src/variable/predicate/assert-variable.mts +++ b/src/variable/predicate/assert-variable.mts @@ -6,7 +6,7 @@ function assertVariable(variable: unknown): asserts variable is Variable { if (!isVariable(variable)) { - throw new ValidationError(`Expected Variable`) + throw new ValidationError("Expected Variable"); } } diff --git a/src/variable/predicate/is-variable-kind-enum.mts b/src/variable/predicate/is-variable-kind-enum.mts index 6ec5151..0f14db8 100644 --- a/src/variable/predicate/is-variable-kind-enum.mts +++ b/src/variable/predicate/is-variable-kind-enum.mts @@ -1,7 +1,7 @@ import { isEnumValue } from "@vitruvius-labs/ts-predicate"; -import { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import { VariableKind, type VariableKindEnum } from "../definition/enum/variable-kind.enum.mjs"; -function isVariableKindEnum(value: unknown): value is VariableKind +function isVariableKindEnum(value: unknown): value is VariableKindEnum { return isEnumValue(value, [ VariableKind.Nil, diff --git a/src/variable/predicate/is-variable-kind.mts b/src/variable/predicate/is-variable-kind.mts index ecb3c85..bbe2b94 100644 --- a/src/variable/predicate/is-variable-kind.mts +++ b/src/variable/predicate/is-variable-kind.mts @@ -1,8 +1,8 @@ -import type { VariableKind } from "../definition/enum/variable-kind.enum.mjs"; +import type { VariableKindEnum } from "../definition/enum/variable-kind.enum.mjs"; import type { Variable } from "../definition/type/variable.type.mjs"; import { isVariable } from "./is-variable.mjs"; -function isVariableKind(variable: unknown, kind: K): variable is Variable & { data_type: K } +function isVariableKind(variable: unknown, kind: K): variable is Variable & { data_type: K } { return isVariable(variable) && variable.data_type === kind; } diff --git a/src/variable/unwrap-variable.mts b/src/variable/unwrap-variable.mts index 3692139..23cd57e 100644 --- a/src/variable/unwrap-variable.mts +++ b/src/variable/unwrap-variable.mts @@ -6,8 +6,13 @@ import type { VariableNil } from "./definition/interface/variable-nil.interface. import type { VariableNumber } from "./definition/interface/variable-number.interface.mjs"; import type { VariableString } from "./definition/interface/variable-string.interface.mjs"; import type { VariableTable } from "./definition/interface/variable-table.interface.mjs"; +import type { NativeFunction } from "../_index.mjs"; +import type { TableType } from "../boundary/definition/type/table.type.mjs"; +import type { TableMapType } from "../boundary/definition/type/table-map.type.mjs"; +import type { FunctionReferenceType } from "../boundary/definition/type/function-reference.type.mjs"; +import type { ValueType } from "../boundary/definition/type/value.type.mjs"; import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; -import { isCallable } from "@vitruvius-labs/ts-predicate"; +import { isTableMapKeyType } from "../boundary/predicate/is-table-map-key-type.mjs"; class VariableUnwrapUtility { @@ -15,12 +20,12 @@ class VariableUnwrapUtility public static unwrap(this: void, input: VariableBoolean): boolean; public static unwrap(this: void, input: VariableNumber): number; public static unwrap(this: void, input: VariableString): string; - public static unwrap(this: void, input: VariableTable): Array | Map; - public static unwrap(this: void, input: VariableFunction): { function_id: number }; - public static unwrap(this: void, input: VariableNativeFunction): Function; - public static unwrap(this: void, input: Variable): unknown; + public static unwrap(this: void, input: VariableTable): TableType; + public static unwrap(this: void, input: VariableFunction): FunctionReferenceType; + public static unwrap(this: void, input: VariableNativeFunction): NativeFunction; + public static unwrap(this: void, input: Variable): ValueType; - public static unwrap(this: void, input: Variable): unknown + public static unwrap(this: void, input: Variable): ValueType { switch (input.data_type) { @@ -41,7 +46,7 @@ class VariableUnwrapUtility } } - public static unwrapTable(this: void, input: VariableTable): Array | Map + public static unwrapTable(this: void, input: VariableTable): TableType { const output: Array = []; @@ -61,13 +66,13 @@ class VariableUnwrapUtility return output; } - protected static unwrapTableGeneric(this: void, input: VariableTable): Map + protected static unwrapTableGeneric(this: void, input: VariableTable): TableMapType { - const output: Map = new Map(); + const output: TableMapType = new Map(); for (const [key, value] of input.table.entries()) { - if (isCallable(key)) + if (!isTableMapKeyType(key)) { continue; } From b6d5add8914f49eb095d77604834486f459e85fc Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 09:14:37 +0000 Subject: [PATCH 10/71] chore: Removed a broken line in pipeline. --- .github/workflows/pipeline.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 204b4d1..59d5185 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -41,7 +41,6 @@ jobs: - name: Install dependencies run: | pnpm install --frozen-lockfile - cd packages/mockingbird && pnpm install --frozen-lockfile && cd ../.. - name: Dependencies caching uses: actions/cache@v4 From dced13b2dfcbb403ef6c13648d6254cd798ac439 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 09:25:20 +0000 Subject: [PATCH 11/71] chore: Made publication manual. --- .github/workflows/pipeline.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 59d5185..741feb0 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -4,6 +4,12 @@ run-name: Running CI/CD for event ${{github.event_name}} (Triggered by user ${{ on: push: + workflow_dispatch: + inputs: + publish: + type: boolean + required: false + default: false jobs: setup: @@ -147,7 +153,7 @@ jobs: run: pnpm ci:test:unit publish-lua-engine: - if: ${{ github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push' }} + if: ${{ (github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push') || inputs.publish == true }} # needs: # [ # spell-check-global, From ef375c7363ced3f0eff995f9b922d931fd6e10d9 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 09:49:16 +0000 Subject: [PATCH 12/71] chore: Added temporary publication. --- .github/workflows/pipeline.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 741feb0..562786d 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -4,12 +4,6 @@ run-name: Running CI/CD for event ${{github.event_name}} (Triggered by user ${{ on: push: - workflow_dispatch: - inputs: - publish: - type: boolean - required: false - default: false jobs: setup: @@ -153,7 +147,7 @@ jobs: run: pnpm ci:test:unit publish-lua-engine: - if: ${{ (github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push') || inputs.publish == true }} + # if: ${{ github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push' }} # needs: # [ # spell-check-global, From e22220314b107504b48f19ecb65bf61696354a16 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 10:03:04 +0000 Subject: [PATCH 13/71] chore: Changed package name to adequate name. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adfe685..81390a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "name": "lua-engine", + "name": "@vitruvius-labs/lua-engine", "version": "0.1.0", "description": "A TypeScript implementation of a Lua interpreter", "license": "BSD-2-Clause", From 30bc599200f5650c07019bce175832e94d90b889 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 10:19:23 +0000 Subject: [PATCH 14/71] chore: Version 0.1.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81390a6..50646ab 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@vitruvius-labs/lua-engine", - "version": "0.1.0", + "version": "0.1.1", "description": "A TypeScript implementation of a Lua interpreter", "license": "BSD-2-Clause", "author": { From 59cb83365ee85f08901f79553912e8f8b80f919c Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 23 Jan 2026 10:31:45 +0000 Subject: [PATCH 15/71] chore: Added missing exports. --- package.json | 2 +- src/variable/definition/type/variable.type.mts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 50646ab..1302376 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@vitruvius-labs/lua-engine", - "version": "0.1.1", + "version": "0.1.2", "description": "A TypeScript implementation of a Lua interpreter", "license": "BSD-2-Clause", "author": { diff --git a/src/variable/definition/type/variable.type.mts b/src/variable/definition/type/variable.type.mts index 8cc327c..b422225 100644 --- a/src/variable/definition/type/variable.type.mts +++ b/src/variable/definition/type/variable.type.mts @@ -16,4 +16,13 @@ type Variable = ( | VariableNativeFunction ); -export type { Variable }; +export type { + Variable, + VariableBoolean, + VariableFunction, + VariableNativeFunction, + VariableNil, + VariableNumber, + VariableString, + VariableTable, +}; From a5257768edd15cbce418f65f46cb20bcba970677 Mon Sep 17 00:00:00 2001 From: Zamralik Date: Sat, 31 Jan 2026 11:02:38 +0100 Subject: [PATCH 16/71] chore: fix eslint errors --- src/ast.mts | 128 +-- src/compiler.mts | 859 ++++++++++------- src/create-binding.mts | 6 +- src/engine.mts | 758 +++++++++------ src/lexer.mts | 817 ++++++++-------- src/lib.mts | 843 +++++++++------- src/opcode.mts | 217 +++-- src/optimizer.mts | 490 +++++++--- src/parser.mts | 904 +++++++++++------- src/runtime-error.mts | 2 +- .../interface/variable-function.interface.mts | 2 +- src/variable/equals.mts | 20 +- 12 files changed, 3077 insertions(+), 1969 deletions(-) diff --git a/src/ast.mts b/src/ast.mts index eba0a15..843684a 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,4 +1,4 @@ -import type { Token } from './lexer.mjs' +import type { Token } from "./lexer.mjs"; export enum ValueKind { @@ -13,21 +13,21 @@ export enum ValueKind export interface LuaFunction { - parameters: Array, - body: Chunk, + parameters: Array; + body: Chunk; } export interface Value { - kind: ValueKind, - token: Token, + kind: ValueKind; + token: Token; - number?: number | undefined, - boolean?: boolean | undefined, - string?: string | undefined, - table?: Map | undefined, - function?: LuaFunction | undefined, - identifier?: string | undefined, + number?: number | undefined; + boolean?: boolean | undefined; + string?: string | undefined; + table?: Map | undefined; + function?: LuaFunction | undefined; + identifier?: string | undefined; } export enum ExpressionKind @@ -68,88 +68,88 @@ export enum ExpressionKind export interface Expression { - kind: ExpressionKind, - token: Token, + kind: ExpressionKind; + token: Token; - lhs?: Expression, - rhs?: Expression, - value?: Value, - expression?: Expression, - index?: Expression, - arguments?: Expression[], + lhs?: Expression; + rhs?: Expression; + value?: Value; + expression?: Expression; + index?: Expression; + arguments?: Array; } export interface Assignment { - local: boolean, - lhs: Expression[], - rhs: Expression[], - token: Token, + local: boolean; + lhs: Array; + rhs: Array; + token: Token; } export interface Local { - names: Token[], - token: Token, + names: Array; + token: Token; } export interface ElseIfBlock { - body: Chunk, - condition: Expression, - token: Token, + body: Chunk; + condition: Expression; + token: Token; } export interface IfBlock { - condition: Expression, - body: Chunk, - else_if_bodies: ElseIfBlock[], - else_body?: Chunk | undefined, - token: Token, + condition: Expression; + body: Chunk; + else_if_bodies: Array; + else_body?: Chunk | undefined; + token: Token; } export interface While { - condition: Expression, - body: Chunk, - token: Token, + condition: Expression; + body: Chunk; + token: Token; } export interface For { - items: Token[], - iterator: Expression, - body: Chunk, - token: Token, + items: Array; + iterator: Expression; + body: Chunk; + token: Token; } export interface NumericFor { - index: Token, - start: Expression, - end: Expression, - step: Expression | undefined, - body: Chunk, + index: Token; + start: Expression; + end: Expression; + step: Expression | undefined; + body: Chunk; } export interface Repeat { - body: Chunk, - condition: Expression, - token: Token, + body: Chunk; + condition: Expression; + token: Token; } export interface Do { - body: Chunk, - token: Token, + body: Chunk; + token: Token; } export interface Return { - values: Expression[], - token: Token, + values: Array; + token: Token; } export enum StatementKind @@ -171,20 +171,20 @@ export enum StatementKind export interface Statement { - kind: StatementKind, - expression?: Expression | undefined, - assignment?: Assignment | undefined, - local?: Local | undefined, - if?: IfBlock | undefined, - while?: While | undefined, - for?: For | undefined, - numeric_for?: NumericFor | undefined, - repeat?: Repeat | undefined, - do?: Do | undefined, - return?: Return | undefined, + kind: StatementKind; + expression?: Expression | undefined; + assignment?: Assignment | undefined; + local?: Local | undefined; + if?: IfBlock | undefined; + while?: While | undefined; + for?: For | undefined; + numeric_for?: NumericFor | undefined; + repeat?: Repeat | undefined; + do?: Do | undefined; + return?: Return | undefined; } export interface Chunk { - statements: Statement[], + statements: Array; } diff --git a/src/compiler.mts b/src/compiler.mts index 43fb540..8a5b960 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,687 +1,830 @@ -import type { Chunk, Expression, Value } from './ast.mjs' -import type { IfBlock, While, For, NumericFor, Repeat, Do } from './ast.mjs' -import type { Op, Program } from './opcode.mjs' -import type { Token } from './lexer.mjs' -import type { Assignment, Local, Return } from './ast.mjs' - -import { StatementKind, ExpressionKind, ValueKind } from './ast.mjs' -import { OpCode } from './opcode.mjs' -import { make_boolean, make_number, make_string } from './runtime.mjs' -import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs" -import { nil } from "./variable/nil.mjs" - -function compile_function(chunk: Chunk, token: Token, parameters: Token[], functions: Op[][]): number +import type { Chunk, Expression, Value } from "./ast.mjs"; +import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; +import type { Op, Program } from "./opcode.mjs"; +import type { Token } from "./lexer.mjs"; +import type { Assignment, Local, Return } from "./ast.mjs"; + +import { ExpressionKind, StatementKind, ValueKind } from "./ast.mjs"; +import { OpCode } from "./opcode.mjs"; +import { make_boolean, make_number, make_string } from "./runtime.mjs"; +import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; +import { nil } from "./variable/nil.mjs"; + +function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { - const ops: Op[] = [] - ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }) + const ops: Array = []; + + ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); + for (const parameter of parameters.reverse()) { - ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }) - ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }) + ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }); + ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }); } - ops.push(...compile_block(chunk, functions)) - ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }) - ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }) - functions.push(ops) - return functions.length - 1 + ops.push(...compile_block(chunk, functions)); + ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }); + ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }); + + functions.push(ops); + + return functions.length - 1; } -function compile_value(value: Value | undefined, functions: Op[][]): Op[] +function compile_value(value: Value | undefined, functions: Array>): Array { if (value === undefined) - throw new Error() + { + throw new Error(); + } + + const debug = value.token.debug; - const debug = value.token.debug switch (value.kind) { case ValueKind.NilLiteral: - return [{ code: OpCode.Push, arg: nil, debug: debug }] + return [{ code: OpCode.Push, arg: nil, debug: debug }]; case ValueKind.BooleanLiteral: - return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }] + return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }]; case ValueKind.NumberLiteral: - return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }] + return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }]; case ValueKind.StringLiteral: - return [{ code: OpCode.Push, arg: make_string(value.string ?? ''), debug: debug }] + return [{ code: OpCode.Push, arg: make_string(value.string ?? ""), debug: debug }]; case ValueKind.Function: { return [{ - code: OpCode.Push, arg: { + code: OpCode.Push, + arg: { data_type: VariableKind.Function, function_id: compile_function( value.function?.body ?? { statements: [] }, value.token, value.function?.parameters ?? [], - functions), + functions + ), }, debug: debug, - }] + }]; } case ValueKind.TableLiteral: { - const output: Op[] = [] - output.push({ code: OpCode.NewTable, debug: debug }) + const output: Array = []; + + output.push({ code: OpCode.NewTable, debug: debug }); for (const [key, expression] of [...value.table?.entries() ?? []].reverse()) { - output.push(...compile_expression(expression, functions)) - output.push(...compile_expression(key, functions)) + output.push(...compile_expression(expression, functions)); + output.push(...compile_expression(key, functions)); } - output.push({ code: OpCode.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }) - return output + output.push({ code: OpCode.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }); + + return output; } case ValueKind.Variable: { return [{ code: OpCode.Load, - arg: { data_type: VariableKind.String, string: value.identifier ?? '' }, + arg: { data_type: VariableKind.String, string: value.identifier ?? "" }, debug: debug, - }] + }]; } - - default: - throw new Error() } } function compile_operation( expression: Expression, operation: OpCode, - functions: Op[][] -): Op[] + functions: Array> +): Array { - const { lhs, rhs } = expression + const { lhs, rhs } = expression; + if (lhs === undefined || rhs === undefined) - throw new Error() + { + throw new Error(); + } - const ops: Op[] = [] - ops.push(...compile_expression(rhs, functions)) - ops.push(...compile_expression(lhs, functions)) - ops.push({ code: operation, debug: expression.token.debug }) - return ops + const ops: Array = []; + + ops.push(...compile_expression(rhs, functions)); + ops.push(...compile_expression(lhs, functions)); + ops.push({ code: operation, debug: expression.token.debug }); + + return ops; } function compile_call( func: Expression | undefined, - args: Expression[] | undefined, - functions: Op[][] -): Op[] + args: Array | undefined, + functions: Array> +): Array { if (func === undefined || args === undefined) - throw new Error() + { + throw new Error(); + } + + const debug = func.token.debug; + const ops: Array = []; - const debug = func.token.debug - const ops: Op[] = [] for (const arg of args) - ops.push(...compile_expression(arg, functions)) - ops.push(...compile_expression(func, functions)) - ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }) - ops.push({ code: OpCode.Call, debug: debug }) - return ops + { + ops.push(...compile_expression(arg, functions)); + } + + ops.push(...compile_expression(func, functions)); + ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }); + ops.push({ code: OpCode.Call, debug: debug }); + + return ops; } function compile_index( target: Expression | undefined, index: Expression | undefined, - functions: Op[][] -): Op[] + functions: Array> +): Array { if (target === undefined || index === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + + ops.push(...compile_expression(index, functions)); + ops.push(...compile_expression(target, functions)); + ops.push({ code: OpCode.LoadIndex, debug: target.token.debug }); - const ops: Op[] = [] - ops.push(...compile_expression(index, functions)) - ops.push(...compile_expression(target, functions)) - ops.push({ code: OpCode.LoadIndex, debug: target.token.debug }) - return ops + return ops; } function compile_unary_operation( expression: Expression | undefined, operation: OpCode, - functions: Op[][] -): Op[] + functions: Array> +): Array { if (expression === undefined || expression.expression === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; - const ops: Op[] = [] - ops.push(...compile_expression(expression.expression, functions)) - ops.push({ code: operation, debug: expression.token.debug }) - return ops + ops.push(...compile_expression(expression.expression, functions)); + ops.push({ code: operation, debug: expression.token.debug }); + + return ops; } -function compile_expression(expression: Expression | undefined, functions: Op[][]): Op[] +function compile_expression(expression: Expression | undefined, functions: Array>): Array { if (expression === undefined) - throw new Error() + { + throw new Error(); + } switch (expression.kind) { case ExpressionKind.Value: - return compile_value(expression.value, functions) + return compile_value(expression.value, functions); case ExpressionKind.Call: - return compile_call(expression.expression, expression.arguments, functions) + return compile_call(expression.expression, expression.arguments, functions); case ExpressionKind.Index: - return compile_index(expression.expression, expression.index, functions) + return compile_index(expression.expression, expression.index, functions); case ExpressionKind.Addition: - return compile_operation(expression, OpCode.Add, functions) + return compile_operation(expression, OpCode.Add, functions); case ExpressionKind.Subtract: - return compile_operation(expression, OpCode.Subtract, functions) + return compile_operation(expression, OpCode.Subtract, functions); case ExpressionKind.Multiplication: - return compile_operation(expression, OpCode.Multiply, functions) + return compile_operation(expression, OpCode.Multiply, functions); case ExpressionKind.Division: - return compile_operation(expression, OpCode.Divide, functions) + return compile_operation(expression, OpCode.Divide, functions); case ExpressionKind.FloorDivision: - return compile_operation(expression, OpCode.FloorDivide, functions) + return compile_operation(expression, OpCode.FloorDivide, functions); case ExpressionKind.Modulo: - return compile_operation(expression, OpCode.Modulo, functions) + return compile_operation(expression, OpCode.Modulo, functions); case ExpressionKind.Exponent: - return compile_operation(expression, OpCode.Exponent, functions) + return compile_operation(expression, OpCode.Exponent, functions); case ExpressionKind.Concat: - return compile_operation(expression, OpCode.Concat, functions) + return compile_operation(expression, OpCode.Concat, functions); case ExpressionKind.BitAnd: - return compile_operation(expression, OpCode.BitAnd, functions) + return compile_operation(expression, OpCode.BitAnd, functions); case ExpressionKind.BitOr: - return compile_operation(expression, OpCode.BitOr, functions) + return compile_operation(expression, OpCode.BitOr, functions); case ExpressionKind.BitXOr: - return compile_operation(expression, OpCode.BitXOr, functions) + return compile_operation(expression, OpCode.BitXOr, functions); case ExpressionKind.BitShiftLeft: - return compile_operation(expression, OpCode.BitShiftLeft, functions) + return compile_operation(expression, OpCode.BitShiftLeft, functions); case ExpressionKind.BitShiftRight: - return compile_operation(expression, OpCode.BitShiftRight, functions) + return compile_operation(expression, OpCode.BitShiftRight, functions); case ExpressionKind.Equals: - return compile_operation(expression, OpCode.Equals, functions) + return compile_operation(expression, OpCode.Equals, functions); case ExpressionKind.NotEquals: - return compile_operation(expression, OpCode.NotEquals, functions) + return compile_operation(expression, OpCode.NotEquals, functions); case ExpressionKind.LessThan: - return compile_operation(expression, OpCode.LessThan, functions) + return compile_operation(expression, OpCode.LessThan, functions); case ExpressionKind.LessThanEquals: - return compile_operation(expression, OpCode.LessThanEquals, functions) + return compile_operation(expression, OpCode.LessThanEquals, functions); case ExpressionKind.GreaterThan: - return compile_operation(expression, OpCode.GreaterThan, functions) + return compile_operation(expression, OpCode.GreaterThan, functions); case ExpressionKind.GreaterThanEquals: - return compile_operation(expression, OpCode.GreaterThanEquals, functions) + return compile_operation(expression, OpCode.GreaterThanEquals, functions); case ExpressionKind.And: - return compile_operation(expression, OpCode.And, functions) + return compile_operation(expression, OpCode.And, functions); case ExpressionKind.Or: - return compile_operation(expression, OpCode.Or, functions) + return compile_operation(expression, OpCode.Or, functions); case ExpressionKind.Not: - return compile_unary_operation(expression, OpCode.Not, functions) + return compile_unary_operation(expression, OpCode.Not, functions); case ExpressionKind.Negate: - return compile_unary_operation(expression, OpCode.Negate, functions) + return compile_unary_operation(expression, OpCode.Negate, functions); case ExpressionKind.Length: - return compile_unary_operation(expression, OpCode.Length, functions) + return compile_unary_operation(expression, OpCode.Length, functions); case ExpressionKind.BitNot: - return compile_unary_operation(expression, OpCode.BitNot, functions) - - default: - throw new Error() + return compile_unary_operation(expression, OpCode.BitNot, functions); } } -function compile_assignment(assignment: Assignment | undefined, functions: Op[][]): Op[] +function compile_assignment(assignment: Assignment | undefined, functions: Array>): Array { if (assignment === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + const debug = assignment.token.debug; + + ops.push({ code: OpCode.StartStackChange, debug: debug }); - const ops: Op[] = [] - const debug = assignment.token.debug - ops.push({ code: OpCode.StartStackChange, debug: debug }) for (const rhs of assignment.rhs) - ops.push(...compile_expression(rhs, functions)) - ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }) + { + ops.push(...compile_expression(rhs, functions)); + } + + ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }); for (const lhs of assignment.lhs) { - const debug = lhs.token.debug + const debug = lhs.token.debug; + switch (lhs.kind) { case ExpressionKind.Value: { if (lhs.value?.kind !== ValueKind.Variable) - throw new Error() + { + throw new Error(); + } + + const identifier = make_string(lhs.value?.identifier ?? ""); - const identifier = make_string(lhs.value?.identifier ?? '') if (assignment.local) - ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }) - ops.push({ code: OpCode.Store, arg: identifier, debug: debug }) - break + { + ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }); + } + + ops.push({ code: OpCode.Store, arg: identifier, debug: debug }); + break; } case ExpressionKind.Index: { // FIXME: Throw error here if `assignment.local` is true. I think? - ops.push(...compile_expression(lhs.expression, functions)) - ops.push({ code: OpCode.Swap, debug: debug }) - ops.push(...compile_expression(lhs.index, functions)) - ops.push({ code: OpCode.StoreIndex, debug: debug }) - ops.push({ code: OpCode.Pop, debug: debug }) - break + ops.push(...compile_expression(lhs.expression, functions)); + ops.push({ code: OpCode.Swap, debug: debug }); + ops.push(...compile_expression(lhs.index, functions)); + ops.push({ code: OpCode.StoreIndex, debug: debug }); + ops.push({ code: OpCode.Pop, debug: debug }); + break; } default: - throw new Error() + throw new Error(); } } - return ops + return ops; } -function compile_local(local: Local | undefined): Op[] +function compile_local(local: Local | undefined): Array { if (local === undefined) - throw new Error() - - return local.names - .map(name => ({ - code: OpCode.MakeLocal, - arg: { - data_type: VariableKind.String, - string: name.data, - }, - debug: name.debug, - })) + { + throw new Error(); + } + + return local.names.map( + (name): Op => + { + return { + code: OpCode.MakeLocal, + arg: { + data_type: VariableKind.String, + string: name.data, + }, + debug: name.debug, + }; + } + ); } -function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] +function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + const debug = condition.token.debug; - const ops: Op[] = [] - const debug = condition.token.debug switch (condition.kind) { case ExpressionKind.And: { - const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_conditional_jump(condition.lhs, rhs.length, functions)) - ops.push(...rhs) - break + const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions); + + ops.push(...compile_conditional_jump(condition.lhs, rhs.length, functions)); + ops.push(...rhs); + break; } case ExpressionKind.Or: { - const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) - ops.push(...rhs) - break + const rhs = compile_inverted_conditional_jump(condition.rhs, jump_by, functions); + + ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length + jump_by, functions)); + ops.push(...rhs); + break; } case ExpressionKind.Not: { - ops.push(...compile_expression(condition.expression, functions)) - break + ops.push(...compile_expression(condition.expression, functions)); + break; } default: { - ops.push(...compile_expression(condition, functions)) - ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }) - break + ops.push(...compile_expression(condition, functions)); + ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }); + break; } } - return ops + return ops; } -function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Op[][]): Op[] +function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + const debug = condition.token.debug; - const ops: Op[] = [] - const debug = condition.token.debug switch (condition.kind) { case ExpressionKind.And: { - const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_conditional_jump(condition.lhs, rhs.length + jump_by, functions)) - ops.push(...rhs) - break + const rhs = compile_conditional_jump(condition.rhs, jump_by, functions); + + ops.push(...compile_conditional_jump(condition.lhs, rhs.length + jump_by, functions)); + ops.push(...rhs); + break; } case ExpressionKind.Or: { - const rhs = compile_conditional_jump(condition.rhs, jump_by, functions) - ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length, functions)) - ops.push(...rhs) - break + const rhs = compile_conditional_jump(condition.rhs, jump_by, functions); + + ops.push(...compile_inverted_conditional_jump(condition.lhs, rhs.length, functions)); + ops.push(...rhs); + break; } case ExpressionKind.Not: { - ops.push(...compile_inverted_conditional_jump(condition.expression, jump_by, functions)) - break + ops.push(...compile_inverted_conditional_jump(condition.expression, jump_by, functions)); + break; } default: { - ops.push(...compile_expression(condition, functions)) - ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }) - break + ops.push(...compile_expression(condition, functions)); + ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }); + break; } } - return ops + return ops; } -function compile_if(if_block: IfBlock | undefined, functions: Op[][]): Op[] +function compile_if(if_block: IfBlock | undefined, functions: Array>): Array { if (if_block === undefined) - throw new Error() + { + throw new Error(); + } + + const else_chunk: Array = []; - const else_chunk: Op[] = [] if (if_block.else_body !== undefined) - else_chunk.push(...compile_block(if_block.else_body, functions)) + { + else_chunk.push(...compile_block(if_block.else_body, functions)); + } + + const if_else_chunks: Array> = []; - const if_else_chunks: Op[][] = [] for (const { body, condition, token } of if_block.else_if_bodies.reverse()) { - const ops: Op[] = [] - const if_else_body = compile_block(body, functions) - ops.push(...compile_conditional_jump(condition, if_else_body.length + 1, functions)) - ops.push(...if_else_body) + const ops: Array = []; + const if_else_body = compile_block(body, functions); + + ops.push(...compile_conditional_jump(condition, if_else_body.length + 1, functions)); + ops.push(...if_else_body); + + const offset = if_else_chunks.reduce( + (acc, chunk) => + { + return chunk.length + acc; + }, + 0 + ) + else_chunk.length; - const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: token.debug }) - if_else_chunks.push(ops) + ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: token.debug }); + if_else_chunks.push(ops); } - const debug = if_block.token.debug - const ops: Op[] = [] - const body = compile_block(if_block.body, functions) - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)) - ops.push(...body) + const debug = if_block.token.debug; + const ops: Array = []; + const body = compile_block(if_block.body, functions); + + ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)); + ops.push(...body); + + const offset = if_else_chunks.reduce( + (acc, chunk) => + { + return chunk.length + acc; + }, + 0 + ) + else_chunk.length; + + ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: debug }); - const offset = if_else_chunks.reduce((acc, chunk) => chunk.length + acc, 0) + else_chunk.length - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: debug }) for (const if_else_chunk of if_else_chunks) - ops.push(...if_else_chunk) - ops.push(...else_chunk) - ops.push({ code: OpCode.EndBlock, debug: debug }) + { + ops.push(...if_else_chunk); + } + + ops.push(...else_chunk); + ops.push({ code: OpCode.EndBlock, debug: debug }); - return ops + return ops; } -function replace_breaks(code: Op[], offset_from_end: number): void +function replace_breaks(code: Array, offset_from_end: number): void { for (const [i, op] of code.entries()) { if (op.code === OpCode.Break) { - const offset = code.length - i - 1 + offset_from_end - op.code = OpCode.Jump - op.arg = make_number(offset) + const offset = code.length - i - 1 + offset_from_end; + + op.code = OpCode.Jump; + op.arg = make_number(offset); } } } -function compile_while(while_block: While | undefined, functions: Op[][]): Op[] +function compile_while(while_block: While | undefined, functions: Array>): Array { if (while_block === undefined) - throw new Error() - - const debug = while_block.token.debug - const ops: Op[] = [] - const body = compile_block(while_block.body, functions) - replace_breaks(body, 1) - - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_conditional_jump(while_block.condition, body.length + 1, functions)) - ops.push(...body) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length - 1), debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + { + throw new Error(); + } + + const debug = while_block.token.debug; + const ops: Array = []; + const body = compile_block(while_block.body, functions); + + replace_breaks(body, 1); + + ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push(...compile_conditional_jump(while_block.condition, body.length + 1, functions)); + ops.push(...body); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length - 1), debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); + + return ops; } -function compile_for(for_block: For | undefined, functions: Op[][]): Op[] +function compile_for(for_block: For | undefined, functions: Array>): Array { if (for_block === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + const body = compile_block(for_block.body, functions); + + replace_breaks(body, 1); - const ops: Op[] = [] - const body = compile_block(for_block.body, functions) - replace_breaks(body, 1) + const debug = for_block.token.debug; - const debug = for_block.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push({ code: OpCode.StartStackChange, debug: debug }) - ops.push(...compile_expression(for_block.iterator, functions)) - ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }) + ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push({ code: OpCode.StartStackChange, debug: debug }); + ops.push(...compile_expression(for_block.iterator, functions)); + ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }); - const after_creating_itorator = ops.length - ops.push({ code: OpCode.StartStackChange, debug: debug }) - ops.push({ code: OpCode.IterNext, debug: debug }) - ops.push({ code: OpCode.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }) + const after_creating_itorator = ops.length; + + ops.push({ code: OpCode.StartStackChange, debug: debug }); + ops.push({ code: OpCode.IterNext, debug: debug }); + ops.push({ code: OpCode.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }); + + ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }); - ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }) for (const [i, item] of [...for_block.items].reverse().entries()) { if (i === for_block.items.length - 1) - ops.push({ code: OpCode.IterUpdateState, debug: debug }) - ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }) + { + ops.push({ code: OpCode.IterUpdateState, debug: debug }); + } + + ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }); } - ops.push(...body) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) - ops.push({ code: OpCode.EndStackChange, arg: make_number(0), debug: debug }) - ops.push({ code: OpCode.Pop, arg: make_number(3), debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + ops.push(...body); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); + + ops.push({ code: OpCode.EndStackChange, arg: make_number(0), debug: debug }); + ops.push({ code: OpCode.Pop, arg: make_number(3), debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); + + return ops; } -function compile_step(step: Expression | undefined, functions: Op[][]): Op[] +function compile_step(step: Expression | undefined, functions: Array>): Array { if (step === undefined) - return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }] + { + return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }]; + } - return compile_expression(step, functions) + return compile_expression(step, functions); } -function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Op[][]): Op[] +function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Array>): Array { if (numeric_for_block === undefined) - throw new Error() - - const ops: Op[] = [] - const body = compile_block(numeric_for_block.body, functions) - const step = compile_step(numeric_for_block.step, functions) - const index = numeric_for_block.index.data - const debug = numeric_for_block.index.debug - replace_breaks(body, step.length + 4) - - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_expression(numeric_for_block.start, functions)) - - const after_creating_itorator = ops.length - ops.push({ code: OpCode.Dup, debug: debug }) - ops.push(...compile_expression(numeric_for_block.end, functions)) - ops.push({ code: OpCode.NotEquals, debug: debug }) - ops.push({ code: OpCode.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }) - - ops.push({ code: OpCode.Store, arg: make_string(index), debug: debug }) - ops.push(...body) - ops.push({ code: OpCode.Load, arg: make_string(index), debug: debug }) - ops.push(...step) - ops.push({ code: OpCode.Add, debug: debug }) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }) - - ops.push({ code: OpCode.Pop, debug: debug }) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + { + throw new Error(); + } + + const ops: Array = []; + const body = compile_block(numeric_for_block.body, functions); + const step = compile_step(numeric_for_block.step, functions); + const index = numeric_for_block.index.data; + const debug = numeric_for_block.index.debug; + + replace_breaks(body, step.length + 4); + + ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push(...compile_expression(numeric_for_block.start, functions)); + + const after_creating_itorator = ops.length; + + ops.push({ code: OpCode.Dup, debug: debug }); + ops.push(...compile_expression(numeric_for_block.end, functions)); + ops.push({ code: OpCode.NotEquals, debug: debug }); + ops.push({ code: OpCode.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }); + + ops.push({ code: OpCode.Store, arg: make_string(index), debug: debug }); + ops.push(...body); + ops.push({ code: OpCode.Load, arg: make_string(index), debug: debug }); + ops.push(...step); + ops.push({ code: OpCode.Add, debug: debug }); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); + + ops.push({ code: OpCode.Pop, debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); + + return ops; } -function compile_repeat(repeat: Repeat | undefined, functions: Op[][]): Op[] +function compile_repeat(repeat: Repeat | undefined, functions: Array>): Array { if (repeat === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; + const debug = repeat.token.debug; + + ops.push({ code: OpCode.StartBlock, debug: debug }); - const ops: Op[] = [] - const debug = repeat.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) + ops.push(...compile_block(repeat.body, functions)); + ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }); - ops.push(...compile_block(repeat.body, functions)) - ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)) - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }) + ops.push({ code: OpCode.EndBlock, debug: debug }); - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + return ops; } -function compile_do(do_block: Do | undefined, functions: Op[][]): Op[] +function compile_do(do_block: Do | undefined, functions: Array>): Array { if (do_block === undefined) - throw new Error() - - const ops: Op[] = [] - const debug = do_block.token.debug - ops.push({ code: OpCode.StartBlock, debug: debug }) - ops.push(...compile_block(do_block.body, functions)) - ops.push({ code: OpCode.EndBlock, debug: debug }) - return ops + { + throw new Error(); + } + + const ops: Array = []; + const debug = do_block.token.debug; + + ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push(...compile_block(do_block.body, functions)); + ops.push({ code: OpCode.EndBlock, debug: debug }); + + return ops; } -function compile_return(return_block: Return | undefined, functions: Op[][]): Op[] +function compile_return(return_block: Return | undefined, functions: Array>): Array { if (return_block === undefined) - throw new Error() + { + throw new Error(); + } + + const ops: Array = []; - const ops: Op[] = [] for (const value of return_block.values) - ops.push(...compile_expression(value, functions)) + { + ops.push(...compile_expression(value, functions)); + } + + const debug = return_block.token.debug; + const return_count = return_block.values.length; - const debug = return_block.token.debug - const return_count = return_block.values.length - ops.push({ code: OpCode.Return, arg: make_number(return_count), debug: debug }) - return ops + ops.push({ code: OpCode.Return, arg: make_number(return_count), debug: debug }); + + return ops; } interface ChunkResult { - code: Op[] - has_last_expression: boolean, + code: Array; + has_last_expression: boolean; } -function compile_block(chunk: Chunk, functions: Op[][]): Op[] +function compile_block(chunk: Chunk, functions: Array>): Array { - const { code, has_last_expression } = compile_chunk(chunk, functions) + const { code, has_last_expression } = compile_chunk(chunk, functions); + if (has_last_expression) - code.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) + { + code.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }); + } - return code + return code; } -function compile_chunk(chunk: Chunk, functions: Op[][]): ChunkResult +function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult { - const ops = [] - let has_last_expression = false + const ops = []; + let has_last_expression = false; for (const [index, statement] of chunk.statements.entries()) { - const is_last_statement = (index === chunk.statements.length - 1) + const is_last_statement = index === chunk.statements.length - 1; + switch (statement.kind) { case StatementKind.Empty: - break + break; case StatementKind.Expression: - ops.push(...compile_expression(statement.expression, functions)) + ops.push(...compile_expression(statement.expression, functions)); + if (statement.expression === undefined) - break + { + break; + } if (is_last_statement) - has_last_expression = true + { + has_last_expression = true; + } else - ops.push({ code: OpCode.Pop, debug: statement.expression.token.debug }) - break + { + ops.push({ code: OpCode.Pop, debug: statement.expression.token.debug }); + } + + break; case StatementKind.Assignment: - ops.push(...compile_assignment(statement.assignment, functions)) - break + ops.push(...compile_assignment(statement.assignment, functions)); + break; case StatementKind.Local: - ops.push(...compile_local(statement.local)) - break + ops.push(...compile_local(statement.local)); + break; case StatementKind.If: - ops.push(...compile_if(statement.if, functions)) - break + ops.push(...compile_if(statement.if, functions)); + break; case StatementKind.While: - ops.push(...compile_while(statement.while, functions)) - break + ops.push(...compile_while(statement.while, functions)); + break; case StatementKind.For: - ops.push(...compile_for(statement.for, functions)) - break + ops.push(...compile_for(statement.for, functions)); + break; case StatementKind.NumericFor: - ops.push(...compile_numeric_for(statement.numeric_for, functions)) - break + ops.push(...compile_numeric_for(statement.numeric_for, functions)); + break; case StatementKind.Repeat: - ops.push(...compile_repeat(statement.repeat, functions)) - break + ops.push(...compile_repeat(statement.repeat, functions)); + break; case StatementKind.Do: - ops.push(...compile_do(statement.do, functions)) - break + ops.push(...compile_do(statement.do, functions)); + break; case StatementKind.Return: - ops.push(...compile_return(statement.return, functions)) - break + ops.push(...compile_return(statement.return, functions)); + break; case StatementKind.Break: - ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }) - break + ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }); + break; } } return { code: ops, has_last_expression: has_last_expression, - } + }; } -function link(code: Op[], function_id: number, location: number): void +function link(code: Array, function_id: number, location: number): void { for (const op of code) { - if (op.arg?.data_type === VariableKind.Function && - op.arg?.function_id === function_id) + if (op.arg?.data_type === VariableKind.Function + && op.arg?.function_id === function_id) { - op.arg.function_id = location + op.arg.function_id = location; } } } -export function compile(chunk: Chunk, extend?: Op[]): Program +export function compile(chunk: Chunk, extend?: Array): Program { - const ops = [...(extend ?? [])] - const functions: Op[][] = [] - const { code, has_last_expression } = compile_chunk(chunk, functions) + const ops = [...(extend ?? [])]; + const functions: Array> = []; + const { code, has_last_expression } = compile_chunk(chunk, functions); + + const function_locations: Array = []; - const function_locations: number[] = [] for (const func of functions) { - function_locations.push(ops.length) - ops.push(...func) + function_locations.push(ops.length); + ops.push(...func); } for (const [id, location] of function_locations.entries()) - link(code, id, location) + { + link(code, id, location); + } + + const start = ops.length; - const start = ops.length if (extend?.length ?? 0 > 0) - ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }) - ops.push(...code) + { + ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }); + } + + ops.push(...code); + if (!has_last_expression) - ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }) + { + ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }); + } return { code: ops, start: start, - } + }; } diff --git a/src/create-binding.mts b/src/create-binding.mts index 2671002..1b38e88 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -1,4 +1,4 @@ -import { assertArray, assertDefined, isInstanceOf, isNullish, ValidationError } from "@vitruvius-labs/ts-predicate"; +import { ValidationError, assertArray, assertDefined, isInstanceOf, isNullish } from "@vitruvius-labs/ts-predicate"; import type { Engine } from "./engine.mjs"; import { make_variable } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; @@ -18,8 +18,8 @@ export type ParameterOptionEnum = typeof ParameterOptionEnum[keyof typeof Parame export interface ParameterDescriptorInterface { - test: (value: unknown) => asserts value is T, - option?: ParameterOptionEnum, + test: (value: unknown) => asserts value is T; + option?: ParameterOptionEnum; } function sanitize_parameters(input: Array, descriptors: Array): Array diff --git a/src/engine.mts b/src/engine.mts index 7ee549b..dba133f 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -25,48 +25,58 @@ import { assertVariable } from "./variable/predicate/assert-variable.mjs"; function index(val: Variable | undefined): string | number | undefined { if (val === undefined) - return undefined + { + return undefined; + } if (val.data_type === VariableKind.String) - return val.string + { + return val.string; + } if (val.data_type === VariableKind.Number) - return val.number + { + return val.number; + } - return undefined + return undefined; } function is_true(val: Variable | undefined): boolean { if (isNil(val)) - return false + { + return false; + } if (isVariableKind(val, VariableKind.Boolean)) - return val.boolean + { + return val.boolean; + } - return true + return true; } export interface LuaOptions { - trace?: boolean - trace_instructions?: boolean - trace_stack?: boolean - locals?: Map, + trace?: boolean; + trace_instructions?: boolean; + trace_stack?: boolean; + locals?: Map; } export class Engine { - private program: Op[]; - private globals: VariableTableMapType; + private program: Array; + private readonly globals: VariableTableMapType; private start_ip: number = 0; private ip: number = 0; - private stack: Variable[] = []; - private locals_stack: Map[] = []; - private locals_capture: Map[] = []; - private call_stack: number[] = []; - private assign_height_stack: number[] = []; + private stack: Array = []; + private locals_stack: Array> = []; + private locals_capture: Array> = []; + private call_stack: Array = []; + private assign_height_stack: Array = []; private error: Error | undefined; @@ -75,58 +85,79 @@ export class Engine globals?: VariableTableMapType ) { - this.program = [] - this.globals = globals ?? std.std_lib() - this.error = undefined - this.reset() + this.program = []; + this.globals = globals ?? std.std_lib(); + this.error = undefined; + this.reset(); if (script !== undefined) { - const result = this.load(script) + const result = this.load(script); + if (result !== undefined) - this.error = result + { + this.error = result; + } } } load(chunk: string): undefined | Error { - const stream = new TokenStream() - stream.feed(chunk) + const stream = new TokenStream(); + + stream.feed(chunk); + + const ast = parse(stream); - const ast = parse(stream) if (ast instanceof Error) - return ast + { + return ast; + } + + optimize_chunk(ast); + const program = compile(ast, this.program); - optimize_chunk(ast) - const program = compile(ast, this.program) - this.program = program.code - this.ip = program.start - this.start_ip = program.start + this.program = program.code; + this.ip = program.start; + this.start_ip = program.start; - return undefined + return undefined; } - bytecode(): string[] + bytecode(): Array { if (this.error !== undefined) - return [ this.error.message ] + { + return [this.error.message]; + } + + const output = []; - const output = [] for (const [i, op] of this.program.entries()) { - const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : '' + const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : ""; + if (i === this.ip) - output.push(`* ${ i } ${ op_code_name(op.code) } ${ arg }`) + { + output.push(`* ${i} ${op_code_name(op.code)} ${arg}`); + } else - output.push(`${ i } ${ op_code_name(op.code) } ${ arg }`) + { + output.push(`${i} ${op_code_name(op.code)} ${arg}`); + } } - return output + return output; } global(name: string): Variable | undefined { - return this.globals.get(name) + return this.globals.get(name); + } + + set(name: string, variable: Variable): void + { + this.globals.set(name, variable); } define(name: string, func: NativeFunction): void @@ -134,7 +165,7 @@ export class Engine this.globals.set(name, { data_type: VariableKind.NativeFunction, native_function: func, - }) + }); } define_table(name: string, table: VariableTableMapType): void @@ -142,75 +173,93 @@ export class Engine this.globals.set(name, { data_type: VariableKind.Table, table: table, - }) + }); } reset(): void { - this.ip = this.start_ip - this.stack = [] - this.locals_stack = [] - this.locals_capture = [] - this.call_stack = [] - this.assign_height_stack = [] - - this.locals_stack.push(new Map()) + this.ip = this.start_ip; + this.stack = []; + this.locals_stack = []; + this.locals_capture = []; + this.call_stack = []; + this.assign_height_stack = []; + + this.locals_stack.push(new Map()); } - async call(func: Variable, ...args: Variable[]): Promise + async call(func: Variable, ...args: Array): Promise | Error> { if (isVariableKind(func, VariableKind.NativeFunction)) - return await this.call_native_function(func.native_function, ...args) + { + return await this.call_native_function(func.native_function, ...args); + } assertUnion(func, [unary(assertVariableKind, VariableKind.Function), unary(assertVariableKind, VariableKind.NativeFunction)]); - const old_stack = this.stack - const old_call_stack = this.call_stack - const old_ip = this.ip - const old_locals_stack = this.locals_stack - this.stack = [] - this.call_stack = [] - this.locals_stack = [...func.locals ?? []] - this.ip = func.function_id ?? this.ip + const old_stack = this.stack; + const old_call_stack = this.call_stack; + const old_ip = this.ip; + const old_locals_stack = this.locals_stack; + + this.stack = []; + this.call_stack = []; + this.locals_stack = [...func.locals ?? []]; + this.ip = func.function_id ?? this.ip; for (const arg of args) - this.stack.push(arg) + { + this.stack.push(arg); + } - this.stack.push(make_number(args.length)) - this.locals_stack.push(new Map()) + this.stack.push(make_number(args.length)); + this.locals_stack.push(new Map()); - const result = await this.run() - const return_values = this.stack - this.stack = old_stack - this.call_stack = old_call_stack - this.locals_stack = old_locals_stack - this.ip = old_ip + const result = await this.run(); + const return_values = this.stack; + + this.stack = old_stack; + this.call_stack = old_call_stack; + this.locals_stack = old_locals_stack; + this.ip = old_ip; if (result instanceof Error) - return result + { + return result; + } - return return_values + return return_values; } async run_for_steps(steps: number, options?: LuaOptions): Promise { if (this.error !== undefined) - return this.error + { + return this.error; + } if (options?.locals !== undefined) - this.locals_stack.push(options.locals) + { + this.locals_stack.push(options.locals); + } + + let step_count = 0; - let step_count = 0 while (this.ip < this.program.length) { - const result = await this.step(options) + const result = await this.step(options); if (result !== undefined) - return result + { + return result; + } + + step_count = step_count + 1; - step_count += 1 if (step_count >= steps) - return undefined + { + return undefined; + } } return this.stack_get(0); @@ -218,18 +267,21 @@ export class Engine async run(options?: LuaOptions): Promise { - const result = await this.run_for_steps(1000, options) + const result = await this.run_for_steps(1000, options); if (result === undefined) - return new Error('Program ran for too long') - else - return result + { + return new Error("Program ran for too long"); + } + + return result; } raise_error(message: string): void { - const op = this.program.at(this.ip - 1) - this.error = this.runtime_error(op, message) + const op = this.program.at(this.ip - 1); + + this.error = this.runtime_error(op, message); } private stack_get(index: number): Variable @@ -267,17 +319,18 @@ export class Engine private async call_native_function(native_function: NativeFunction, ...args: Array): Promise | Error> { - const results = await native_function(this, ...args) + const results = await native_function(this, ...args); if (this.error !== undefined) { - const error = this.error - this.error = undefined + const error = this.error; - return error + this.error = undefined; + + return error; } - return results + return results; } private operation(op: (x: number, y: number) => number): void @@ -285,7 +338,7 @@ export class Engine const x = this.stack_pop_kind(VariableKind.Number); const y = this.stack_pop_kind(VariableKind.Number); - this.stack.push(make_number(op(x.number, y.number))) + this.stack.push(make_number(op(x.number, y.number))); } private compare(op: (x: number, y: number) => boolean): void @@ -293,150 +346,257 @@ export class Engine const x = this.stack_pop_kind(VariableKind.Number); const y = this.stack_pop_kind(VariableKind.Number); - this.stack.push(make_boolean(op(x.number, y.number))) + this.stack.push(make_boolean(op(x.number, y.number))); } private force_stack_height(expected: number, got: number): void { for (let i = got; i < expected; ++i) - this.stack.push(nil) + { + this.stack.push(nil); + } for (let i = expected; i < got; ++i) - this.stack.pop() + { + this.stack.pop(); + } } private runtime_error(op: Op | undefined, message: string): never { if (op === undefined) - throw new RuntimeError(`${ message }`) + { + throw new RuntimeError(message); + } - throw new RuntimeError(message, {}, op.debug) + throw new RuntimeError(message, {}, op.debug); } private async run_instruction(op: Op): Promise { - const { code, arg } = op + const { code, arg } = op; - switch(code) + switch (code) { case OpCode.Pop: { - const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 - this.stack.splice(this.stack.length - count, count) - break + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; + + this.stack.splice(this.stack.length - count, count); + break; } case OpCode.Dup: { - const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 - const items = this.stack.splice(this.stack.length - count, count) - this.stack.push(...items, ...items) - break + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; + const items = this.stack.splice(this.stack.length - count, count); + + this.stack.push(...items, ...items); + break; } case OpCode.Swap: { - const x = this.stack.splice(this.stack.length - 2, 1) - this.stack.push(...x) - break + const x = this.stack.splice(this.stack.length - 2, 1); + + this.stack.push(...x); + break; } case OpCode.IterUpdateState: { - this.stack[this.stack.length - 2] = this.stack_get(-1) - break + this.stack[this.stack.length - 2] = this.stack_get(-1); + break; } case OpCode.IterNext: { - const state = this.stack_get(-1) - const control = this.stack_get(-2) - const iter = this.stack_get(-3) + const state = this.stack_get(-1); + const control = this.stack_get(-2); + const iter = this.stack_get(-3); if (isVariableKind(iter, VariableKind.NativeFunction)) { - const result = await this.call_native_function(iter.native_function, control, state) + const result = await this.call_native_function(iter.native_function, control, state); if (result instanceof Error) - return result + { + return result; + } - this.stack.push(...result) - break + this.stack.push(...result); + break; } - this.stack.push(control, state, make_number(2)) - this.call_stack.push(this.ip) - this.locals_stack.push(new Map()) - this.locals_capture = iter.locals ?? [] + this.stack.push(control, state, make_number(2)); + this.call_stack.push(this.ip); + this.locals_stack.push(new Map()); + this.locals_capture = iter.locals ?? []; if (isVariableKind(iter, VariableKind.Function) && iter.function_id !== undefined) { this.ip = iter.function_id; } - break + break; } case OpCode.IterJumpIfDone: { if (isVariableKind(arg, VariableKind.Number) && isNil(this.stack_get(-1))) { - this.ip += arg.number + this.ip = this.ip + arg.number; } - break + break; } case OpCode.Add: - this.operation((x, y) => x + y); - break + this.operation( + (x, y) => + { + return x + y; + } + ); + + break; case OpCode.Subtract: - this.operation((x, y) => x - y); - break + this.operation( + (x, y) => + { + return x - y; + } + ); + + break; case OpCode.Multiply: - this.operation((x, y) => x * y); - break + this.operation( + (x, y) => + { + return x * y; + } + ); + + break; case OpCode.Divide: - this.operation((x, y) => x / y); - break + this.operation( + (x, y) => + { + return x / y; + } + ); + + break; case OpCode.FloorDivide: - this.operation((x, y) => Math.floor(x / y)); - break + this.operation( + (x, y) => + { + return Math.floor(x / y); + } + ); + + break; case OpCode.Modulo: - this.operation((x, y) => x % y); - break + this.operation( + (x, y) => + { + return x % y; + } + ); + + break; case OpCode.Exponent: - this.operation((x, y) => Math.pow(x, y)); - break + this.operation( + (x, y) => + { + return Math.pow(x, y); + } + ); + + break; case OpCode.LessThan: - this.compare((x, y) => x < y); - break + this.compare( + (x, y) => + { + return x < y; + } + ); + + break; case OpCode.LessThanEquals: - this.compare((x, y) => x <= y); - break + this.compare( + (x, y) => + { + return x <= y; + } + ); + + break; case OpCode.GreaterThan: - this.compare((x, y) => x > y); - break + this.compare( + (x, y) => + { + return x > y; + } + ); + + break; case OpCode.GreaterThanEquals: - this.compare((x, y) => x >= y); - break + this.compare( + (x, y) => + { + return x >= y; + } + ); + + break; case OpCode.BitAnd: - this.operation((x, y) => x & y); - break + this.operation( + (x, y) => + { + return x & y; + } + ); + + break; case OpCode.BitOr: - this.operation((x, y) => x | y); - break + this.operation( + (x, y) => + { + return x | y; + } + ); + + break; case OpCode.BitXOr: - this.operation((x, y) => x ^ y); - break + this.operation( + (x, y) => + { + return x ^ y; + } + ); + + break; case OpCode.BitShiftLeft: - this.operation((x, y) => x << y); - break + this.operation( + (x, y) => + { + return x << y; + } + ); + + break; case OpCode.BitShiftRight: - this.operation((x, y) => x >> y); - break + this.operation( + (x, y) => + { + return x >> y; + } + ); + + break; case OpCode.Concat: { @@ -445,24 +605,26 @@ export class Engine const result = std.variable_to_string(x) + std.variable_to_string(y); - this.stack.push(make_string(result)) - break + this.stack.push(make_string(result)); + break; } case OpCode.Equals: { const x = this.stack_pop(); const y = this.stack_pop(); - this.stack.push(make_boolean(equals(x, y))) - break + + this.stack.push(make_boolean(equals(x, y))); + break; } case OpCode.NotEquals: { const x = this.stack_pop(); const y = this.stack_pop(); - this.stack.push(make_boolean(!equals(x, y))) - break + + this.stack.push(make_boolean(!equals(x, y))); + break; } case OpCode.And: @@ -471,8 +633,9 @@ export class Engine const y = this.stack_pop(); const result = is_true(x) ? y : x; - this.stack.push(result) - break + + this.stack.push(result); + break; } case OpCode.Or: @@ -481,279 +644,342 @@ export class Engine const y = this.stack_pop(); const result = is_true(x) ? x : y; - this.stack.push(result) - break + + this.stack.push(result); + break; } case OpCode.Not: this.stack.push(make_boolean(!is_true(this.stack_pop()))); - break + break; case OpCode.BitNot: - this.stack.push(make_number(~(this.stack_pop_kind(VariableKind.Number).number))); - break + this.stack.push(make_number(~this.stack_pop_kind(VariableKind.Number).number)); + break; case OpCode.Negate: - this.stack.push(make_number(-(this.stack_pop_kind(VariableKind.Number).number))); - break + this.stack.push(make_number(-this.stack_pop_kind(VariableKind.Number).number)); + break; case OpCode.IsNotNil: this.stack.push(make_boolean(!isNil(this.stack_pop()))); - break + break; case OpCode.Jump: if (isVariableKind(arg, VariableKind.Number)) { - this.ip += arg.number; + this.ip = this.ip + arg.number; } - break + + break; case OpCode.JumpIfNot: if (isVariableKind(arg, VariableKind.Number) && !is_true(this.stack_pop())) { - this.ip += arg.number + this.ip = this.ip + arg.number; } - break + + break; case OpCode.JumpIf: if (isVariableKind(arg, VariableKind.Number) && is_true(this.stack_pop())) { - this.ip += arg.number + this.ip = this.ip + arg.number; } - break + + break; case OpCode.MakeLocal: - const last_locals = this.locals_stack.at(-1) + const last_locals = this.locals_stack.at(-1); if (last_locals) { - last_locals.set(isVariableKind(arg, VariableKind.String) ? arg.string : '', nil); + last_locals.set(isVariableKind(arg, VariableKind.String) ? arg.string : "", nil); } - break + + break; case OpCode.NewTable: this.stack.push(make_table()); - break + break; case OpCode.StartBlock: this.locals_stack.push(new Map()); - break + break; case OpCode.EndBlock: this.locals_stack.pop(); - break + break; case OpCode.Length: { - const variable = this.stack_pop_maybe() + const variable = this.stack_pop_maybe(); switch (variable.data_type) { case VariableKind.String: - this.stack.push(make_number(variable.string.length)) - break + this.stack.push(make_number(variable.string.length)); + break; case VariableKind.Table: - this.stack.push(make_number(std.table_size(variable))) - break + this.stack.push(make_number(std.table_size(variable))); + break; + default: - this.runtime_error(op, `Attempt to get length of a ${ variable.data_type } value`) + this.runtime_error(op, `Attempt to get length of a ${variable.data_type} value`); } - break + + break; } case OpCode.Return: { - this.ip = this.call_stack.pop() ?? this.program.length - this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()) - this.locals_capture = [] - break + this.ip = this.call_stack.pop() ?? this.program.length; + this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()); + this.locals_capture = []; + break; } case OpCode.LoadIndex: { - const table = this.stack_pop_maybe() + const table = this.stack_pop_maybe(); if (isNil(table)) { - this.runtime_error(op, 'Attempt to index a nil value') + this.runtime_error(op, "Attempt to index a nil value"); } assertVariableKind(table, VariableKind.Table); - const i_var = this.stack_pop_maybe() + const i_var = this.stack_pop_maybe(); if (isNil(i_var)) { - this.runtime_error(op, 'Attempt to index with a nil value') + this.runtime_error(op, "Attempt to index with a nil value"); } - const i = index(i_var) + const i = index(i_var); if (i === undefined) - this.runtime_error(op, 'Invalid index, must be a number or string') + { + this.runtime_error(op, "Invalid index, must be a number or string"); + } - this.stack.push(table.table.get(i) ?? nil) - break + this.stack.push(table.table.get(i) ?? nil); + break; } case OpCode.StoreIndex: { - const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1 - const table = this.stack_get(- 1 - count * 2) + const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; + const table = this.stack_get(-1 - count * 2); assertVariableKind(table, VariableKind.Table); for (let i = 0; i < count; ++i) { - const key = index(this.stack_pop_maybe()) - const value = this.stack_pop_maybe() + const key = index(this.stack_pop_maybe()); + const value = this.stack_pop_maybe(); if (key === undefined) - return this.runtime_error(op, 'Invalid key, must be a number or string') + { + return this.runtime_error(op, "Invalid key, must be a number or string"); + } - table.table.set(key, value) + table.table.set(key, value); } - break + break; } case OpCode.Store: { - const name = isVariableKind(arg, VariableKind.String) ? arg.string : '' - const value = this.stack_pop_maybe() - const local = [...this.locals_capture, ...this.locals_stack].findLast(x => x.has(name)) + const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; + const value = this.stack_pop_maybe(); + const local = [...this.locals_capture, ...this.locals_stack].findLast( + (x) => + { + return x.has(name); + } + ); if (local !== undefined) - local.set(name, value) + { + local.set(name, value); + } else - this.globals.set(name, value) - break + { + this.globals.set(name, value); + } + + break; } case OpCode.Push: { if (this.locals_stack.length > 0 && isVariableKind(arg, VariableKind.Function)) - arg.locals = [...this.locals_stack] - this.stack.push(arg ?? nil) - break + { + arg.locals = [...this.locals_stack]; + } + + this.stack.push(arg ?? nil); + break; } case OpCode.Load: { - const name = isVariableKind(arg, VariableKind.String) ? arg.string : '' + const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; const local = [...this.locals_capture, ...this.locals_stack] - .map(x => x.get(name)) - .findLast(x => !isNil(x)) + .map( + (x) => + { + return x.get(name); + } + ) + .findLast( + (x) => + { + return !isNil(x); + } + ); + + const global = this.globals.get(name); - const global = this.globals.get(name) - this.stack.push(local ?? global ?? nil) - break + this.stack.push(local ?? global ?? nil); + break; } case OpCode.Call: { const x = this.stack_pop_maybe(); - const count = isVariableKind(x, VariableKind.Number) ? x.number : 0 - const func_var = this.stack_pop_maybe() + const count = isVariableKind(x, VariableKind.Number) ? x.number : 0; + const func_var = this.stack_pop_maybe(); switch (func_var.data_type) { case VariableKind.NativeFunction: { - const args = this.stack.splice(this.stack.length - count, count) + const args = this.stack.splice(this.stack.length - count, count); + if (func_var.native_function !== undefined) { - const result = await this.call_native_function(func_var.native_function, ...args) + const result = await this.call_native_function(func_var.native_function, ...args); if (result instanceof Error) - return result + { + return result; + } - this.stack.push(...result) + this.stack.push(...result); } if (this.error !== undefined) { - const error = this.error - this.error = undefined - return error + const error = this.error; + + this.error = undefined; + + return error; } - break + + break; } case VariableKind.Function: { - this.stack.push(make_number(count)) - this.call_stack.push(this.locals_stack.length, this.ip) - this.locals_stack.push(new Map()) - this.locals_capture = func_var.locals ?? [] - this.ip = func_var.function_id ?? this.ip - break + this.stack.push(make_number(count)); + this.call_stack.push(this.locals_stack.length, this.ip); + this.locals_stack.push(new Map()); + this.locals_capture = func_var.locals ?? []; + this.ip = func_var.function_id ?? this.ip; + break; } default: { - return this.runtime_error(op, `Object of type '${ func_var.data_type }' is not callable`) + return this.runtime_error(op, `Object of type '${func_var.data_type}' is not callable`); } } - break + break; } case OpCode.ArgumentCount: { const x = this.stack_pop_maybe(); - const got = isVariableKind(x, VariableKind.Number) ? x.number : 0 - const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0 - this.force_stack_height(expected, got) - break + const got = isVariableKind(x, VariableKind.Number) ? x.number : 0; + const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0; + + this.force_stack_height(expected, got); + break; } case OpCode.StartStackChange: { - this.assign_height_stack.push(this.stack.length) - break + this.assign_height_stack.push(this.stack.length); + break; } case OpCode.EndStackChange: { - const got = this.stack.length - (this.assign_height_stack.pop() ?? 0) - const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0 - this.force_stack_height(expected, got) - break + const got = this.stack.length - (this.assign_height_stack.pop() ?? 0); + const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0; + + this.force_stack_height(expected, got); + break; } } - return undefined + return undefined; } async step(options?: LuaOptions): Promise { if (this.error !== undefined) - return this.error + { + return this.error; + } if (this.ip >= this.program.length) - return + { + return undefined; + } - const op = this.program[this.ip] - ++this.ip + const op = this.program[this.ip]; + + ++this.ip; if (op === undefined) - return this.runtime_error(op, 'Instruction pointer out of bounds') + { + this.runtime_error(op, "Instruction pointer out of bounds"); + } if (options?.trace || options?.trace_instructions) { - const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : '' - console.log(this.ip - 1, op_code_name(op.code), arg) + const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : ""; + + console.log(this.ip - 1, op_code_name(op.code), arg); } - const result = await this.run_instruction(op) + const result = await this.run_instruction(op); if (result !== undefined) - return result + { + return result; + } if (options?.trace || options?.trace_stack) - console.log(this.ip - 1, ...this.stack.map(x => std.variable_to_string(x))) + { + console.log( + this.ip - 1, + ...this.stack.map( + (x) => + { + return std.variable_to_string(x); + } + ) + ); + } - return undefined + return undefined; } } diff --git a/src/lexer.mts b/src/lexer.mts index 778a49b..a2bf6f9 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,264 +1,272 @@ export enum State { - Initial, - Identifier, - StringLiteral, - StringLiteralEscape, - MultiLineString, - NumberLiteral, - NumberLiteralDot, - NumberLiteralExpSign, - NumberLiteralExp, - NumberHex, - Comment, + Initial = "initial", + Identifier = "identifier", + StringLiteral = "string-literal", + StringLiteralEscape = "string-literal-escape", + MultiLineString = "multi-line-string", + NumberLiteral = "number-literal", + NumberLiteralDot = "number-literal-dot", + NumberLiteralExpSign = "number-literal-exp-sign", + NumberLiteralExp = "number-literal-exp", + NumberHex = "number-hex", + Comment = "comment", } export enum TokenKind { - EOF, - NotFinished, - - Identifier, - StringLiteral, - BooleanLiteral, - NumberLiteral, - NilLiteral, - - OpenBrace, - CloseBrace, - OpenSquare, - CloseSquare, - SquiglyOpen, - SquiglyClose, - - Addition, - Subtract, - Multiply, - Division, - FloorDivision, - Modulo, - Exponent, - Concat, - Hash, - - BitAnd, - BitOr, - BitXOrNot, - BitShiftLeft, - BitShiftRight, - - Equals, - NotEquals, - LessThan, - LessThanEquals, - GreaterThan, - GreaterThanEquals, - And, - Or, - Not, - - Assign, - Semicolon, - Comma, - Dot, - - Function, - If, - While, - For, - Repeat, - In, - Do, - Then, - ElseIf, - Else, - Until, - End, - Return, - Break, - Local, + EOF = "EOF", + NotFinished = "not-finished", + + Identifier = "identifier", + StringLiteral = "string-literal", + BooleanLiteral = "boolean-literal", + NumberLiteral = "number-literal", + NilLiteral = "nil-literal", + + OpenBrace = "open-brace", + CloseBrace = "close-brace", + OpenSquare = "open-square", + CloseSquare = "close-square", + SquiglyOpen = "squigly-open", + SquiglyClose = "squigly-close", + + Addition = "addition", + Subtract = "subtract", + Multiply = "multiply", + Division = "division", + FloorDivision = "floor-division", + Modulo = "modulo", + Exponent = "exponent", + Concat = "concat", + Hash = "hash", + + BitAnd = "bit-and", + BitOr = "bit-or", + BitXOrNot = "bit-xor-not", + BitShiftLeft = "bit-shift-left", + BitShiftRight = "bit-shift-right", + + Equals = "equals", + NotEquals = "not-equals", + LessThan = "less-than", + LessThanEquals = "less-than-equals", + GreaterThan = "greater-than", + GreaterThanEquals = "greater-than-equals", + And = "and", + Or = "or", + Not = "not", + + Assign = "assign", + Semicolon = "semicolon", + Comma = "comma", + Dot = "dot", + + Function = "function", + If = "if", + While = "while", + For = "for", + Repeat = "repeat", + In = "in", + Do = "do", + Then = "then", + ElseIf = "elseif", + Else = "else", + Until = "until", + End = "end", + Return = "return", + Break = "break", + Local = "local", } -export function token_kind_to_string(kind: TokenKind): string | undefined +export function token_kind_to_string(kind: TokenKind): string { - switch(kind) + switch (kind) { - case TokenKind.EOF: return 'EOF' - case TokenKind.NotFinished: return 'NotFinished' - case TokenKind.Identifier: return 'Identifier' - case TokenKind.StringLiteral: return 'StringLiteral' - case TokenKind.BooleanLiteral: return 'BooleanLiteral' - case TokenKind.NumberLiteral: return 'NumberLiteral' - case TokenKind.NilLiteral: return 'nil' - case TokenKind.OpenBrace: return '(' - case TokenKind.CloseBrace: return ')' - case TokenKind.OpenSquare: return '[' - case TokenKind.CloseSquare: return ']' - case TokenKind.SquiglyOpen: return '{' - case TokenKind.SquiglyClose: return '}' - case TokenKind.Addition: return '+' - case TokenKind.Subtract: return '-' - case TokenKind.Multiply: return '*' - case TokenKind.Division: return '/' - case TokenKind.FloorDivision: return '//' - case TokenKind.Modulo: return '%' - case TokenKind.Exponent: return '^' - case TokenKind.BitAnd: return '&' - case TokenKind.BitOr: return '|' - case TokenKind.BitXOrNot: return '~' - case TokenKind.BitShiftLeft: return '<<' - case TokenKind.BitShiftRight: return '>>' - case TokenKind.LessThan: return '<' - case TokenKind.GreaterThan: return '>' - case TokenKind.And: return 'and' - case TokenKind.Or: return 'or' - case TokenKind.Not: return 'not' - case TokenKind.Assign: return '=' - case TokenKind.Semicolon: return ';' - case TokenKind.Comma: return ',' - case TokenKind.Dot: return '.' - case TokenKind.Function: return 'function' - case TokenKind.If: return 'if' - case TokenKind.While: return 'while' - case TokenKind.For: return 'for' - case TokenKind.Repeat: return 'repeat' - case TokenKind.In: return 'in' - case TokenKind.Do: return 'do' - case TokenKind.Then: return 'then' - case TokenKind.ElseIf: return 'elseif' - case TokenKind.Else: return 'else' - case TokenKind.Until: return 'until' - case TokenKind.End: return 'end' - case TokenKind.Return: return 'return' - case TokenKind.Break: return 'break' - case TokenKind.Local: return 'local' + case TokenKind.EOF: return "EOF"; + case TokenKind.NotFinished: return "NotFinished"; + case TokenKind.Identifier: return "Identifier"; + case TokenKind.StringLiteral: return "StringLiteral"; + case TokenKind.BooleanLiteral: return "BooleanLiteral"; + case TokenKind.NumberLiteral: return "NumberLiteral"; + case TokenKind.NilLiteral: return "nil"; + case TokenKind.OpenBrace: return "("; + case TokenKind.CloseBrace: return ")"; + case TokenKind.OpenSquare: return "["; + case TokenKind.CloseSquare: return "]"; + case TokenKind.SquiglyOpen: return "{"; + case TokenKind.SquiglyClose: return "}"; + case TokenKind.Addition: return "+"; + case TokenKind.Subtract: return "-"; + case TokenKind.Multiply: return "*"; + case TokenKind.Division: return "/"; + case TokenKind.FloorDivision: return "//"; + case TokenKind.Modulo: return "%"; + case TokenKind.Exponent: return "^"; + case TokenKind.BitAnd: return "&"; + case TokenKind.BitOr: return "|"; + case TokenKind.BitXOrNot: return "~"; + case TokenKind.BitShiftLeft: return "<<"; + case TokenKind.BitShiftRight: return ">>"; + case TokenKind.LessThan: return "<"; + case TokenKind.GreaterThan: return ">"; + case TokenKind.And: return "and"; + case TokenKind.Or: return "or"; + case TokenKind.Not: return "not"; + case TokenKind.Assign: return "="; + case TokenKind.Semicolon: return ";"; + case TokenKind.Comma: return ","; + case TokenKind.Dot: return "."; + case TokenKind.Function: return "function"; + case TokenKind.If: return "if"; + case TokenKind.While: return "while"; + case TokenKind.For: return "for"; + case TokenKind.Repeat: return "repeat"; + case TokenKind.In: return "in"; + case TokenKind.Do: return "do"; + case TokenKind.Then: return "then"; + case TokenKind.ElseIf: return "elseif"; + case TokenKind.Else: return "else"; + case TokenKind.Until: return "until"; + case TokenKind.End: return "end"; + case TokenKind.Return: return "return"; + case TokenKind.Break: return "break"; + case TokenKind.Local: return "local"; + default: - return "unknown token" + return "unknown token"; } } export interface Debug { - line: number - column: number + line: number; + column: number; } export interface Token { - data: string - kind: TokenKind - debug: Debug + data: string; + kind: TokenKind; + debug: Debug; } const single_token_map: Map = new Map([ - ['(', TokenKind.OpenBrace], - [')', TokenKind.CloseBrace], - ['[', TokenKind.OpenSquare], - [']', TokenKind.CloseSquare], - ['{', TokenKind.SquiglyOpen], - ['}', TokenKind.SquiglyClose], - - ['+', TokenKind.Addition], - ['-', TokenKind.Subtract], - ['*', TokenKind.Multiply], - ['/', TokenKind.Division], - ['%', TokenKind.Modulo], - ['^', TokenKind.Exponent], - ['&', TokenKind.BitAnd], - ['|', TokenKind.BitOr], - ['~', TokenKind.BitXOrNot], - - ['<', TokenKind.LessThan], - ['>', TokenKind.GreaterThan], - - ['=', TokenKind.Assign], - [';', TokenKind.Semicolon], - [',', TokenKind.Comma], - ['.', TokenKind.Dot], - ['#', TokenKind.Hash], -]) + ["(", TokenKind.OpenBrace], + [")", TokenKind.CloseBrace], + ["[", TokenKind.OpenSquare], + ["]", TokenKind.CloseSquare], + ["{", TokenKind.SquiglyOpen], + ["}", TokenKind.SquiglyClose], + + ["+", TokenKind.Addition], + ["-", TokenKind.Subtract], + ["*", TokenKind.Multiply], + ["/", TokenKind.Division], + ["%", TokenKind.Modulo], + ["^", TokenKind.Exponent], + ["&", TokenKind.BitAnd], + ["|", TokenKind.BitOr], + ["~", TokenKind.BitXOrNot], + + ["<", TokenKind.LessThan], + [">", TokenKind.GreaterThan], + + ["=", TokenKind.Assign], + [";", TokenKind.Semicolon], + [",", TokenKind.Comma], + [".", TokenKind.Dot], + ["#", TokenKind.Hash], +]); const double_token_map: Map = new Map([ - ['==', TokenKind.Equals], - ['<=', TokenKind.LessThanEquals], - ['>=', TokenKind.GreaterThanEquals], - ['~=', TokenKind.NotEquals], - ['..', TokenKind.Concat], - ['//', TokenKind.FloorDivision], - ['<<', TokenKind.BitShiftLeft], - ['>>', TokenKind.BitShiftRight], -]) + ["==", TokenKind.Equals], + ["<=", TokenKind.LessThanEquals], + [">=", TokenKind.GreaterThanEquals], + ["~=", TokenKind.NotEquals], + ["..", TokenKind.Concat], + ["//", TokenKind.FloorDivision], + ["<<", TokenKind.BitShiftLeft], + [">>", TokenKind.BitShiftRight], +]); const keyword_map: Map = new Map([ - ['function', TokenKind.Function], - ['if', TokenKind.If], - ['while', TokenKind.While], - ['for', TokenKind.For], - ['repeat', TokenKind.Repeat], - ['in', TokenKind.In], - ['do', TokenKind.Do], - ['then', TokenKind.Then], - ['elseif', TokenKind.ElseIf], - ['else', TokenKind.Else], - ['until', TokenKind.Until], - ['end', TokenKind.End], - ['return', TokenKind.Return], - ['break', TokenKind.Break], - - ['and', TokenKind.And], - ['or', TokenKind.Or], - ['not', TokenKind.Not], - - ['true', TokenKind.BooleanLiteral], - ['false', TokenKind.BooleanLiteral], - ['nil', TokenKind.NilLiteral], - ['local', TokenKind.Local], -]) + ["function", TokenKind.Function], + ["if", TokenKind.If], + ["while", TokenKind.While], + ["for", TokenKind.For], + ["repeat", TokenKind.Repeat], + ["in", TokenKind.In], + ["do", TokenKind.Do], + ["then", TokenKind.Then], + ["elseif", TokenKind.ElseIf], + ["else", TokenKind.Else], + ["until", TokenKind.Until], + ["end", TokenKind.End], + ["return", TokenKind.Return], + ["break", TokenKind.Break], + + ["and", TokenKind.And], + ["or", TokenKind.Or], + ["not", TokenKind.Not], + + ["true", TokenKind.BooleanLiteral], + ["false", TokenKind.BooleanLiteral], + ["nil", TokenKind.NilLiteral], + ["local", TokenKind.Local], +]); export class TokenStream { + private readonly processing_stream: Array; + private readonly peek_queue: Array; - private readonly processing_stream: string[] - private readonly peek_queue: Token[] - - private state: State - private end_of_stream: boolean = false - private buffer: string - private token_start_debug: Debug + private state: State; + private end_of_stream: boolean = false; + private buffer: string; + private token_start_debug: Debug; - private line: number - private column: number + private line: number; + private column: number; constructor() { - this.state = State.Initial - this.processing_stream = [] - this.buffer = '' - this.token_start_debug = { line: 0, column: 0 } - - this.line = 1 - this.column = 1 - this.peek_queue = [] + this.state = State.Initial; + this.processing_stream = []; + this.buffer = ""; + this.token_start_debug = { line: 0, column: 0 }; + + this.line = 1; + this.column = 1; + this.peek_queue = []; } private current(): string | undefined { if (this.processing_stream.length > 0) - return this.processing_stream[0] + { + return this.processing_stream[0]; + } if (this.end_of_stream) - return undefined + { + return undefined; + } + + this.end_of_stream = true; - this.end_of_stream = true - return '\0' + return "\0"; } private consume() { if (this.processing_stream.length <= 0) - return + { + return; + } - this.column += 1 - if (this.processing_stream.shift() === '\n') + this.column = this.column + 1; + + if (this.processing_stream.shift() === "\n") { - this.line += 1 - this.column = 1 + this.line = this.line + 1; + this.column = 1; } } @@ -267,8 +275,9 @@ export class TokenStream this.token_start_debug = { line: this.line, column: this.column, - } - this.buffer = '' + }; + + this.buffer = ""; } private initial() @@ -276,40 +285,49 @@ export class TokenStream if (this.processing_stream.length === 0) { this.peek_queue.push({ - data: '', + data: "", kind: TokenKind.EOF, debug: { line: this.line, column: this.column, }, - }) + }); - return + return; } - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/\s/.test(c)) - return this.consume() + { +this.consume(); + + return; +} if (this.processing_stream.length > 1) { - const double = c + this.processing_stream[1] - if (double === '--') + const double = c + this.processing_stream[1]; + + if (double === "--") { - this.state = State.Comment - return + this.state = State.Comment; + + return; } - if (double === '[[') + if (double === "[[") { - this.state = State.MultiLineString - this.start_token() - this.consume() - this.consume() - return + this.state = State.MultiLineString; + this.start_token(); + this.consume(); + this.consume(); + + return; } - const dobule_token_type = double_token_map.get(double) + const dobule_token_type = double_token_map.get(double); + if (dobule_token_type !== undefined) { this.peek_queue.push({ @@ -319,14 +337,17 @@ export class TokenStream line: this.line, column: this.column, }, - }) - this.consume() - this.consume() - return + }); + + this.consume(); + this.consume(); + + return; } } - const single_token_type = single_token_map.get(c) + const single_token_type = single_token_map.get(c); + if (single_token_type !== undefined) { this.peek_queue.push({ @@ -336,38 +357,42 @@ export class TokenStream line: this.line, column: this.column, }, - }) - this.consume() - return + }); + + this.consume(); + + return; } if (c === '"') { - this.start_token() - this.consume() - this.state = State.StringLiteral - return + this.start_token(); + this.consume(); + this.state = State.StringLiteral; + + return; } if (/[a-zA-Z_]/.test(c)) { - this.start_token() - this.state = State.Identifier - return + this.start_token(); + this.state = State.Identifier; + + return; } if (/[0-9]/.test(c)) { - this.start_token() - this.state = State.NumberLiteral - return + this.start_token(); + this.state = State.NumberLiteral; } } private read_string() { - const c = this.current() - this.consume() + const c = this.current(); + + this.consume(); if (c === '"') { @@ -375,221 +400,254 @@ export class TokenStream data: this.buffer, kind: TokenKind.StringLiteral, debug: this.token_start_debug, - }) + }); - this.state = State.Initial - return + this.state = State.Initial; + + return; } - if (c === '\\') + if (c === "\\") { - this.state = State.StringLiteralEscape - return + this.state = State.StringLiteralEscape; + + return; } - this.buffer += c + this.buffer = this.buffer + c; } private read_string_escape() { - const c = this.current() - this.consume() - this.state = State.StringLiteral + const c = this.current(); + + this.consume(); + this.state = State.StringLiteral; switch (c) { - case 'n': this.buffer += '\n'; break - case '0': this.buffer += '\0'; break - case 'r': this.buffer += '\r'; break - case 't': this.buffer += '\t'; break + case "n": this.buffer = `${this.buffer}\n`; break; + case "0": this.buffer = `${this.buffer}\0`; break; + case "r": this.buffer = `${this.buffer}\r`; break; + case "t": this.buffer = `${this.buffer}\t`; break; + default: - this.buffer += c - break + this.buffer = this.buffer + c; + break; } } private read_multi_line_string() { - const c = this.current() ?? '\0' - this.consume() + const c = this.current() ?? "\0"; - if (c + this.current() === ']]') + this.consume(); + + if (c + this.current() === "]]") { this.peek_queue.push({ data: this.buffer, kind: TokenKind.StringLiteral, debug: this.token_start_debug, - }) + }); + + this.consume(); + this.state = State.Initial; - this.consume() - this.state = State.Initial - return + return; } - this.buffer += c + this.buffer = this.buffer + c; } private read_identifier() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (!/[a-zA-Z0-9_]/.test(c)) { - const kind = keyword_map.get(this.buffer) + const kind = keyword_map.get(this.buffer); + this.peek_queue.push({ data: this.buffer, kind: kind ?? TokenKind.Identifier, debug: this.token_start_debug, - }) + }); + + this.state = State.Initial; - this.state = State.Initial - return + return; } - this.buffer += c - this.consume() + this.buffer = this.buffer + c; + this.consume(); } private number() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/[0-9]/.test(c)) { - this.buffer += c - this.consume() - return + this.buffer = this.buffer + c; + this.consume(); + + return; } - if (c === '.') + if (c === ".") { - this.buffer += c - this.consume() - this.state = State.NumberLiteralDot - return + this.buffer = this.buffer + c; + this.consume(); + this.state = State.NumberLiteralDot; + + return; } - if (c === 'e' || c === 'E') + if (c === "e" || c === "E") { - this.buffer += c - this.consume() - this.state = State.NumberLiteralExp - return + this.buffer = this.buffer + c; + this.consume(); + this.state = State.NumberLiteralExp; + + return; } - if (c === 'x') + if (c === "x") { - if (this.buffer !== '0') + if (this.buffer !== "0") { this.peek_queue.push({ data: this.buffer, kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial - return + }); + + this.state = State.Initial; + + return; } - this.buffer += c - this.state = State.NumberHex - this.consume() - return + this.buffer = this.buffer + c; + this.state = State.NumberHex; + this.consume(); + + return; } this.peek_queue.push({ data: this.buffer, kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial + }); + + this.state = State.Initial; } private number_dot() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/[0-9]/.test(c)) { - this.buffer += c - this.consume() - return + this.buffer = this.buffer + c; + this.consume(); + + return; } - if (c === 'e' || c === 'E') + if (c === "e" || c === "E") { - this.buffer += c - this.state = State.NumberLiteralExpSign - this.consume() - return + this.buffer = this.buffer + c; + this.state = State.NumberLiteralExpSign; + this.consume(); + + return; } this.peek_queue.push({ data: this.buffer, kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial + }); + + this.state = State.Initial; } private number_exp_sign() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/[0-9+-]/.test(c)) { - this.buffer += c - this.consume() - this.state = State.NumberLiteralExp - return + this.buffer = this.buffer + c; + this.consume(); + this.state = State.NumberLiteralExp; + + return; } this.peek_queue.push({ data: this.buffer, kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial + }); + + this.state = State.Initial; } private number_exp() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/[0-9]/.test(c)) { - this.buffer += c - this.consume() - return + this.buffer = this.buffer + c; + this.consume(); + + return; } this.peek_queue.push({ data: this.buffer, kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial + }); + + this.state = State.Initial; } private number_hex() { - const c = this.current() ?? '\0' + const c = this.current() ?? "\0"; + if (/[0-9a-fA-F]/.test(c)) { - this.buffer += c - this.consume() - return + this.buffer = this.buffer + c; + this.consume(); + + return; } this.peek_queue.push({ data: parseInt(this.buffer.slice(2), 16).toString(), kind: TokenKind.NumberLiteral, debug: this.token_start_debug, - }) - this.state = State.Initial + }); + + this.state = State.Initial; } private comment() { - const c = this.current() - this.consume() + const c = this.current(); - if (c === '\n') - this.state = State.Initial + this.consume(); + + if (c === "\n") + { + this.state = State.Initial; + } } private on_char() @@ -597,7 +655,7 @@ export class TokenStream if (this.current() === undefined) { this.peek_queue.push({ - data: '', + data: "", kind: this.state === State.Initial ? TokenKind.EOF : TokenKind.NotFinished, @@ -605,74 +663,79 @@ export class TokenStream line: this.line, column: this.column, }, - }) + }); - return + return; } switch (this.state) { case State.Initial: - this.initial() - break + this.initial(); + break; case State.Identifier: - this.read_identifier() - break + this.read_identifier(); + break; case State.StringLiteral: - this.read_string() - break + this.read_string(); + break; case State.StringLiteralEscape: - this.read_string_escape() - break + this.read_string_escape(); + break; case State.MultiLineString: - this.read_multi_line_string() - break + this.read_multi_line_string(); + break; case State.NumberLiteral: - this.number() - break + this.number(); + break; case State.NumberLiteralDot: - this.number_dot() - break + this.number_dot(); + break; case State.NumberLiteralExpSign: - this.number_exp_sign() - break + this.number_exp_sign(); + break; case State.NumberLiteralExp: - this.number_exp() - break + this.number_exp(); + break; case State.NumberHex: - this.number_hex() - break + this.number_hex(); + break; case State.Comment: - this.comment() - break + this.comment(); + break; } } feed(stream: string) { - this.processing_stream.push(...stream.split('')) - this.end_of_stream = false + this.processing_stream.push(...stream.split("")); + this.end_of_stream = false; } next(): Token { if (this.peek_queue.length === 0) - this.peek() + { + this.peek(); + } - return this.peek_queue.shift() as Token + return this.peek_queue.shift() as Token; } peek(count = 1): Token { while (this.peek_queue.length < count) - this.on_char() + { + this.on_char(); + } - const token = this.peek_queue[count - 1] + const token = this.peek_queue[count - 1]; if (token === undefined) - throw new Error() + { + throw new Error(); + } - return token + return token; } - } diff --git a/src/lib.mts b/src/lib.mts index b7478f5..62c0ae6 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -23,7 +23,9 @@ function optional_parameter( ): VariableValueType | undefined { if (isNil(parameter)) - return undefined + { + return undefined; + } assertVariableKind(parameter, expected_kind); @@ -49,43 +51,64 @@ export function table_size(value: VariableTable): number return size; } -export function variable_to_string(variable: Variable, tables_done: Variable[] = []): string +export function variable_to_string(variable: Variable, tables_done: Array = []): string { switch (variable.data_type) { - case VariableKind.Nil: return 'nil' - case VariableKind.Boolean: return variable.boolean ? 'true' : 'false' - case VariableKind.Number: return variable.number?.toString() ?? '0' - case VariableKind.String: return variable.string ?? '' - case VariableKind.Function: return `` - case VariableKind.NativeFunction: return `` + case VariableKind.Nil: + return "nil"; + case VariableKind.Boolean: + return variable.boolean ? "true" : "false"; + case VariableKind.Number: + return variable.number.toString(); + case VariableKind.String: + return variable.string; + case VariableKind.Function: + return ``; + case VariableKind.NativeFunction: + return ``; case VariableKind.Table: { if (tables_done.includes(variable)) - return '...' + { + return "..."; + } + + tables_done.push(variable); - tables_done.push(variable) - return `{ ${ [...variable.table?.entries() ?? []] - .map(([i, v]) => - `${ i } = ${ variable_to_string(v, tables_done) }`) - .join(', ') } }` + const items: Array = []; + + for (const [key, value] of variable.table.entries()) + { + const item: string = `${String(key)} = ${variable_to_string(value, tables_done)}`; + + items.push(item); + } + + return `{ ${items.join(", ")} }`; } - default: return '' } } -function print(_: Engine, ...args: Variable[]): Variable[] +function print(_: Engine, ...args: Array): Array { - console.log(...args.map(arg => variable_to_string(arg))) - return [nil] + // eslint-disable-next-line no-console + console.log(...args.map( + (arg): string => + { + return variable_to_string(arg); + } + )); + + return [nil]; } -function type(_: Engine, variable: Variable): Variable[] +function type(_: Engine, variable: Variable): Array { - return [make_string(variable.data_type)] + return [make_string(variable.data_type)]; } -function inext(_: Engine, table: Variable, previous_index: Variable): Variable[] +function inext(_: Engine, table: Variable, previous_index: Variable): Array { assertVariableKind(table, VariableKind.Table); assertVariableKind(previous_index, VariableKind.Number); @@ -105,44 +128,56 @@ function inext(_: Engine, table: Variable, previous_index: Variable): Variable[] const inext_binding: VariableNativeFunction = { data_type: VariableKind.NativeFunction, - native_function: inext + native_function: inext, }; -function ipairs(_: Engine, table: Variable): Variable[] +function ipairs(_: Engine, table: Variable): Array { assertVariableKind(table, VariableKind.Table); return [ inext_binding, table, - make_number(0) - ] + make_number(0), + ]; } function compare_keys(next_key: unknown, index: Variable): boolean { if (isVariableKind(index, VariableKind.Boolean)) - return next_key === index.boolean + { + return next_key === index.boolean; + } if (isVariableKind(index, VariableKind.Number)) - return next_key === index.number + { + return next_key === index.number; + } if (isVariableKind(index, VariableKind.String)) - return next_key === index.string + { + return next_key === index.string; + } if (isVariableKind(index, VariableKind.Table)) - return next_key === index.table + { + return next_key === index.table; + } if (isVariableKind(index, VariableKind.Function)) - return next_key === index.function_id + { + return next_key === index.function_id; + } if (isVariableKind(index, VariableKind.NativeFunction)) - return next_key === index.native_function + { + return next_key === index.native_function; + } - return false + return false; } -function next(_: Engine, variable: Variable, start_index?: Variable): Variable[] +function next(_: Engine, variable: Variable, start_index?: Variable): Array { assertVariableKind(variable, VariableKind.Table); @@ -152,9 +187,11 @@ function next(_: Engine, variable: Variable, start_index?: Variable): Variable[] const first_value: Variable | undefined = variable.table.get(first_key); if (isNil(first_value)) - return [nil] + { + return [nil]; + } - return [make_variable(first_key), first_value] + return [make_variable(first_key), first_value]; } let previous_key_found: boolean = false; @@ -176,34 +213,39 @@ function next(_: Engine, variable: Variable, start_index?: Variable): Variable[] return [nil]; } -function pairs(_: Engine, table: Variable): Variable[] +function pairs(_: Engine, table: Variable): Array { const next_func: VariableNativeFunction = { data_type: VariableKind.NativeFunction, - native_function: next - } + native_function: next, + }; - return [next_func, table, nil] + return [next_func, table, nil]; } -function range(_: Engine, count: Variable): Variable[] +function range(_: Engine, count: Variable): Array { assertVariableKind(count, VariableKind.Number); - let index = 0 + let index = 0; + return [{ data_type: VariableKind.NativeFunction, native_function: () => { - index += 1 + index = index + 1; + if (index >= (count.number ?? 0)) - return [nil] - return [{ data_type: VariableKind.Number, number: index }] + { + return [nil]; + } + + return [{ data_type: VariableKind.Number, number: index }]; }, - }] + }]; } -function is_empty(_: Engine, table: Variable): Variable[] +function is_empty(_: Engine, table: Variable): Array { assertVariableKind(table, VariableKind.Table); const empty: boolean = table.table.size === 0; @@ -218,14 +260,14 @@ function key_variable(key: unknown): Variable return { data_type: VariableKind.NativeFunction, native_function: key, - } + }; } return make_variable(key); } // @ts-expect-error: unimplemented -function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] +function table_sort(engine: Engine, table: Variable, by: Variable): Array { assertVariableKind(table, VariableKind.Table); @@ -235,16 +277,18 @@ function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] const entries: Array<[unknown, Variable]> = [...table.table.entries()] - entries.sort(([_, a], [__, b]) => - { - const result = engine.call(by, a, b) - if (result instanceof Error) - return 0 + entries.sort( + ([_, a], [__, b]) => + { + const result = engine.call(by, a, b) + if (result instanceof Error) + return 0 - const comparison = result.at(0) - assertVariableKind(comparison, VariableKind.Number); - return comparison.number - }) + const comparison = result.at(0) + assertVariableKind(comparison, VariableKind.Number); + return comparison.number + } + ) const numbered_entries = entries.map(([key, _], i) => [i + 1, key_variable(key)] as const) return [{ data_type: VariableKind.Table, table: new Map(numbered_entries) }] @@ -252,19 +296,22 @@ function table_sort(engine: Engine, table: Variable, by: Variable): Variable[] */ } -async function find(engine: Engine, table: Variable, matches: Variable): Promise +async function find(engine: Engine, table: Variable, matches: Variable): Promise> { assertVariableKind(table, VariableKind.Table); - const entries: Array<[unknown, Variable]> = [...table.table.entries()] + const entries: Array<[unknown, Variable]> = [...table.table.entries()]; for (const [key, value] of entries) { - const result = await engine.call(matches, value) + const result = await engine.call(matches, value); if (result instanceof Error) + { throw result; + } const matching: Variable | undefined = result.at(0); + assertVariableKind(matching, VariableKind.Boolean); return [make_variable(key)]; @@ -273,112 +320,152 @@ async function find(engine: Engine, table: Variable, matches: Variable): Promise return [nil]; } -function first(_: Engine, table: Variable): Variable[] +function first(_: Engine, table: Variable): Array { assertVariableKind(table, VariableKind.Table); - const key = table.table.keys().next().value - if (typeof key === 'string') - return [make_string(key)] - else if (typeof key === 'number') - return [make_number(key)] - else - return [nil] + const key = table.table.keys().next().value; + + if (typeof key === "string") + { + return [make_string(key)]; + } + + if (typeof key === "number") + { + return [make_number(key)]; + } + + return [nil]; } -function keys(_: Engine, table: Variable): Variable[] +function keys(_: Engine, table: Variable): Array { assertVariableKind(table, VariableKind.Table); - const keys = [...table.table.keys()] - const entries = keys.map((key, i) => [i + 1, key_variable(key)] as const) - return [{ data_type: VariableKind.Table, table: new Map(entries) }] + const keys = [...table.table.keys()]; + const entries = keys.map( + (key, i) => + { + return [i + 1, key_variable(key)] as const; + } + ); + + return [{ data_type: VariableKind.Table, table: new Map(entries) }]; } -function values(_: Engine, table: Variable): Variable[] +function values(_: Engine, table: Variable): Array { assertVariableKind(table, VariableKind.Table); - const values = [...table.table.values()] - const entries = values.map((value, i) => [i + 1, value] as const) - return [{ data_type: VariableKind.Table, table: new Map(entries) }] + const values = [...table.table.values()]; + const entries = values.map( + (value, i) => + { + return [i + 1, value] as const; + } + ); + + return [{ data_type: VariableKind.Table, table: new Map(entries) }]; } -function to_number(_: Engine, arg: Variable): Variable[] +function to_number(_: Engine, arg: Variable): Array { switch (arg.data_type) { case VariableKind.Number: - return [arg] + return [arg]; case VariableKind.String: const text: string = arg.string.trim(); + if (!/^-?\d+(\.\d+)?$/.test(text)) - return [nil] - return [make_number(parseFloat(text))] + { + return [nil]; + } + + return [make_number(parseFloat(text))]; + default: - return [nil] + return [nil]; } } -function to_string(_: Engine, arg: Variable): Variable[] +function to_string(_: Engine, arg: Variable): Array { - return [make_string(variable_to_string(arg))] + return [make_string(variable_to_string(arg))]; } -function assert(engine: Engine, condition: Variable, message?: Variable): Variable[] +function assert(engine: Engine, condition: Variable, message?: Variable): Array { - if (isVariableKind(condition, VariableKind.Nil) || isVariableKind(condition, VariableKind.Boolean) && !condition.boolean) { - error(engine, message ?? make_string('assertion failed!')) + if (isVariableKind(condition, VariableKind.Nil) || isVariableKind(condition, VariableKind.Boolean) && !condition.boolean) + { + error(engine, message ?? make_string("assertion failed!")); } - return [nil] + return [nil]; } -function error(engine: Engine, message: Variable): Variable[] +function error(engine: Engine, message: Variable): Array { assertVariableKind(message, VariableKind.String); - engine.raise_error(message.string) - return [nil] + engine.raise_error(message.string); + + return [nil]; } -let warnings_on = true -function warn(_: Engine, ...messages: Variable[]): Variable[] +let warnings_on = true; + +function warn(_: Engine, ...messages: Array): Array { - const message = messages[0] + const message = messages[0]; if (isVariableKind(message, VariableKind.String)) { switch (message.string) { - case '@on': - warnings_on = true - break - case '@off': - warnings_on = false - break + case "@on": + warnings_on = true; + break; + case "@off": + warnings_on = false; + break; } } if (warnings_on) - console.error('WARNING', messages.map(x => variable_to_string(x))) + { + console.error( + "WARNING", + messages.map( + (x) => + { + return variable_to_string(x); + } + ) + ); + } - return [nil] + return [nil]; } -function select(_: Engine, index: Variable, ...args: Variable[]): Variable[] +function select(_: Engine, index: Variable, ...args: Array): Array { if (isVariableKind(index, VariableKind.Number)) { if (index.number > 0) - return args.slice(index.number - 1) - else - return args.slice(args.length + index.number - 1) + { + return args.slice(index.number - 1); + } + + return args.slice(args.length + index.number - 1); } - if (isVariableKind(index, VariableKind.String) && index.string === '#') - return [make_number(args.length)] + if (isVariableKind(index, VariableKind.String) && index.string === "#") + { + return [make_number(args.length)]; + } - return [nil] + return [nil]; } -function string_byte(_: Engine, s: Variable, i?: Variable, j?: Variable): Variable[] +function string_byte(_: Engine, s: Variable, i?: Variable, j?: Variable): Array { assertVariableKind(s, VariableKind.String); @@ -398,194 +485,229 @@ function string_byte(_: Engine, s: Variable, i?: Variable, j?: Variable): Variab end = j.number; } - const bytes: Variable[] = [] + const bytes: Array = []; for (let index = start - 1; index <= end - 1; index++) - bytes.push(make_number(s.string.charCodeAt(index))) + { + bytes.push(make_number(s.string.charCodeAt(index))); + } - return bytes + return bytes; } -function string_char(_: Engine, ...chars: Variable[]): Variable[] +function string_char(_: Engine, ...chars: Array): Array { assertArray(chars, unary(assertVariableKind, VariableKind.Number)); - const s = String.fromCharCode(...chars.map(c => c.number)) + const s = String.fromCharCode(...chars.map( + (c) => + { + return c.number; + } + )); - return [make_string(s)] + return [make_string(s)]; } function string_format_helper(char: string, args_iterator: IterableIterator): string { switch (char) { - case 'd': + case "d": { - const arg = args_iterator.next().value + const arg = args_iterator.next().value; + assertVariableKind(arg, VariableKind.Number); - return Math.floor(arg.number).toString() + + return Math.floor(arg.number).toString(); } - case 'f': + case "f": { - const arg = args_iterator.next().value + const arg = args_iterator.next().value; + assertVariableKind(arg, VariableKind.Number); - return arg.number.toString() + + return arg.number.toString(); } - case 's': + case "s": { - const arg = args_iterator.next().value + const arg = args_iterator.next().value; + assertVariable(arg); - return variable_to_string(arg) + + return variable_to_string(arg); } - case '%': - return '%' + case "%": + return "%"; + default: - throw new Error(`Invalid format specifier: %${ char }`) + throw new Error(`Invalid format specifier: %${char}`); } } -function string_format(_: Engine, format: Variable, ...args: Variable[]): Variable[] +function string_format(_: Engine, format: Variable, ...args: Array): Array { assertVariableKind(format, VariableKind.String); const args_iterator = args[Symbol.iterator](); - let result = '' - let is_format = false + let result = ""; + let is_format = false; for (const char of format.string) { if (is_format) { - result += string_format_helper(char, args_iterator) - is_format = false + result = result + string_format_helper(char, args_iterator); + is_format = false; + continue; } - if (char === '%') + if (char === "%") { - is_format = true + is_format = true; + continue; } - result += char + result = result + char; } if (is_format) - throw new Error(`Invalid format specifier: %`) + { + throw new Error("Invalid format specifier: %"); + } - return [make_string(result)] + return [make_string(result)]; } -function string_find(_: Engine, s: Variable, pattern: Variable, init?: Variable, plain?: Variable): Variable[] +function string_find(_: Engine, s: Variable, pattern: Variable, init?: Variable, plain?: Variable): Array { assertVariableKind(s, VariableKind.String); assertVariableKind(pattern, VariableKind.String); - const offset: number = optional_parameter(VariableKind.Number, init) ?? 1 - const str = s.string.slice(offset - 1) + const offset: number = optional_parameter(VariableKind.Number, init) ?? 1; + const str = s.string.slice(offset - 1); - const plain_param = optional_parameter(VariableKind.Boolean, plain) ?? false + const plain_param = optional_parameter(VariableKind.Boolean, plain) ?? false; if (plain_param) - return [make_number(str.indexOf(pattern.string) + 1)] + { + return [make_number(str.indexOf(pattern.string) + 1)]; + } + + const results = RegExp(pattern.string).exec(str); - const results = RegExp(pattern.string).exec(str) if (results === null || results.length === 0) - return [nil] + { + return [nil]; + } + + const index = s.string.indexOf(results[0]); - const index = s.string.indexOf(results[0]) - return [make_number(index + 1)] + return [make_number(index + 1)]; } -function string_len(_: Engine, s: Variable): Variable[] +function string_len(_: Engine, s: Variable): Array { assertVariableKind(s, VariableKind.String); - return [make_number(s.string.length)] + + return [make_number(s.string.length)]; } -function string_lower(_: Engine, s: Variable): Variable[] +function string_lower(_: Engine, s: Variable): Array { assertVariableKind(s, VariableKind.String); - return [make_string(s.string.toLowerCase())] + + return [make_string(s.string.toLowerCase())]; } -function string_rep(_: Engine, s: Variable, n: Variable, sep?: Variable): Variable[] +function string_rep(_: Engine, s: Variable, n: Variable, sep?: Variable): Array { assertVariableKind(s, VariableKind.String); assertVariableKind(n, VariableKind.Number); - const separator: string = optional_parameter(VariableKind.String, sep) ?? '' + const separator: string = optional_parameter(VariableKind.String, sep) ?? ""; - return [make_string(new Array(n.number).fill(s.string).join(separator))] + return [make_string(new Array(n.number).fill(s.string).join(separator))]; } -function string_sub(_: Engine, s: Variable, i: Variable, j?: Variable): Variable[] +function string_sub(_: Engine, s: Variable, i: Variable, j?: Variable): Array { assertVariableKind(s, VariableKind.String); assertVariableKind(i, VariableKind.Number); - const end: number | undefined = optional_parameter(VariableKind.Number, j) + const end: number | undefined = optional_parameter(VariableKind.Number, j); - const start = i.number ?? 1 - return [make_string(s.string.slice(start - 1, end))] + const start = i.number ?? 1; + + return [make_string(s.string.slice(start - 1, end))]; } -function string_upper(_: Engine, s: Variable): Variable[] +function string_upper(_: Engine, s: Variable): Array { assertVariableKind(s, VariableKind.String); - return [make_string(s.string.toUpperCase())] + + return [make_string(s.string.toUpperCase())]; } -function string_reverse(_: Engine, s: Variable): Variable[] +function string_reverse(_: Engine, s: Variable): Array { assertVariableKind(s, VariableKind.String); - return [make_string(s.string.split('').reverse().join(''))] + + return [make_string(s.string.split("").reverse().join(""))]; } -function table_concat(_: Engine, list: Variable, sep?: Variable, i?: Variable, j?: Variable): Variable[] +function table_concat(_: Engine, list: Variable, sep?: Variable, i?: Variable, j?: Variable): Array { assertVariableKind(list, VariableKind.Table); - const separator = optional_parameter(VariableKind.String, sep) ?? '' - const start = optional_parameter(VariableKind.Number, i) ?? 1 - const end = optional_parameter(VariableKind.Number, j) + const separator = optional_parameter(VariableKind.String, sep) ?? ""; + const start = optional_parameter(VariableKind.Number, i) ?? 1; + const end = optional_parameter(VariableKind.Number, j); const result = [...list.table.values()] .slice(start - 1, end) - .map(item => variable_to_string(item)) - .join(separator) - return [make_string(result)] + .map((item) => +{ +return variable_to_string(item); +}) + .join(separator); + + return [make_string(result)]; } -function table_insert(_: Engine, list: Variable, index?: Variable, value?: Variable): Variable[] +function table_insert(_: Engine, list: Variable, index?: Variable, value?: Variable): Array { assertVariableKind(list, VariableKind.Table); - const size = table_size(list) + const size = table_size(list); if (isNil(value)) { - return table_insert(_, list, make_number(size + 1), index) + return table_insert(_, list, make_number(size + 1), index); } assertVariableKind(index, VariableKind.Number); - const position: number = index.number + const position: number = index.number; for (let i = size + 1; i > position; --i) - list.table.set(i, list.table.get(i - 1) ?? nil) + { + list.table.set(i, list.table.get(i - 1) ?? nil); + } - list.table.set(position, value) + list.table.set(position, value); - return [nil] + return [nil]; } -function table_move(_: Engine, a1: Variable, f: Variable, e: Variable, t: Variable, a2?: Variable): Variable[] +function table_move(_: Engine, a1: Variable, f: Variable, e: Variable, t: Variable, a2?: Variable): Array { if (isNil(a2)) { - return table_move(_, a1, f, e, t, a1) + return table_move(_, a1, f, e, t, a1); } assertVariableKind(a1, VariableKind.Table); @@ -594,148 +716,185 @@ function table_move(_: Engine, a1: Variable, f: Variable, e: Variable, t: Variab assertVariableKind(t, VariableKind.Number); assertVariableKind(a2, VariableKind.Table); - const src_start = f.number - const src_end = e.number - const dest_start = t.number - const count = src_end - src_start + const src_start = f.number; + const src_end = e.number; + const dest_start = t.number; + const count = src_end - src_start; for (let index = 0; index <= count; index++) - a2.table.set(dest_start + index, a1.table.get(src_start + index) ?? nil) + { + a2.table.set(dest_start + index, a1.table.get(src_start + index) ?? nil); + } - return [a2] + return [a2]; } -function table_pack(_: Engine, ...args: Variable[]): Variable[] +function table_pack(_: Engine, ...args: Array): Array { - const elements = args - .map((item, i) => [i + 1, item] as [number | string, Variable]) + const elements = args.map( + (item, i) => + { + return [i + 1, item] as [number | string, Variable]; + } + ); return [{ data_type: VariableKind.Table, - table: new Map([...elements, ['n', make_number(args.length)]]), - }] + table: new Map([...elements, ["n", make_number(args.length)]]), + }]; } -function table_remove(_: Engine, list: Variable, pos?: Variable): Variable[] +function table_remove(_: Engine, list: Variable, pos?: Variable): Array { assertVariableKind(list, VariableKind.Table); - const size = table_size(list) - const remove_index = optional_parameter(VariableKind.Number, pos) ?? (size + 1) - const deleted_value = list.table.get(remove_index) ?? nil + const size = table_size(list); + const remove_index = optional_parameter(VariableKind.Number, pos) ?? (size + 1); + const deleted_value = list.table.get(remove_index) ?? nil; for (let index = remove_index; index < size; ++index) - list.table.set(index, list.table.get(index + 1) ?? nil) + { + list.table.set(index, list.table.get(index + 1) ?? nil); + } - list.table.delete(size) + list.table.delete(size); - return [deleted_value] + return [deleted_value]; } -function table_unpack(_: Engine, list: Variable, i?: Variable, j?: Variable): Variable[] +function table_unpack(_: Engine, list: Variable, i?: Variable, j?: Variable): Array { assertVariableKind(list, VariableKind.Table); - const size = table_size(list) ?? 0 - const start = optional_parameter(VariableKind.Number, i) ?? 1 - const end = optional_parameter(VariableKind.Number, j) ?? size - return [...list.table.values()].splice(start - 1, end) + const size = table_size(list) ?? 0; + const start = optional_parameter(VariableKind.Number, i) ?? 1; + const end = optional_parameter(VariableKind.Number, j) ?? size; + + return [...list.table.values()].splice(start - 1, end); } -function math_abs(_: Engine, x: Variable): Variable[] +function math_abs(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.abs(x.number))] + + return [make_number(Math.abs(x.number))]; } -function math_acos(_: Engine, x: Variable): Variable[] +function math_acos(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.acos(x.number))] + + return [make_number(Math.acos(x.number))]; } -function math_asin(_: Engine, x: Variable): Variable[] +function math_asin(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.asin(x.number))] + + return [make_number(Math.asin(x.number))]; } -function math_atan(_: Engine, x: Variable): Variable[] +function math_atan(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.atan(x.number))] + + return [make_number(Math.atan(x.number))]; } -function math_ceil(_: Engine, x: Variable): Variable[] +function math_ceil(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.ceil(x.number))] + + return [make_number(Math.ceil(x.number))]; } -function math_cos(_: Engine, x: Variable): Variable[] +function math_cos(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.cos(x.number))] + + return [make_number(Math.cos(x.number))]; } -function math_deg(_: Engine, x: Variable): Variable[] +function math_deg(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(x.number * (180 / Math.PI))] + + return [make_number(x.number * (180 / Math.PI))]; } -function math_exp(_: Engine, x: Variable): Variable[] +function math_exp(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.exp(x.number))] + + return [make_number(Math.exp(x.number))]; } -function math_floor(_: Engine, x: Variable): Variable[] +function math_floor(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.floor(x.number))] + + return [make_number(Math.floor(x.number))]; } -function math_fmod(_: Engine, x: Variable, y: Variable): Variable[] +function math_fmod(_: Engine, x: Variable, y: Variable): Array { assertVariableKind(x, VariableKind.Number); assertVariableKind(y, VariableKind.Number); - return [make_number(x.number % y.number)] + + return [make_number(x.number % y.number)]; } -function math_log(_: Engine, x: Variable, base: Variable): Variable[] +function math_log(_: Engine, x: Variable, base: Variable): Array { assertVariableKind(x, VariableKind.Number); assertVariableKind(base, VariableKind.Number); - return [make_number(Math.log(x.number) / Math.log(base.number))] + + return [make_number(Math.log(x.number) / Math.log(base.number))]; } -function math_max(_: Engine, ...args: Variable[]): Variable[] +function math_max(_: Engine, ...args: Array): Array { assertPopulatedArray(args, unary(assertVariableKind, VariableKind.Number)); - const max = args.reduce((acc, x) => Math.max(acc, x.number), Number.NEGATIVE_INFINITY) - return [make_number(max)] + + const max = args.reduce( + (acc, x) => + { + return Math.max(acc, x.number); + }, + Number.NEGATIVE_INFINITY + ); + + return [make_number(max)]; } -function math_min(_: Engine, ...args: Variable[]): Variable[] +function math_min(_: Engine, ...args: Array): Array { assertPopulatedArray(args, unary(assertVariableKind, VariableKind.Number)); - const min = args.reduce((acc, x) => Math.min(acc, x.number), Number.POSITIVE_INFINITY) - return [make_number(min)] + const min = args.reduce( + (acc, x) => + { + return Math.min(acc, x.number); + }, + Number.POSITIVE_INFINITY + ); + + return [make_number(min)]; } -function math_modf(_: Engine, x: Variable): Variable[] +function math_modf(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - const n = x.number - const integral = Math.trunc(n) - return [make_number(integral), make_number(n - integral)] + const n = x.number; + const integral = Math.trunc(n); + + return [make_number(integral), make_number(n - integral)]; } -function math_rad(_: Engine, x: Variable): Variable[] +function math_rad(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(x.number * Math.PI / 180)] + + return [make_number(x.number * Math.PI / 180)]; } /** @@ -743,92 +902,114 @@ function math_rad(_: Engine, x: Variable): Variable[] * 1 arg : [1; m] * 2 args: [m; n] */ -function math_random(_: Engine, m?: Variable, n?: Variable): Variable[] +function math_random(_: Engine, m?: Variable, n?: Variable): Array { if (isNil(m)) - return [make_number(Math.random())] + { + return [make_number(Math.random())]; + } if (isNil(n)) - return math_random(_, make_number(1), m) + { + return math_random(_, make_number(1), m); + } assertVariableKind(m, VariableKind.Number); assertVariableKind(n, VariableKind.Number); - const min = m.number - const max = n.number + const min = m.number; + const max = n.number; if (max === 0) - throw new ValidationError('math.random: upper bound must be greater than 0.'); + { + throw new ValidationError("math.random: upper bound must be greater than 0."); + } if (min > max) - throw new ValidationError('math.random: upper bound must be greater than or equal to lower bound.'); + { + throw new ValidationError("math.random: upper bound must be greater than or equal to lower bound."); + } - const result: number = Math.floor(Math.random() * (max + 1 - min) + min) + const result: number = Math.floor(Math.random() * (max + 1 - min) + min); - return [make_number(result)] + return [make_number(result)]; } -function math_sin(_: Engine, x: Variable): Variable[] +function math_sin(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.sin(x.number))] + + return [make_number(Math.sin(x.number))]; } -function math_sqrt(_: Engine, x: Variable): Variable[] +function math_sqrt(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.sqrt(x.number))] + + return [make_number(Math.sqrt(x.number))]; } -function math_tan(_: Engine, x: Variable): Variable[] +function math_tan(_: Engine, x: Variable): Array { assertVariableKind(x, VariableKind.Number); - return [make_number(Math.tan(x.number))] + + return [make_number(Math.tan(x.number))]; } -function math_tointeger(_: Engine, x: Variable): Variable[] +function math_tointeger(_: Engine, x: Variable): Array { - assertVariable(x) + assertVariable(x); + switch (x.data_type) { case VariableKind.Number: - return [x] + return [x]; case VariableKind.String: const text: string = x.string.trim(); + if (!/^-?\d+$/.test(text)) - return [nil] - return [make_number(parseInt(text))] + { + return [nil]; + } + + return [make_number(parseInt(text))]; + default: - return [nil] + return [nil]; } } -function math_type(_: Engine, x: Variable): Variable[] +function math_type(_: Engine, x: Variable): Array { - assertVariable(x) + assertVariable(x); if (!isVariableKind(x, VariableKind.Number)) - return [nil] + { + return [nil]; + } if (isInteger(x.number)) - return [make_string('integer')] + { + return [make_string("integer")]; + } - return [make_string('float')] + return [make_string("float")]; } -function math_ult(_: Engine, m: Variable, n: Variable): Variable[] +function math_ult(_: Engine, m: Variable, n: Variable): Array { assertVariableKind(m, VariableKind.Number); assertVariableKind(n, VariableKind.Number); - const buffer = Buffer.from(new ArrayBuffer(8)) - buffer.writeInt32LE(m.number) - buffer.writeInt32LE(n.number, 4) + const buffer = Buffer.from(new ArrayBuffer(8)); + + buffer.writeInt32LE(m.number); + buffer.writeInt32LE(n.number, 4); - const m_unsigned = buffer.readUInt32LE() - const n_unsigned = buffer.readUInt32LE(4) + const m_unsigned = buffer.readUInt32LE(); + const n_unsigned = buffer.readUInt32LE(4); - return [make_boolean(m_unsigned < n_unsigned)] + return [make_boolean(m_unsigned < n_unsigned)]; } function fwrap(fn: NativeFunction): VariableNativeFunction @@ -850,102 +1031,102 @@ function twrap(content: Array<[unknown, Variable]>): VariableTable export function std_lib(): VariableTableMapType { const string_mapping: VariableTable = twrap([ - ['byte', fwrap(string_byte)], - ['char', fwrap(string_char)], + ["byte", fwrap(string_byte)], + ["char", fwrap(string_char)], // dump - ['format', fwrap(string_format)], - ['find', fwrap(string_find)], + ["format", fwrap(string_format)], + ["find", fwrap(string_find)], // gfind // gsub - ['len', fwrap(string_len)], - ['lower', fwrap(string_lower)], - ['rep', fwrap(string_rep)], - ['reverse', fwrap(string_reverse)], - ['sub', fwrap(string_sub)], - ['upper', fwrap(string_upper)], + ["len", fwrap(string_len)], + ["lower", fwrap(string_lower)], + ["rep", fwrap(string_rep)], + ["reverse", fwrap(string_reverse)], + ["sub", fwrap(string_sub)], + ["upper", fwrap(string_upper)], ]); const table_mapping: VariableTable = twrap([ - ['concat', { data_type: VariableKind.NativeFunction, native_function: table_concat }], + ["concat", { data_type: VariableKind.NativeFunction, native_function: table_concat }], // foreach // foreachi // getn - ['insert', fwrap(table_insert)], - ['move', fwrap(table_move)], - ['pack', fwrap(table_pack)], - ['remove', fwrap(table_remove)], + ["insert", fwrap(table_insert)], + ["move", fwrap(table_move)], + ["pack", fwrap(table_pack)], + ["remove", fwrap(table_remove)], // setn - ['sort', fwrap(table_sort)], - ['unpack', fwrap(table_unpack)], + ["sort", fwrap(table_sort)], + ["unpack", fwrap(table_unpack)], ]); const math_mapping: VariableTable = twrap([ - ['abs', fwrap(math_abs)], - ['acos', fwrap(math_acos)], - ['asin', fwrap(math_asin)], - ['atan', fwrap(math_atan)], + ["abs", fwrap(math_abs)], + ["acos", fwrap(math_acos)], + ["asin", fwrap(math_asin)], + ["atan", fwrap(math_atan)], // atan2 - ['ceil', fwrap(math_ceil)], - ['cos', fwrap(math_cos)], - ['deg', fwrap(math_deg)], - ['exp', fwrap(math_exp)], - ['floor', fwrap(math_floor)], - ['fmod', fwrap(math_fmod)], - ['log', fwrap(math_log)], + ["ceil", fwrap(math_ceil)], + ["cos", fwrap(math_cos)], + ["deg", fwrap(math_deg)], + ["exp", fwrap(math_exp)], + ["floor", fwrap(math_floor)], + ["fmod", fwrap(math_fmod)], + ["log", fwrap(math_log)], // log10 - ['max', fwrap(math_max)], - ['min', fwrap(math_min)], + ["max", fwrap(math_max)], + ["min", fwrap(math_min)], // mod - ['modf', fwrap(math_modf)], + ["modf", fwrap(math_modf)], // pow - ['rad', fwrap(math_rad)], - ['random', fwrap(math_random)], - ['sin', fwrap(math_sin)], - ['sqrt', fwrap(math_sqrt)], - ['tan', fwrap(math_tan)], + ["rad", fwrap(math_rad)], + ["random", fwrap(math_random)], + ["sin", fwrap(math_sin)], + ["sqrt", fwrap(math_sqrt)], + ["tan", fwrap(math_tan)], // frexp // ldexp - ['tointeger', fwrap(math_tointeger)], - ['type', fwrap(math_type)], - ['ult', fwrap(math_ult)], + ["tointeger", fwrap(math_tointeger)], + ["type", fwrap(math_type)], + ["ult", fwrap(math_ult)], // Constants - ['pi', make_number(Math.PI)], - ['maxinteger', make_number(Number.MAX_SAFE_INTEGER)], - ['mininteger', make_number(Number.MIN_SAFE_INTEGER)], - ['huge', make_number(Number.POSITIVE_INFINITY)], + ["pi", make_number(Math.PI)], + ["maxinteger", make_number(Number.MAX_SAFE_INTEGER)], + ["mininteger", make_number(Number.MIN_SAFE_INTEGER)], + ["huge", make_number(Number.POSITIVE_INFINITY)], ]); const global: VariableTable = twrap([ - ['assert', fwrap(assert)], - ['error', fwrap(error)], - ['find', fwrap(find)], - ['first', fwrap(first)], + ["assert", fwrap(assert)], + ["error", fwrap(error)], + ["find", fwrap(find)], + ["first", fwrap(first)], // getmetatable - ['ipairs', fwrap(ipairs)], - ['isempty', fwrap(is_empty)], - ['keys', fwrap(keys)], - ['math', math_mapping], - ['next', fwrap(next)], - ['pairs', fwrap(pairs)], + ["ipairs", fwrap(ipairs)], + ["isempty", fwrap(is_empty)], + ["keys", fwrap(keys)], + ["math", math_mapping], + ["next", fwrap(next)], + ["pairs", fwrap(pairs)], // pcall - ['print', fwrap(print)], - ['range', fwrap(range)], + ["print", fwrap(print)], + ["range", fwrap(range)], // rawequal // rawget // rawset - ['select', fwrap(select)], + ["select", fwrap(select)], // setmetatable - ['string', string_mapping], - ['table', table_mapping], - ['tonumber', fwrap(to_number)], - ['tostring', fwrap(to_string)], - ['type', fwrap(type)], - ['values', fwrap(values)], - ['warn', fwrap(warn)], + ["string", string_mapping], + ["table", table_mapping], + ["tonumber", fwrap(to_number)], + ["tostring", fwrap(to_string)], + ["type", fwrap(type)], + ["values", fwrap(values)], + ["warn", fwrap(warn)], // xpcall ]); - global.table.set('_G', global) + global.table.set("_G", global); return global.table; } diff --git a/src/opcode.mts b/src/opcode.mts index 37f8949..21d8e9d 100644 --- a/src/opcode.mts +++ b/src/opcode.mts @@ -1,133 +1,132 @@ -import type { Variable } from './variable/definition/type/variable.type.mjs' -import type { Debug } from './lexer.mjs' +import type { Variable } from "./variable/definition/type/variable.type.mjs"; +import type { Debug } from "./lexer.mjs"; export enum OpCode { - Load, - Store, - Push, - Pop, - Dup, - Swap, + Load = "load", + Store = "store", + Push = "push", + Pop = "pop", + Dup = "dup", + Swap = "swap", - IterUpdateState, - IterNext, - IterJumpIfDone, + IterUpdateState = "iter_update_state", + IterNext = "iter_next", + IterJumpIfDone = "iter_jump_if_done", - NewTable, - LoadIndex, - StoreIndex, + NewTable = "new_table", + LoadIndex = "load_index", + StoreIndex = "store_index", - Add, - Subtract, - Multiply, - Divide, - FloorDivide, - Modulo, - Exponent, - Concat, + Add = "add", + Subtract = "subtract", + Multiply = "multiply", + Divide = "divide", + FloorDivide = "floor_divide", + Modulo = "modulo", + Exponent = "exponent", + Concat = "concat", - BitAnd, - BitOr, - BitXOr, - BitNot, - BitShiftLeft, - BitShiftRight, + BitAnd = "bit_and", + BitOr = "bit_or", + BitXOr = "bit_xor", + BitNot = "bit_not", + BitShiftLeft = "bit_shift_left", + BitShiftRight = "bit_shift_right", - Equals, - NotEquals, - LessThan, - LessThanEquals, - GreaterThan, - GreaterThanEquals, - And, - Or, + Equals = "equals", + NotEquals = "not_equals", + LessThan = "less_than", + LessThanEquals = "less_than_equals", + GreaterThan = "greater_than", + GreaterThanEquals = "greater_than_equals", - Not, - Negate, - Length, - IsNotNil, + And = "and", + Or = "or", + Not = "not", - StartBlock, - EndBlock, - MakeLocal, - Call, - Return, - Jump, - JumpIfNot, - JumpIf, + Negate = "negate", + Length = "length", + IsNotNil = "is_not_nil", - StartStackChange, - EndStackChange, - ArgumentCount, - Break, + StartBlock = "start_block", + EndBlock = "end_block", + MakeLocal = "make_local", + Call = "call", + Return = "return", + Jump = "jump", + JumpIfNot = "jump_if_not", + JumpIf = "jump_if", + + StartStackChange = "start_stack_change", + EndStackChange = "end_stack_change", + ArgumentCount = "argument_count", + Break = "break", } export function op_code_name(op_code: OpCode): string { switch (op_code) { - case OpCode.Load: return 'Load' - case OpCode.Store: return 'Store' - case OpCode.Push: return 'Push' - case OpCode.Pop: return 'Pop' - case OpCode.Dup: return 'Dup' - case OpCode.Swap: return 'Swap' - case OpCode.IterUpdateState: return 'IterUpdateState' - case OpCode.IterNext: return 'IterNext' - case OpCode.IterJumpIfDone: return 'IterJumpIfDone' - case OpCode.NewTable: return 'NewTable' - case OpCode.LoadIndex: return 'LoadIndex' - case OpCode.StoreIndex: return 'StoreIndex' - case OpCode.Add: return 'Add' - case OpCode.Subtract: return 'Subtract' - case OpCode.Multiply: return 'Multiply' - case OpCode.Divide: return 'Divide' - case OpCode.FloorDivide: return 'FloorDivide' - case OpCode.Modulo: return 'Modulo' - case OpCode.Exponent: return 'Exponent' - case OpCode.Concat: return 'Concat' - case OpCode.BitAnd: return 'BitAnd' - case OpCode.BitOr: return 'BitOr' - case OpCode.BitXOr: return 'BitXOr' - case OpCode.BitNot: return 'BitNot' - case OpCode.BitShiftLeft: return 'BitShiftLeft' - case OpCode.BitShiftRight: return 'BitShiftRight' - case OpCode.Equals: return 'Equals' - case OpCode.NotEquals: return 'NotEquals' - case OpCode.LessThan: return 'LessThan' - case OpCode.LessThanEquals: return 'LessThanEquals' - case OpCode.GreaterThan: return 'GreaterThan' - case OpCode.GreaterThanEquals: return 'GreaterThanEquals' - case OpCode.And: return 'And' - case OpCode.Or: return 'Or' - case OpCode.Not: return 'Not' - case OpCode.Negate: return 'Negate' - case OpCode.Length: return 'Length' - case OpCode.IsNotNil: return 'IsNotNil' - case OpCode.StartBlock: return 'StartBlock' - case OpCode.EndBlock: return 'EndBlock' - case OpCode.MakeLocal: return 'MakeLocal' - case OpCode.Call: return 'Call' - case OpCode.Return: return 'Return' - case OpCode.Jump: return 'Jump' - case OpCode.JumpIfNot: return 'JumpIfNot' - case OpCode.JumpIf: return 'JumpIf' - case OpCode.StartStackChange: return 'StartStackChange' - case OpCode.EndStackChange: return 'EndStackChange' - case OpCode.ArgumentCount: return 'ArgumentCount' - case OpCode.Break: return 'Break[Debug]' - default: - throw new Error() + case OpCode.Load: return "Load"; + case OpCode.Store: return "Store"; + case OpCode.Push: return "Push"; + case OpCode.Pop: return "Pop"; + case OpCode.Dup: return "Dup"; + case OpCode.Swap: return "Swap"; + case OpCode.IterUpdateState: return "IterUpdateState"; + case OpCode.IterNext: return "IterNext"; + case OpCode.IterJumpIfDone: return "IterJumpIfDone"; + case OpCode.NewTable: return "NewTable"; + case OpCode.LoadIndex: return "LoadIndex"; + case OpCode.StoreIndex: return "StoreIndex"; + case OpCode.Add: return "Add"; + case OpCode.Subtract: return "Subtract"; + case OpCode.Multiply: return "Multiply"; + case OpCode.Divide: return "Divide"; + case OpCode.FloorDivide: return "FloorDivide"; + case OpCode.Modulo: return "Modulo"; + case OpCode.Exponent: return "Exponent"; + case OpCode.Concat: return "Concat"; + case OpCode.BitAnd: return "BitAnd"; + case OpCode.BitOr: return "BitOr"; + case OpCode.BitXOr: return "BitXOr"; + case OpCode.BitNot: return "BitNot"; + case OpCode.BitShiftLeft: return "BitShiftLeft"; + case OpCode.BitShiftRight: return "BitShiftRight"; + case OpCode.Equals: return "Equals"; + case OpCode.NotEquals: return "NotEquals"; + case OpCode.LessThan: return "LessThan"; + case OpCode.LessThanEquals: return "LessThanEquals"; + case OpCode.GreaterThan: return "GreaterThan"; + case OpCode.GreaterThanEquals: return "GreaterThanEquals"; + case OpCode.And: return "And"; + case OpCode.Or: return "Or"; + case OpCode.Not: return "Not"; + case OpCode.Negate: return "Negate"; + case OpCode.Length: return "Length"; + case OpCode.IsNotNil: return "IsNotNil"; + case OpCode.StartBlock: return "StartBlock"; + case OpCode.EndBlock: return "EndBlock"; + case OpCode.MakeLocal: return "MakeLocal"; + case OpCode.Call: return "Call"; + case OpCode.Return: return "Return"; + case OpCode.Jump: return "Jump"; + case OpCode.JumpIfNot: return "JumpIfNot"; + case OpCode.JumpIf: return "JumpIf"; + case OpCode.StartStackChange: return "StartStackChange"; + case OpCode.EndStackChange: return "EndStackChange"; + case OpCode.ArgumentCount: return "ArgumentCount"; + case OpCode.Break: return "Break[Debug]"; } } export interface Op { - code: OpCode, - arg?: Variable, - debug: Debug, + code: OpCode; + arg?: Variable; + debug: Debug; } export interface Program { - code: Op[], - start: number, + code: Array; + start: number; } diff --git a/src/optimizer.mts b/src/optimizer.mts index 7a31967..b9fcc8e 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,14 +1,11 @@ -import type { Chunk, Expression } from './ast.mjs' -import type { IfBlock, While, For, NumericFor, Repeat } from './ast.mjs' -import type { Assignment, Value } from './ast.mjs' -import { StatementKind, ExpressionKind, ValueKind } from './ast.mjs' +import { type Assignment, type Chunk, type Expression, ExpressionKind, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, ValueKind, type While } from "./ast.mjs"; const CONSTANT_VALUES = [ ValueKind.NilLiteral, ValueKind.NumberLiteral, ValueKind.BooleanLiteral, ValueKind.StringLiteral, -] +]; function compute_arithmetic_operation( expression: Expression, @@ -16,35 +13,40 @@ function compute_arithmetic_operation( constants: Map ): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) + const lhs = compute_constant_expression(expression.lhs, constants); + const rhs = compute_constant_expression(expression.rhs, constants); + if (lhs === undefined || rhs === undefined) - return undefined + { + return undefined; + } return { kind: ValueKind.NumberLiteral, - number: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), + number: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, - } + }; } function compute_comparison_operation( expression: Expression, - operation: (a: number, b: number) => boolean, + operation: (a: number | string, b: number | string) => boolean, constants: Map ): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) + const lhs = compute_constant_expression(expression.lhs, constants); + const rhs = compute_constant_expression(expression.rhs, constants); if (lhs === undefined || rhs === undefined) - return undefined + { + return undefined; + } return { kind: ValueKind.BooleanLiteral, - boolean: operation((lhs?.number ?? 0), (rhs?.number ?? 0)), + boolean: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, - } + }; } function compute_logical_operation( @@ -53,13 +55,15 @@ function compute_logical_operation( constants: Map ): Value | undefined { - const lhs = compute_constant_expression(expression.lhs, constants) - const rhs = compute_constant_expression(expression.rhs, constants) + const lhs = compute_constant_expression(expression.lhs, constants); + const rhs = compute_constant_expression(expression.rhs, constants); if (lhs === undefined || rhs === undefined) - return undefined + { + return undefined; + } - const lhs_falsy: boolean = lhs.kind === ValueKind.NilLiteral || lhs.kind === ValueKind.BooleanLiteral && lhs.boolean === false; + const lhs_falsy: boolean = lhs.kind === ValueKind.NilLiteral || lhs.kind === ValueKind.BooleanLiteral && !(lhs.boolean ?? true); if (operation === ExpressionKind.And) { @@ -69,107 +73,260 @@ function compute_logical_operation( return lhs_falsy ? rhs : lhs; } +// @TODO: Fix complexity warning function compute_constant_expression( expression: Expression | undefined, constants: Map ): Value | undefined { if (expression === undefined) - return undefined + { + return undefined; + } + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (expression.kind) { case ExpressionKind.Value: { if (expression.value === undefined) - return undefined + { + return undefined; + } + + const value = expression.value; - const value = expression.value if (CONSTANT_VALUES.includes(value.kind)) - return value - if (value.kind === ValueKind.Variable && constants.has(value.identifier ?? '')) - return constants.get(value.identifier ?? '') - return undefined + { + return value; + } + + if (value.kind === ValueKind.Variable && constants.has(value.identifier ?? "")) + { + return constants.get(value.identifier ?? ""); + } + + return undefined; } case ExpressionKind.Addition: - return compute_arithmetic_operation(expression, (a, b) => a + b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a + b; + }, + constants + ); case ExpressionKind.Subtract: - return compute_arithmetic_operation(expression, (a, b) => a - b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a - b; + }, + constants + ); case ExpressionKind.Multiplication: - return compute_arithmetic_operation(expression, (a, b) => a * b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a * b; + }, + constants + ); case ExpressionKind.Division: - return compute_arithmetic_operation(expression, (a, b) => a / b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a / b; + }, + constants + ); case ExpressionKind.FloorDivision: - return compute_arithmetic_operation(expression, (a, b) => Math.floor(a / b), constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return Math.floor(a / b); + }, + constants + ); case ExpressionKind.Modulo: - return compute_arithmetic_operation(expression, (a, b) => a % b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a % b; + }, + constants + ); case ExpressionKind.Exponent: - return compute_arithmetic_operation(expression, (a, b) => Math.pow(a, b), constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return Math.pow(a, b); + }, + constants + ); case ExpressionKind.Concat: - return undefined + return undefined; case ExpressionKind.BitAnd: - return compute_arithmetic_operation(expression, (a, b) => a & b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a & b; + }, + constants + ); case ExpressionKind.BitOr: - return compute_arithmetic_operation(expression, (a, b) => a | b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a | b; + }, + constants + ); case ExpressionKind.BitXOr: - return compute_arithmetic_operation(expression, (a, b) => a ^ b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a ^ b; + }, + constants + ); case ExpressionKind.BitShiftLeft: - return compute_arithmetic_operation(expression, (a, b) => a << b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a << b; + }, + constants + ); case ExpressionKind.BitShiftRight: - return compute_arithmetic_operation(expression, (a, b) => a >> b, constants) + return compute_arithmetic_operation( + expression, + (a: number, b: number): number => + { + return a >> b; + }, + constants + ); case ExpressionKind.BitNot: - return undefined + return undefined; case ExpressionKind.Equals: - return compute_comparison_operation(expression, (a, b) => a === b, constants) + return compute_comparison_operation( + expression, + (a: unknown, b: unknown): boolean => + { + return a === b; + }, + constants + ); case ExpressionKind.NotEquals: - return compute_comparison_operation(expression, (a, b) => a !== b, constants) + return compute_comparison_operation( + expression, + (a: unknown, b: unknown): boolean => + { + return a !== b; + }, + constants + ); case ExpressionKind.LessThan: - return compute_comparison_operation(expression, (a, b) => a < b, constants) + return compute_comparison_operation( + expression, + (a: number | string, b: number | string): boolean => + { + return a < b; + }, + constants + ); case ExpressionKind.LessThanEquals: - return compute_comparison_operation(expression, (a, b) => a <= b, constants) + return compute_comparison_operation( + expression, + (a: number | string, b: number | string): boolean => + { + return a <= b; + }, + constants + ); case ExpressionKind.GreaterThan: - return compute_comparison_operation(expression, (a, b) => a > b, constants) + return compute_comparison_operation( + expression, + (a: number | string, b: number | string): boolean => + { + return a > b; + }, + constants + ); case ExpressionKind.GreaterThanEquals: - return compute_comparison_operation(expression, (a, b) => a >= b, constants) + return compute_comparison_operation( + expression, + (a: number | string, b: number | string): boolean => + { + return a >= b; + }, + constants + ); case ExpressionKind.And: - return compute_logical_operation(expression, ExpressionKind.And, constants) + return compute_logical_operation( + expression, + ExpressionKind.And, + constants + ); case ExpressionKind.Or: - return compute_logical_operation(expression, ExpressionKind.Or, constants) + return compute_logical_operation( + expression, + ExpressionKind.Or, + constants + ); case ExpressionKind.Not: { - const lhs = compute_constant_expression(expression.lhs, constants) + const lhs = compute_constant_expression(expression.lhs, constants); + if (lhs === undefined) - return undefined + { + return undefined; + } return { kind: ValueKind.BooleanLiteral, boolean: !(lhs.boolean ?? false), token: expression.token, - } + }; } case ExpressionKind.Negate: { - const lhs = compute_constant_expression(expression.lhs, constants) + const lhs = compute_constant_expression(expression.lhs, constants); + if (lhs === undefined) - return undefined + { + return undefined; + } return { kind: ValueKind.NumberLiteral, - number: -(lhs.number ?? false), + number: -(lhs.number ?? 0), token: expression.token, - } + }; } case ExpressionKind.Length: - return undefined + return undefined; default: - return undefined + return undefined; } } @@ -179,29 +336,36 @@ function optimize_expression( ): void { if (expression === undefined) - return + { + return; + } if (expression.value?.function !== undefined) { - optimize_chunk(expression.value.function.body, constants) - return + optimize_chunk(expression.value.function.body, constants); + + return; } - const value = compute_constant_expression(expression, constants) + const value = compute_constant_expression(expression, constants); + if (value !== undefined) { - expression.kind = ExpressionKind.Value - expression.value = value - return + expression.kind = ExpressionKind.Value; + expression.value = value; + + return; } - optimize_expression(expression.lhs, constants) - optimize_expression(expression.rhs, constants) - optimize_expression(expression.expression, constants) - optimize_expression(expression.index, constants) + optimize_expression(expression.lhs, constants); + optimize_expression(expression.rhs, constants); + optimize_expression(expression.expression, constants); + optimize_expression(expression.index, constants); for (const argument of expression.arguments ?? []) - optimize_expression(argument, constants) + { + optimize_expression(argument, constants); + } } function mark_local_constants(assignment: Assignment, constants: Map): void @@ -209,26 +373,36 @@ function mark_local_constants(assignment: Assignment, constants: Map= assignment.lhs.length) - continue + { + continue; + } - const lhs = assignment.lhs[index] + const lhs = assignment.lhs[index]; if (lhs === undefined) - throw new Error() + { + throw new Error(); + } if (lhs.kind !== ExpressionKind.Value) - continue + { + continue; + } if (lhs.value?.identifier === undefined) - continue + { + continue; + } - const name = lhs.value.identifier - const value = compute_constant_expression(rhs, constants) + const name = lhs.value.identifier; + const value = compute_constant_expression(rhs, constants); if (value === undefined) - continue + { + continue; + } - constants.set(name, value) + constants.set(name, value); } } @@ -237,12 +411,18 @@ function unmark_constants_if_reassigned(assignment: Assignment, constants: Map x.value?.identifier === name) + (x: Expression): boolean => + { + return x.value?.identifier === name; + } + ); if (index < 0) - continue - assignment.lhs.splice(index, 1) - assignment.rhs.splice(index, 1) + { + continue; + } + + assignment.lhs.splice(index, 1); + assignment.rhs.splice(index, 1); } } - chunk.statements = chunk.statements - .filter(x => x.assignment === undefined || x.assignment.lhs.length > 0) + chunk.statements = chunk.statements.filter( + (x: Statement): boolean => + { + return x.assignment === undefined || x.assignment.lhs.length > 0; + } + ); } function optimize_if(if_block: IfBlock | undefined, constants: Map): void { if (if_block === undefined) - return + { + return; + } - optimize_expression(if_block.condition, constants) - optimize_chunk(if_block.body, constants) + optimize_expression(if_block.condition, constants); + optimize_chunk(if_block.body, constants); } function optimize_while(while_block: While | undefined, constants: Map): void { if (while_block === undefined) - return + { + return; + } - optimize_expression(while_block.condition, constants) - optimize_chunk(while_block.body, constants) + optimize_expression(while_block.condition, constants); + optimize_chunk(while_block.body, constants); } function optimize_for(for_block: For | undefined, constants: Map): void { if (for_block === undefined) - return + { + return; + } - optimize_expression(for_block.iterator, constants) - optimize_chunk(for_block.body, constants) + optimize_expression(for_block.iterator, constants); + optimize_chunk(for_block.body, constants); } function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map): void { if (numeric_for_block === undefined) - return + { + return; + } - optimize_expression(numeric_for_block.start, constants) - optimize_expression(numeric_for_block.end, constants) - optimize_expression(numeric_for_block.step, constants) - optimize_chunk(numeric_for_block.body, constants) + optimize_expression(numeric_for_block.start, constants); + optimize_expression(numeric_for_block.end, constants); + optimize_expression(numeric_for_block.step, constants); + optimize_chunk(numeric_for_block.body, constants); } function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void { if (repeat_block === undefined) - return + { + return; + } - optimize_expression(repeat_block.condition, constants) - optimize_chunk(repeat_block.body, constants) + optimize_expression(repeat_block.condition, constants); + optimize_chunk(repeat_block.body, constants); } export function optimize_chunk(chunk: Chunk, parent_constants?: Map): void { - const constants = new Map(parent_constants) + const constants = new Map(parent_constants); + for (const statement of chunk.statements) { + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (statement.kind) { case StatementKind.Assignment: - optimize_assignment(statement.assignment, constants) - break + optimize_assignment(statement.assignment, constants); + break; case StatementKind.Expression: - optimize_expression(statement.expression, constants) - break + optimize_expression(statement.expression, constants); + break; case StatementKind.If: - optimize_if(statement.if, constants) - break + optimize_if(statement.if, constants); + break; case StatementKind.While: - optimize_while(statement.if, constants) - break + optimize_while(statement.if, constants); + break; case StatementKind.For: - optimize_for(statement.for, constants) - break + optimize_for(statement.for, constants); + break; case StatementKind.NumericFor: - optimize_numeric_for(statement.numeric_for, constants) - break + optimize_numeric_for(statement.numeric_for, constants); + break; case StatementKind.Repeat: - optimize_repeat(statement.repeat, constants) - break + optimize_repeat(statement.repeat, constants); + break; case StatementKind.Do: if (statement.do !== undefined) - optimize_chunk(statement.do.body, constants) - break + { + optimize_chunk(statement.do.body, constants); + } + + break; case StatementKind.Return: for (const expression of statement.return?.values ?? []) - optimize_expression(expression, constants) - break + { + optimize_expression(expression, constants); + } + + break; case StatementKind.Local: case StatementKind.Break: - break + break; } } @@ -393,9 +613,11 @@ export function optimize_chunk(chunk: Chunk, parent_constants?: Map = new Map() - let current_numeric_key = 1 + const elements: Map = new Map(); + let current_numeric_key = 1; while (stream.peek().kind !== TokenKind.SquiglyClose) { - const element = parse_table_key(stream) + const element = parse_table_key(stream); + if (element instanceof Error) - return element + { + return element; + } if (consume(stream, TokenKind.Assign)) { - const value = parse_expression(stream) + const value = parse_expression(stream); + if (value instanceof Error) - return value + { + return value; + } - elements.set(element, value) + elements.set(element, value); } else { @@ -125,7 +137,7 @@ function parse_table(stream: TokenStream): Value | Error kind: TokenKind.NumberLiteral, data: current_numeric_key.toString(), debug: element.token.debug, - } + }; const key = { kind: ExpressionKind.Value, @@ -135,165 +147,210 @@ function parse_table(stream: TokenStream): Value | Error token: key_token, number: current_numeric_key, }, - } + }; - current_numeric_key += 1 - elements.set(key, element) + current_numeric_key = current_numeric_key + 1; + elements.set(key, element); } if (!consume(stream, TokenKind.Comma)) - break + { + break; + } } - const close_squigly = expect(stream, TokenKind.SquiglyClose) + const close_squigly = expect(stream, TokenKind.SquiglyClose); + if (close_squigly instanceof Error) - return close_squigly + { + return close_squigly; + } return { kind: ValueKind.TableLiteral, token: squigly_open, table: elements, - } + }; } function parse_value(stream: TokenStream): Value | Error { - const token = stream.peek() + const token = stream.peek(); + + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { case TokenKind.NumberLiteral: - return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) } + return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; case TokenKind.BooleanLiteral: - return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data === 'true' } + return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; case TokenKind.StringLiteral: - return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data } + return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data }; case TokenKind.NilLiteral: - return { kind: ValueKind.NilLiteral, token: stream.next() } + return { kind: ValueKind.NilLiteral, token: stream.next() }; case TokenKind.Identifier: - return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data } + return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data }; case TokenKind.SquiglyOpen: - return parse_table(stream) + return parse_table(stream); case TokenKind.Function: - return parse_function_value(stream.next(), stream) + return parse_function_value(stream.next(), stream); default: - return error(token, `Expected value, got ${ token_kind_to_string(token.kind) } instead`) + return error(token, `Expected value, got ${token_kind_to_string(token.kind)} instead`); } } function unary_type_to_expression_kind(kind: TokenKind): ExpressionKind { + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (kind) { - case TokenKind.Not: return ExpressionKind.Not - case TokenKind.Subtract: return ExpressionKind.Negate - case TokenKind.Hash: return ExpressionKind.Length - case TokenKind.BitXOrNot: return ExpressionKind.BitNot + case TokenKind.Not: return ExpressionKind.Not; + case TokenKind.Subtract: return ExpressionKind.Negate; + case TokenKind.Hash: return ExpressionKind.Length; + case TokenKind.BitXOrNot: return ExpressionKind.BitNot; + default: - throw new Error() + throw new Error(); } } function parse_unary_operator(stream: TokenStream): Expression | Error { - const operator_token = stream.next() - const operator = unary_type_to_expression_kind(operator_token.kind) + const operator_token = stream.next(); + const operator = unary_type_to_expression_kind(operator_token.kind); + + const expression = parse_expression(stream); - const expression = parse_expression(stream) if (expression instanceof Error) - return expression + { + return expression; + } return { kind: operator, token: operator_token, expression: expression, - } + }; } function parse_value_expression(stream: TokenStream): Expression | Error { if (consume(stream, TokenKind.OpenBrace)) { - const sub_expression = parse_expression(stream) + const sub_expression = parse_expression(stream); + if (sub_expression instanceof Error) - return sub_expression + { + return sub_expression; + } + + const close_brace = expect(stream, TokenKind.CloseBrace); - const close_brace = expect(stream, TokenKind.CloseBrace) if (close_brace instanceof Error) - return close_brace + { + return close_brace; + } - return sub_expression + return sub_expression; } if (UNARY.includes(stream.peek().kind)) - return parse_unary_operator(stream) + { + return parse_unary_operator(stream); + } + + const value = parse_value(stream); - const value = parse_value(stream) if (value instanceof Error) - return value + { + return value; + } const expression_value = { kind: ExpressionKind.Value, token: value.token, value: value, - } + }; - return parse_access_expression(expression_value, stream) + return parse_access_expression(expression_value, stream); } function parse_call(func: Expression, stream: TokenStream): Expression | Error { - const open_brace = stream.next() - const args: Expression[] = [] + const open_brace = stream.next(); + const args: Array = []; + while (stream.peek().kind !== TokenKind.CloseBrace) { - const argument = parse_expression(stream) + const argument = parse_expression(stream); + if (argument instanceof Error) - break + { + break; + } + + args.push(argument); - args.push(argument) if (!consume(stream, TokenKind.Comma)) - break + { + break; + } } - const close_brace = expect(stream, TokenKind.CloseBrace) + const close_brace = expect(stream, TokenKind.CloseBrace); + if (close_brace instanceof Error) - return close_brace + { + return close_brace; + } return parse_access_expression({ kind: ExpressionKind.Call, token: open_brace, expression: func, arguments: args, - }, stream) + }, stream); } function parse_index(table: Expression, stream: TokenStream): Expression | Error { - const open_square = stream.next() - const index = parse_expression(stream) + const open_square = stream.next(); + const index = parse_expression(stream); + if (index instanceof Error) - return index + { + return index; + } + + const close_square = expect(stream, TokenKind.CloseSquare); - const close_square = expect(stream, TokenKind.CloseSquare) if (close_square instanceof Error) - return close_square + { + return close_square; + } - return parse_access_expression({ - kind: ExpressionKind.Index, - token: open_square, - expression: table, - index: index, - }, stream) + return parse_access_expression( + { + kind: ExpressionKind.Index, + token: open_square, + expression: table, + index: index, + }, + stream + ); } function parse_dot(table: Expression, stream: TokenStream): Expression | Error { - const dot = stream.next() - const index = expect(stream, TokenKind.Identifier) + const dot = stream.next(); + const index = expect(stream, TokenKind.Identifier); + if (index instanceof Error) - return index + { + return index; + } return parse_access_expression({ kind: ExpressionKind.Index, @@ -308,72 +365,79 @@ function parse_dot(table: Expression, stream: TokenStream): Expression | Error string: index.data, }, }, - }, stream) + }, stream); } function parse_single_argument_call(func: Expression, stream: TokenStream): Expression | Error { - const argument = parse_expression(stream) + const argument = parse_expression(stream); + if (argument instanceof Error) - return argument + { + return argument; + } return { kind: ExpressionKind.Call, token: func.token, expression: func, arguments: [argument], - } + }; } function parse_access_expression(expression: Expression, stream: TokenStream): Expression | Error { + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (stream.peek().kind) { case TokenKind.OpenBrace: - return parse_call(expression, stream) + return parse_call(expression, stream); case TokenKind.OpenSquare: - return parse_index(expression, stream) + return parse_index(expression, stream); case TokenKind.Dot: - return parse_dot(expression, stream) + return parse_dot(expression, stream); case TokenKind.SquiglyOpen: case TokenKind.StringLiteral: - return parse_single_argument_call(expression, stream) + return parse_single_argument_call(expression, stream); } - return expression + return expression; } function operation_type_to_expression_kind( - operation_type: TokenKind): ExpressionKind + operation_type: TokenKind +): ExpressionKind { + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (operation_type) { - case TokenKind.Addition: return ExpressionKind.Addition - case TokenKind.Subtract: return ExpressionKind.Subtract - case TokenKind.Multiply: return ExpressionKind.Multiplication - case TokenKind.Division: return ExpressionKind.Division - case TokenKind.FloorDivision: return ExpressionKind.FloorDivision - case TokenKind.Modulo: return ExpressionKind.Modulo - case TokenKind.Exponent: return ExpressionKind.Exponent - case TokenKind.Concat: return ExpressionKind.Concat - case TokenKind.BitAnd: return ExpressionKind.BitAnd - case TokenKind.BitOr: return ExpressionKind.BitOr - case TokenKind.BitXOrNot: return ExpressionKind.BitXOr - case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft - case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight - case TokenKind.LessThan: return ExpressionKind.LessThan - case TokenKind.LessThanEquals: return ExpressionKind.LessThanEquals - case TokenKind.GreaterThan: return ExpressionKind.GreaterThan - case TokenKind.GreaterThanEquals: return ExpressionKind.GreaterThanEquals - case TokenKind.Equals: return ExpressionKind.Equals - case TokenKind.NotEquals: return ExpressionKind.NotEquals - case TokenKind.And: return ExpressionKind.And - case TokenKind.Or: return ExpressionKind.Or + case TokenKind.Addition: return ExpressionKind.Addition; + case TokenKind.Subtract: return ExpressionKind.Subtract; + case TokenKind.Multiply: return ExpressionKind.Multiplication; + case TokenKind.Division: return ExpressionKind.Division; + case TokenKind.FloorDivision: return ExpressionKind.FloorDivision; + case TokenKind.Modulo: return ExpressionKind.Modulo; + case TokenKind.Exponent: return ExpressionKind.Exponent; + case TokenKind.Concat: return ExpressionKind.Concat; + case TokenKind.BitAnd: return ExpressionKind.BitAnd; + case TokenKind.BitOr: return ExpressionKind.BitOr; + case TokenKind.BitXOrNot: return ExpressionKind.BitXOr; + case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft; + case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight; + case TokenKind.LessThan: return ExpressionKind.LessThan; + case TokenKind.LessThanEquals: return ExpressionKind.LessThanEquals; + case TokenKind.GreaterThan: return ExpressionKind.GreaterThan; + case TokenKind.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; + case TokenKind.Equals: return ExpressionKind.Equals; + case TokenKind.NotEquals: return ExpressionKind.NotEquals; + case TokenKind.And: return ExpressionKind.And; + case TokenKind.Or: return ExpressionKind.Or; + default: - throw new Error() + throw new Error(); } } @@ -383,56 +447,71 @@ function parse_operation( ): Expression | Error { if (order >= ORDERS.length) - return parse_value_expression(stream) + { + return parse_value_expression(stream); + } - let lhs = parse_operation(stream, order + 1) + let lhs = parse_operation(stream, order + 1); if (lhs instanceof Error) - return lhs + { + return lhs; + } - const orders_order = ORDERS[order] + const orders_order = ORDERS[order]; if (orders_order === undefined) - throw new Error() + { + throw new Error(); + } while (orders_order.includes(stream.peek().kind)) { - const operation_type = stream.next() - const rhs = parse_operation(stream, order + 1) + const operation_type = stream.next(); + const rhs = parse_operation(stream, order + 1); if (rhs instanceof Error) - return rhs + { + return rhs; + } - const expression_kind = operation_type_to_expression_kind(operation_type.kind) + const expression_kind = operation_type_to_expression_kind(operation_type.kind); lhs = { kind: expression_kind, token: operation_type, lhs: lhs, rhs: rhs, - } + }; } - return lhs + return lhs; } function parse_expression(stream: TokenStream): Expression | Error { if (stream.peek().kind === TokenKind.BitXOrNot) - return parse_unary_operator(stream) + { + return parse_unary_operator(stream); + } - return parse_operation(stream, 0) + return parse_operation(stream, 0); } -function parse_local_statement(local: Token, values: Expression[]): Statement | Error +function parse_local_statement(local: Token, values: Array): Statement | Error { - const names: Token[] = [] + const names: Array = []; + for (const expression of values) { - const value = expression.value + const value = expression.value; + if (value === undefined || value.kind !== ValueKind.Variable) - return error(expression.token, 'Invalid local name') - names.push(value.token) + { + return error(expression.token, "Invalid local name"); + } + + names.push(value.token); } return { @@ -441,38 +520,51 @@ function parse_local_statement(local: Token, values: Expression[]): Statement | token: local, names: names, }, - } + }; } function parse_assign_or_expression(stream: TokenStream): Statement | Error { - const local = expect(stream, TokenKind.Local) - const lhs: Expression[] = [] + const local = expect(stream, TokenKind.Local); + const lhs: Array = []; + while (lhs.length === 0 || consume(stream, TokenKind.Comma)) { - const lvalue = parse_expression(stream) + const lvalue = parse_expression(stream); + if (lvalue instanceof Error) - return lvalue - lhs.push(lvalue) + { + return lvalue; + } + + lhs.push(lvalue); } - const assign = expect(stream, TokenKind.Assign) + const assign = expect(stream, TokenKind.Assign); + if (assign instanceof Error) { if (local instanceof Error) + { // @TODO: Investigate as lhs[0] seems to always be undefined - return { kind: StatementKind.Expression, expression: lhs[0] } + return { kind: StatementKind.Expression, expression: lhs[0] }; + } - return parse_local_statement(local, lhs) + return parse_local_statement(local, lhs); } - const rhs: Expression[] = [] + const rhs: Array = []; + while (rhs.length === 0 || consume(stream, TokenKind.Comma)) { - const rvalue = parse_expression(stream) + const rvalue = parse_expression(stream); + if (rvalue instanceof Error) - return rvalue - rhs.push(rvalue) + { + return rvalue; + } + + rhs.push(rvalue); } return { @@ -483,27 +575,35 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error rhs: rhs, token: assign, }, - } + }; } function parse_return(stream: TokenStream): Statement | Error { - const ret = expect(stream, TokenKind.Return) + const ret = expect(stream, TokenKind.Return); + if (ret instanceof Error) - return ret + { + return ret; + } + + const values: Array = []; - const values: Expression[] = [] while (values.length === 0 || consume(stream, TokenKind.Comma)) { - const value = parse_expression(stream) + const value = parse_expression(stream); + if (value instanceof Error) { if (values.length > 0) - return value - break + { + return value; + } + + break; } - values.push(value) + values.push(value); } if (values.length === 0) @@ -515,7 +615,7 @@ function parse_return(stream: TokenStream): Statement | Error kind: ValueKind.NilLiteral, token: ret, }, - }) + }); } return { @@ -524,72 +624,104 @@ function parse_return(stream: TokenStream): Statement | Error values: values, token: ret, }, - } + }; } function parse_break(stream: TokenStream): Statement | Error { - const break_token = expect(stream, TokenKind.Break) + const break_token = expect(stream, TokenKind.Break); + if (break_token instanceof Error) - return break_token + { + return break_token; + } return { kind: StatementKind.Break, - } + }; } function parse_if(stream: TokenStream): Statement | Error { - const if_token = expect(stream, TokenKind.If) + const if_token = expect(stream, TokenKind.If); + if (if_token instanceof Error) - return if_token + { + return if_token; + } + + const condition = parse_expression(stream); - const condition = parse_expression(stream) if (condition instanceof Error) - return condition + { + return condition; + } + + const then = expect(stream, TokenKind.Then); - const then = expect(stream, TokenKind.Then) if (then instanceof Error) - return then + { + return then; + } + + const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End); - const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } + + const else_if_bodies: Array = []; + let else_body: Chunk | undefined = undefined; - const else_if_bodies: ElseIfBlock[] = [] - let else_body: Chunk | undefined = undefined while (consume(stream, TokenKind.ElseIf)) { - const condition = parse_expression(stream) + const condition = parse_expression(stream); + if (condition instanceof Error) - return condition + { + return condition; + } + + const then = expect(stream, TokenKind.Then); - const then = expect(stream, TokenKind.Then) if (then instanceof Error) - return then + { + return then; + } + + const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else); - const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else) if (chunk instanceof Error) - return chunk + { + return chunk; + } else_if_bodies.push({ body: chunk, condition: condition, token: then, - }) + }); } if (consume(stream, TokenKind.Else)) { - const chunk = parse(stream, TokenKind.End) + const chunk = parse(stream, TokenKind.End); + if (chunk instanceof Error) - return chunk - else_body = chunk + { + return chunk; + } + + else_body = chunk; } - const end = expect(stream, TokenKind.End) + const end = expect(stream, TokenKind.End); + if (end instanceof Error) - return end + { + return end; + } return { kind: StatementKind.If, @@ -600,28 +732,41 @@ function parse_if(stream: TokenStream): Statement | Error else_body: else_body, token: if_token, }, - } + }; } function parse_while(stream: TokenStream): Statement | Error { - const while_token = expect(stream, TokenKind.While) + const while_token = expect(stream, TokenKind.While); + if (while_token instanceof Error) - return while_token + { + return while_token; + } + + const condition = parse_expression(stream); - const condition = parse_expression(stream) if (condition instanceof Error) - return condition + { + return condition; + } + + const do_token = expect(stream, TokenKind.Do); - const do_token = expect(stream, TokenKind.Do) if (do_token instanceof Error) - return do_token + { + return do_token; + } + + const body = parse(stream, TokenKind.End); - const body = parse(stream, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } + + consume(stream, TokenKind.End); - consume(stream, TokenKind.End) return { kind: StatementKind.While, while: { @@ -629,42 +774,62 @@ function parse_while(stream: TokenStream): Statement | Error body: body, token: while_token, }, - } + }; } function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error { - const start = parse_expression(stream) + const start = parse_expression(stream); + if (start instanceof Error) - return start + { + return start; + } + + const comma = expect(stream, TokenKind.Comma); - const comma = expect(stream, TokenKind.Comma) if (comma instanceof Error) - return comma + { + return comma; + } + + const end = parse_expression(stream); - const end = parse_expression(stream) if (end instanceof Error) - return end + { + return end; + } + + let step: Expression | undefined = undefined; - let step: Expression | undefined = undefined if (consume(stream, TokenKind.Comma)) { - const expression = parse_expression(stream) + const expression = parse_expression(stream); + if (expression instanceof Error) - return expression + { + return expression; + } - step = expression + step = expression; } - const do_token = expect(stream, TokenKind.Do) + const do_token = expect(stream, TokenKind.Do); + if (do_token instanceof Error) - return do_token + { + return do_token; + } + + const body = parse(stream, TokenKind.End); - const body = parse(stream, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } + + consume(stream, TokenKind.End); - consume(stream, TokenKind.End) return { kind: StatementKind.NumericFor, numeric_for: { @@ -674,22 +839,30 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error step: step, body: body, }, - } + }; } function parse_for(stream: TokenStream): Statement | Error { - const for_token = expect(stream, TokenKind.For) + const for_token = expect(stream, TokenKind.For); + if (for_token instanceof Error) - return for_token + { + return for_token; + } + + const items: Array = []; - const items: Token[] = [] while (items.length === 0 || consume(stream, TokenKind.Comma)) { - const item = expect(stream, TokenKind.Identifier) + const item = expect(stream, TokenKind.Identifier); + if (item instanceof Error) - return item - items.push(item) + { + return item; + } + + items.push(item); } if (consume(stream, TokenKind.Assign)) @@ -697,28 +870,42 @@ function parse_for(stream: TokenStream): Statement | Error const token = items[0]; if (token === undefined) - throw new Error() + { + throw new Error(); + } - return parse_numeric_for(token, stream) + return parse_numeric_for(token, stream); } - const in_token = expect(stream, TokenKind.In) + const in_token = expect(stream, TokenKind.In); + if (in_token instanceof Error) - return in_token + { + return in_token; + } + + const iterator = parse_expression(stream); - const iterator = parse_expression(stream) if (iterator instanceof Error) - return iterator + { + return iterator; + } + + const do_token = expect(stream, TokenKind.Do); - const do_token = expect(stream, TokenKind.Do) if (do_token instanceof Error) - return do_token + { + return do_token; + } + + const body = parse(stream, TokenKind.End); - const body = parse(stream, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } - consume(stream, TokenKind.End) + consume(stream, TokenKind.End); return { kind: StatementKind.For, @@ -728,26 +915,38 @@ function parse_for(stream: TokenStream): Statement | Error body: body, token: for_token, }, - } + }; } function parse_repeat(stream: TokenStream): Statement | Error { - const repeat_token = expect(stream, TokenKind.Repeat) + const repeat_token = expect(stream, TokenKind.Repeat); + if (repeat_token instanceof Error) - return repeat_token + { + return repeat_token; + } + + const body = parse(stream, TokenKind.Until); - const body = parse(stream, TokenKind.Until) if (body instanceof Error) - return body + { + return body; + } + + const until_token = expect(stream, TokenKind.Until); - const until_token = expect(stream, TokenKind.Until) if (until_token instanceof Error) - return until_token + { + return until_token; + } + + const condition = parse_expression(stream); - const condition = parse_expression(stream) if (condition instanceof Error) - return condition + { + return condition; + } return { kind: StatementKind.Repeat, @@ -756,22 +955,31 @@ function parse_repeat(stream: TokenStream): Statement | Error condition: condition, token: repeat_token, }, - } + }; } function parse_do(stream: TokenStream): Statement | Error { - const do_token = expect(stream, TokenKind.Do) + const do_token = expect(stream, TokenKind.Do); + if (do_token instanceof Error) - return do_token + { + return do_token; + } + + const body = parse(stream, TokenKind.End); - const body = parse(stream, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } + + const end_token = expect(stream, TokenKind.End); - const end_token = expect(stream, TokenKind.End) if (end_token instanceof Error) - return end_token + { + return end_token; + } return { kind: StatementKind.Do, @@ -779,45 +987,65 @@ function parse_do(stream: TokenStream): Statement | Error body: body, token: do_token, }, - } + }; } -function parse_function_params(stream: TokenStream): Token[] | Error +function parse_function_params(stream: TokenStream): Array | Error { - const open_brace = expect(stream, TokenKind.OpenBrace) + const open_brace = expect(stream, TokenKind.OpenBrace); + if (open_brace instanceof Error) - return open_brace + { + return open_brace; + } + + const params: Array = []; - const params: Token[] = [] while (stream.peek().kind !== TokenKind.CloseBrace) { - const param = expect(stream, TokenKind.Identifier) + const param = expect(stream, TokenKind.Identifier); + if (param instanceof Error) - break + { + break; + } + + params.push(param); - params.push(param) if (!consume(stream, TokenKind.Comma)) - break + { + break; + } } - const close_brace = expect(stream, TokenKind.CloseBrace) + const close_brace = expect(stream, TokenKind.CloseBrace); + if (close_brace instanceof Error) - return close_brace + { + return close_brace; + } - return params + return params; } function parse_function_value(function_token: Token, stream: TokenStream): Value | Error { - const params = parse_function_params(stream) + const params = parse_function_params(stream); + if (params instanceof Error) - return params + { + return params; + } + + const body = parse(stream, TokenKind.End); - const body = parse(stream, TokenKind.End) if (body instanceof Error) - return body + { + return body; + } + + consume(stream, TokenKind.End); - consume(stream, TokenKind.End) return { kind: ValueKind.Function, token: function_token, @@ -825,18 +1053,24 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value parameters: params, body: body, }, - } + }; } function parse_local_function(table_name: Token, stream: TokenStream): Statement | Error { - const local_name = expect(stream, TokenKind.Identifier) + const local_name = expect(stream, TokenKind.Identifier); + if (local_name instanceof Error) - return local_name + { + return local_name; + } + + const function_value = parse_function_value(local_name, stream); - const function_value = parse_function_value(local_name, stream) if (function_value instanceof Error) - return function_value + { + return function_value; + } return { kind: StatementKind.Assignment, @@ -871,25 +1105,36 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement value: function_value, }], }, - } + }; } function parse_function(stream: TokenStream): Statement | Error { - const function_token = expect(stream, TokenKind.Function) + const function_token = expect(stream, TokenKind.Function); + if (function_token instanceof Error) - return function_token + { + return function_token; + } + + const name = expect(stream, TokenKind.Identifier); - const name = expect(stream, TokenKind.Identifier) if (name instanceof Error) - return name + { + return name; + } if (consume(stream, TokenKind.Dot)) - return parse_local_function(name, stream) + { + return parse_local_function(name, stream); + } + + const function_value = parse_function_value(name, stream); - const function_value = parse_function_value(name, stream) if (function_value instanceof Error) - return function_value + { + return function_value; + } return { kind: StatementKind.Assignment, @@ -911,12 +1156,14 @@ function parse_function(stream: TokenStream): Statement | Error value: function_value, }], }, - } + }; } -function parse_statement(stream: TokenStream, end_tokens: TokenKind[]): Statement | Error | undefined +function parse_statement(stream: TokenStream, end_tokens: Array): Statement | Error | undefined { - const token = stream.peek() + const token = stream.peek(); + + // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { case TokenKind.Identifier: @@ -926,56 +1173,73 @@ function parse_statement(stream: TokenStream, end_tokens: TokenKind[]): Statemen case TokenKind.BooleanLiteral: case TokenKind.SquiglyOpen: case TokenKind.Local: - return parse_assign_or_expression(stream) + return parse_assign_or_expression(stream); case TokenKind.Return: - return parse_return(stream) + return parse_return(stream); case TokenKind.Break: - return parse_break(stream) + return parse_break(stream); case TokenKind.If: - return parse_if(stream) + return parse_if(stream); case TokenKind.While: - return parse_while(stream) + return parse_while(stream); case TokenKind.For: - return parse_for(stream) + return parse_for(stream); case TokenKind.Repeat: - return parse_repeat(stream) + return parse_repeat(stream); case TokenKind.Do: - return parse_do(stream) + return parse_do(stream); case TokenKind.Function: - return parse_function(stream) + return parse_function(stream); case TokenKind.Semicolon: - stream.next() - return { kind: StatementKind.Empty } + stream.next(); + + return { kind: StatementKind.Empty }; + default: + { if (end_tokens.includes(token.kind)) - return undefined + { + return undefined; + } - const first_end_token = end_tokens[0] + const first_end_token = end_tokens[0]; if (first_end_token === undefined) - throw new Error() + { + throw new Error(); + } - return error(token, `Missing '${ token_kind_to_string(first_end_token) }', ` + - `got '${ token_kind_to_string(token.kind) }' instead`) + return error(token, `Missing '${token_kind_to_string(first_end_token)}', ` + + `got '${token_kind_to_string(token.kind)}' instead`); + } } } -export function parse(stream: TokenStream, ...end_tokens: TokenKind[]): Chunk | Error +export function parse(stream: TokenStream, ...end_tokens: Array): Chunk | Error { - const chunk: Chunk = { statements: [] } + const chunk: Chunk = { statements: [] }; + if (end_tokens.length === 0) - end_tokens.push(TokenKind.EOF) + { + end_tokens.push(TokenKind.EOF); + } - while (true) + for (;;) { - const statement = parse_statement(stream, end_tokens) + const statement = parse_statement(stream, end_tokens); + if (statement === undefined) - break + { + break; + } + if (statement instanceof Error) - return statement + { + return statement; + } - chunk.statements.push(statement) + chunk.statements.push(statement); } - return chunk + return chunk; } diff --git a/src/runtime-error.mts b/src/runtime-error.mts index 42c71a3..a27c8b4 100644 --- a/src/runtime-error.mts +++ b/src/runtime-error.mts @@ -31,7 +31,7 @@ class RuntimeError extends Error return this.message; } - return `${ this.line }:${ this.column }: ${ this.message }` + return `${this.line.toFixed(0)}:${this.column.toFixed(0)}: ${this.message}`; } } diff --git a/src/variable/definition/interface/variable-function.interface.mts b/src/variable/definition/interface/variable-function.interface.mts index 48cbef1..15d64e3 100644 --- a/src/variable/definition/interface/variable-function.interface.mts +++ b/src/variable/definition/interface/variable-function.interface.mts @@ -4,7 +4,7 @@ import type { VariableKind } from "../enum/variable-kind.enum.mjs"; interface VariableFunction extends BaseVariable { data_type: typeof VariableKind.Function; - function_id?: number; + function_id: number; } export type { VariableFunction }; diff --git a/src/variable/equals.mts b/src/variable/equals.mts index 510e5bb..12feeec 100644 --- a/src/variable/equals.mts +++ b/src/variable/equals.mts @@ -1,4 +1,4 @@ -import { Variable } from "./definition/type/variable.type.mjs"; +import type { Variable } from "./definition/type/variable.type.mjs"; import { VariableKind } from "./definition/enum/variable-kind.enum.mjs"; import { isVariableKind } from "./predicate/is-variable-kind.mjs"; import { isNullish } from "@vitruvius-labs/ts-predicate"; @@ -6,10 +6,14 @@ import { isNullish } from "@vitruvius-labs/ts-predicate"; function equals(a: Variable | undefined, b: Variable | undefined): boolean { if (a === b) + { return true; + } if (isNullish(a) || isNullish(b)) + { return false; + } switch (a.data_type) { @@ -28,18 +32,24 @@ function equals(a: Variable | undefined, b: Variable | undefined): boolean case VariableKind.Table: { if (!isVariableKind(b, VariableKind.Table)) - return false + { + return false; + } if (a.table.size !== b.table.size) - return false + { + return false; + } for (const key of a.table.keys()) { if (!equals(a.table.get(key), b.table.get(key))) - return false + { + return false; + } } - return true + return true; } } } From f9dc478306dc3230b52587e990b40777499d4ac3 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:02:55 +0000 Subject: [PATCH 17/71] fix: Moved ValueKind to its own file and renamed it ValueKindEnum. --- src/ast/definition/enum/value-kind.enum.mts | 12 +++++++ src/compiler.mts | 19 +++++------ src/optimizer.mts | 23 +++++++------- src/parser.mts | 35 +++++++++++---------- 4 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 src/ast/definition/enum/value-kind.enum.mts diff --git a/src/ast/definition/enum/value-kind.enum.mts b/src/ast/definition/enum/value-kind.enum.mts new file mode 100644 index 0000000..48db867 --- /dev/null +++ b/src/ast/definition/enum/value-kind.enum.mts @@ -0,0 +1,12 @@ +const enum ValueKindEnum +{ + NilLiteral = 0, + NumberLiteral = 1, + BooleanLiteral = 2, + StringLiteral = 3, + TableLiteral = 4, + FunctionLike = 5, + Variable = 6, +} + +export { ValueKindEnum }; diff --git a/src/compiler.mts b/src/compiler.mts index 8a5b960..b5582a2 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -4,11 +4,12 @@ import type { Op, Program } from "./opcode.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; -import { ExpressionKind, StatementKind, ValueKind } from "./ast.mjs"; +import { ExpressionKind, StatementKind } from "./ast.mjs"; import { OpCode } from "./opcode.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; +import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -42,16 +43,16 @@ function compile_value(value: Value | undefined, functions: Array>): A switch (value.kind) { - case ValueKind.NilLiteral: + case ValueKindEnum.NilLiteral: return [{ code: OpCode.Push, arg: nil, debug: debug }]; - case ValueKind.BooleanLiteral: + case ValueKindEnum.BooleanLiteral: return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }]; - case ValueKind.NumberLiteral: + case ValueKindEnum.NumberLiteral: return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }]; - case ValueKind.StringLiteral: + case ValueKindEnum.StringLiteral: return [{ code: OpCode.Push, arg: make_string(value.string ?? ""), debug: debug }]; - case ValueKind.Function: + case ValueKindEnum.FunctionLike: { return [{ code: OpCode.Push, @@ -68,7 +69,7 @@ function compile_value(value: Value | undefined, functions: Array>): A }]; } - case ValueKind.TableLiteral: + case ValueKindEnum.TableLiteral: { const output: Array = []; @@ -85,7 +86,7 @@ function compile_value(value: Value | undefined, functions: Array>): A return output; } - case ValueKind.Variable: + case ValueKindEnum.Variable: { return [{ code: OpCode.Load, @@ -282,7 +283,7 @@ function compile_assignment(assignment: Assignment | undefined, functions: Array { case ExpressionKind.Value: { - if (lhs.value?.kind !== ValueKind.Variable) + if (lhs.value?.kind !== ValueKindEnum.Variable) { throw new Error(); } diff --git a/src/optimizer.mts b/src/optimizer.mts index b9fcc8e..41a2190 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,10 +1,11 @@ -import { type Assignment, type Chunk, type Expression, ExpressionKind, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, ValueKind, type While } from "./ast.mjs"; +import { type Assignment, type Chunk, type Expression, ExpressionKind, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, type While } from "./ast.mjs"; +import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; const CONSTANT_VALUES = [ - ValueKind.NilLiteral, - ValueKind.NumberLiteral, - ValueKind.BooleanLiteral, - ValueKind.StringLiteral, + ValueKindEnum.NilLiteral, + ValueKindEnum.NumberLiteral, + ValueKindEnum.BooleanLiteral, + ValueKindEnum.StringLiteral, ]; function compute_arithmetic_operation( @@ -22,7 +23,7 @@ function compute_arithmetic_operation( } return { - kind: ValueKind.NumberLiteral, + kind: ValueKindEnum.NumberLiteral, number: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, }; @@ -43,7 +44,7 @@ function compute_comparison_operation( } return { - kind: ValueKind.BooleanLiteral, + kind: ValueKindEnum.BooleanLiteral, boolean: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, }; @@ -63,7 +64,7 @@ function compute_logical_operation( return undefined; } - const lhs_falsy: boolean = lhs.kind === ValueKind.NilLiteral || lhs.kind === ValueKind.BooleanLiteral && !(lhs.boolean ?? true); + const lhs_falsy: boolean = lhs.kind === ValueKindEnum.NilLiteral || lhs.kind === ValueKindEnum.BooleanLiteral && !(lhs.boolean ?? true); if (operation === ExpressionKind.And) { @@ -101,7 +102,7 @@ function compute_constant_expression( return value; } - if (value.kind === ValueKind.Variable && constants.has(value.identifier ?? "")) + if (value.kind === ValueKindEnum.Variable && constants.has(value.identifier ?? "")) { return constants.get(value.identifier ?? ""); } @@ -300,7 +301,7 @@ function compute_constant_expression( } return { - kind: ValueKind.BooleanLiteral, + kind: ValueKindEnum.BooleanLiteral, boolean: !(lhs.boolean ?? false), token: expression.token, }; @@ -316,7 +317,7 @@ function compute_constant_expression( } return { - kind: ValueKind.NumberLiteral, + kind: ValueKindEnum.NumberLiteral, number: -(lhs.number ?? 0), token: expression.token, }; diff --git a/src/parser.mts b/src/parser.mts index 67963f6..4b8d5a6 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,4 +1,5 @@ -import { type Chunk, type ElseIfBlock, type Expression, ExpressionKind, type Statement, StatementKind, type Value, ValueKind } from "./ast.mjs"; +import { type Chunk, type ElseIfBlock, type Expression, ExpressionKind, type Statement, StatementKind, type Value } from "./ast.mjs"; +import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import { type Token, TokenKind, type TokenStream, token_kind_to_string } from "./lexer.mjs"; const UNARY = [ @@ -85,9 +86,9 @@ function parse_table_key(stream: TokenStream): Expression | Error return value; } - if (value.kind === ValueKind.Variable) + if (value.kind === ValueKindEnum.Variable) { - value.kind = ValueKind.StringLiteral; + value.kind = ValueKindEnum.StringLiteral; value.string = value.identifier; value.identifier = undefined; } @@ -143,7 +144,7 @@ function parse_table(stream: TokenStream): Value | Error kind: ExpressionKind.Value, token: key_token, value: { - kind: ValueKind.NumberLiteral, + kind: ValueKindEnum.NumberLiteral, token: key_token, number: current_numeric_key, }, @@ -167,7 +168,7 @@ function parse_table(stream: TokenStream): Value | Error } return { - kind: ValueKind.TableLiteral, + kind: ValueKindEnum.TableLiteral, token: squigly_open, table: elements, }; @@ -181,15 +182,15 @@ function parse_value(stream: TokenStream): Value | Error switch (token.kind) { case TokenKind.NumberLiteral: - return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; + return { kind: ValueKindEnum.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; case TokenKind.BooleanLiteral: - return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; + return { kind: ValueKindEnum.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; case TokenKind.StringLiteral: - return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data }; + return { kind: ValueKindEnum.StringLiteral, token: stream.next(), string: token.data }; case TokenKind.NilLiteral: - return { kind: ValueKind.NilLiteral, token: stream.next() }; + return { kind: ValueKindEnum.NilLiteral, token: stream.next() }; case TokenKind.Identifier: - return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data }; + return { kind: ValueKindEnum.Variable, token: stream.next(), identifier: token.data }; case TokenKind.SquiglyOpen: return parse_table(stream); @@ -360,7 +361,7 @@ function parse_dot(table: Expression, stream: TokenStream): Expression | Error kind: ExpressionKind.Value, token: index, value: { - kind: ValueKind.StringLiteral, + kind: ValueKindEnum.StringLiteral, token: index, string: index.data, }, @@ -506,7 +507,7 @@ function parse_local_statement(local: Token, values: Array): Stateme { const value = expression.value; - if (value === undefined || value.kind !== ValueKind.Variable) + if (value === undefined || value.kind !== ValueKindEnum.Variable) { return error(expression.token, "Invalid local name"); } @@ -612,7 +613,7 @@ function parse_return(stream: TokenStream): Statement | Error kind: ExpressionKind.Value, token: ret, value: { - kind: ValueKind.NilLiteral, + kind: ValueKindEnum.NilLiteral, token: ret, }, }); @@ -1047,7 +1048,7 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value consume(stream, TokenKind.End); return { - kind: ValueKind.Function, + kind: ValueKindEnum.FunctionLike, token: function_token, function: { parameters: params, @@ -1084,7 +1085,7 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement kind: ExpressionKind.Value, token: table_name, value: { - kind: ValueKind.Variable, + kind: ValueKindEnum.Variable, token: table_name, identifier: table_name.data, }, @@ -1093,7 +1094,7 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement kind: ExpressionKind.Value, token: local_name, value: { - kind: ValueKind.StringLiteral, + kind: ValueKindEnum.StringLiteral, token: local_name, string: local_name.data, }, @@ -1145,7 +1146,7 @@ function parse_function(stream: TokenStream): Statement | Error kind: ExpressionKind.Value, token: name, value: { - kind: ValueKind.Variable, + kind: ValueKindEnum.Variable, token: name, identifier: name.data, }, From 6132300554fda7a1a802629bf0e33b0c87d7e6bf Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:04:44 +0000 Subject: [PATCH 18/71] fix: Moved ExpressionKind to its own file and renamed it ExpressionKindEnum. --- .../definition/enum/expression-kind.enum.mts | 37 +++++++++++++++++++ src/compiler.mts | 3 +- src/optimizer.mts | 4 +- src/parser.mts | 3 +- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/ast/definition/enum/expression-kind.enum.mts diff --git a/src/ast/definition/enum/expression-kind.enum.mts b/src/ast/definition/enum/expression-kind.enum.mts new file mode 100644 index 0000000..9159892 --- /dev/null +++ b/src/ast/definition/enum/expression-kind.enum.mts @@ -0,0 +1,37 @@ +enum ExpressionKind +{ + Value = 0, + Call = 1, + Index = 2, + + Addition = 3, + Subtract = 4, + Multiplication = 5, + Division = 6, + FloorDivision = 7, + Modulo = 8, + Exponent = 9, + Concat = 10, + + BitAnd = 11, + BitOr = 12, + BitXOr = 13, + BitNot = 14, + BitShiftLeft = 15, + BitShiftRight = 16, + + Equals = 17, + NotEquals = 18, + LessThan = 19, + LessThanEquals = 20, + GreaterThan = 21, + GreaterThanEquals = 22, + And = 23, + Or = 24, + + Not = 25, + Negate = 26, + Length = 27, +} + +export { ExpressionKind }; diff --git a/src/compiler.mts b/src/compiler.mts index b5582a2..7e1f661 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -4,12 +4,13 @@ import type { Op, Program } from "./opcode.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; -import { ExpressionKind, StatementKind } from "./ast.mjs"; +import { StatementKind } from "./ast.mjs"; import { OpCode } from "./opcode.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { diff --git a/src/optimizer.mts b/src/optimizer.mts index 41a2190..9ef471e 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,4 +1,5 @@ -import { type Assignment, type Chunk, type Expression, ExpressionKind, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, type While } from "./ast.mjs"; +import { type Assignment, type Chunk, type Expression, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, type While } from "./ast.mjs"; +import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; const CONSTANT_VALUES = [ @@ -85,7 +86,6 @@ function compute_constant_expression( return undefined; } - // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (expression.kind) { case ExpressionKind.Value: diff --git a/src/parser.mts b/src/parser.mts index 4b8d5a6..ced1175 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,4 +1,5 @@ -import { type Chunk, type ElseIfBlock, type Expression, ExpressionKind, type Statement, StatementKind, type Value } from "./ast.mjs"; +import { type Chunk, type ElseIfBlock, type Expression, type Statement, StatementKind, type Value } from "./ast.mjs"; +import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import { type Token, TokenKind, type TokenStream, token_kind_to_string } from "./lexer.mjs"; From 220a7fa87b6a1e137ae23fa04fa1220f31e6c3b8 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:07:24 +0000 Subject: [PATCH 19/71] fix: Moved StatementKind to its own file and renamed it StatementKindEnum. --- src/ast.mts | 67 +------------------ .../definition/enum/statement-kind.enum.mts | 18 +++++ src/compiler.mts | 26 +++---- src/optimizer.mts | 26 +++---- src/parser.mts | 31 ++++----- 5 files changed, 62 insertions(+), 106 deletions(-) create mode 100644 src/ast/definition/enum/statement-kind.enum.mts diff --git a/src/ast.mts b/src/ast.mts index 843684a..a5f175e 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,16 +1,6 @@ +import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { Token } from "./lexer.mjs"; -export enum ValueKind -{ - NilLiteral, - NumberLiteral, - BooleanLiteral, - StringLiteral, - TableLiteral, - Function, - Variable, -} - export interface LuaFunction { parameters: Array; @@ -30,42 +20,6 @@ export interface Value identifier?: string | undefined; } -export enum ExpressionKind -{ - Value, - Call, - Index, - - Addition, - Subtract, - Multiplication, - Division, - FloorDivision, - Modulo, - Exponent, - Concat, - - BitAnd, - BitOr, - BitXOr, - BitNot, - BitShiftLeft, - BitShiftRight, - - Equals, - NotEquals, - LessThan, - LessThanEquals, - GreaterThan, - GreaterThanEquals, - And, - Or, - - Not, - Negate, - Length, -} - export interface Expression { kind: ExpressionKind; @@ -152,26 +106,9 @@ export interface Return token: Token; } -export enum StatementKind -{ - Invalid, - Empty, - Expression, - Assignment, - Local, - If, - While, - For, - NumericFor, - Repeat, - Do, - Return, - Break, -} - export interface Statement { - kind: StatementKind; + kind: StatementKindEnum; expression?: Expression | undefined; assignment?: Assignment | undefined; local?: Local | undefined; diff --git a/src/ast/definition/enum/statement-kind.enum.mts b/src/ast/definition/enum/statement-kind.enum.mts new file mode 100644 index 0000000..2aa3f95 --- /dev/null +++ b/src/ast/definition/enum/statement-kind.enum.mts @@ -0,0 +1,18 @@ +const enum StatementKindEnum +{ + Invalid = 0, + Empty = 1, + Expression = 2, + Assignment = 3, + Local = 4, + If = 5, + While = 6, + For = 7, + NumericFor = 8, + Repeat = 9, + Do = 10, + Return = 11, + Break = 12, +} + +export { StatementKindEnum }; diff --git a/src/compiler.mts b/src/compiler.mts index 7e1f661..07bba8b 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -4,13 +4,13 @@ import type { Op, Program } from "./opcode.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; -import { StatementKind } from "./ast.mjs"; import { OpCode } from "./opcode.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; +import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -721,9 +721,9 @@ function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult switch (statement.kind) { - case StatementKind.Empty: + case StatementKindEnum.Empty: break; - case StatementKind.Expression: + case StatementKindEnum.Expression: ops.push(...compile_expression(statement.expression, functions)); if (statement.expression === undefined) @@ -741,34 +741,34 @@ function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult } break; - case StatementKind.Assignment: + case StatementKindEnum.Assignment: ops.push(...compile_assignment(statement.assignment, functions)); break; - case StatementKind.Local: + case StatementKindEnum.Local: ops.push(...compile_local(statement.local)); break; - case StatementKind.If: + case StatementKindEnum.If: ops.push(...compile_if(statement.if, functions)); break; - case StatementKind.While: + case StatementKindEnum.While: ops.push(...compile_while(statement.while, functions)); break; - case StatementKind.For: + case StatementKindEnum.For: ops.push(...compile_for(statement.for, functions)); break; - case StatementKind.NumericFor: + case StatementKindEnum.NumericFor: ops.push(...compile_numeric_for(statement.numeric_for, functions)); break; - case StatementKind.Repeat: + case StatementKindEnum.Repeat: ops.push(...compile_repeat(statement.repeat, functions)); break; - case StatementKind.Do: + case StatementKindEnum.Do: ops.push(...compile_do(statement.do, functions)); break; - case StatementKind.Return: + case StatementKindEnum.Return: ops.push(...compile_return(statement.return, functions)); break; - case StatementKind.Break: + case StatementKindEnum.Break: ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }); break; } diff --git a/src/optimizer.mts b/src/optimizer.mts index 9ef471e..3ed3a04 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,5 +1,6 @@ -import { type Assignment, type Chunk, type Expression, type For, type IfBlock, type NumericFor, type Repeat, type Statement, StatementKind, type Value, type While } from "./ast.mjs"; +import type { Assignment, Chunk, Expression, For, IfBlock, NumericFor, Repeat, Statement, Value, While } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; +import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; const CONSTANT_VALUES = [ @@ -556,38 +557,37 @@ export function optimize_chunk(chunk: Chunk, parent_constants?: Map): Stateme } return { - kind: StatementKind.Local, + kind: StatementKindEnum.Local, local: { token: local, names: names, @@ -549,7 +550,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error if (local instanceof Error) { // @TODO: Investigate as lhs[0] seems to always be undefined - return { kind: StatementKind.Expression, expression: lhs[0] }; + return { kind: StatementKindEnum.Expression, expression: lhs[0] }; } return parse_local_statement(local, lhs); @@ -570,7 +571,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Assignment, + kind: StatementKindEnum.Assignment, assignment: { local: !(local instanceof Error), lhs: lhs.reverse(), @@ -621,7 +622,7 @@ function parse_return(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Return, + kind: StatementKindEnum.Return, return: { values: values, token: ret, @@ -639,7 +640,7 @@ function parse_break(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Break, + kind: StatementKindEnum.Break, }; } @@ -726,7 +727,7 @@ function parse_if(stream: TokenStream): Statement | Error } return { - kind: StatementKind.If, + kind: StatementKindEnum.If, if: { condition: condition, body: body, @@ -770,7 +771,7 @@ function parse_while(stream: TokenStream): Statement | Error consume(stream, TokenKind.End); return { - kind: StatementKind.While, + kind: StatementKindEnum.While, while: { condition: condition, body: body, @@ -833,7 +834,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error consume(stream, TokenKind.End); return { - kind: StatementKind.NumericFor, + kind: StatementKindEnum.NumericFor, numeric_for: { index: index, start: start, @@ -910,7 +911,7 @@ function parse_for(stream: TokenStream): Statement | Error consume(stream, TokenKind.End); return { - kind: StatementKind.For, + kind: StatementKindEnum.For, for: { items: items, iterator: iterator, @@ -951,7 +952,7 @@ function parse_repeat(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Repeat, + kind: StatementKindEnum.Repeat, repeat: { body: body, condition: condition, @@ -984,7 +985,7 @@ function parse_do(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Do, + kind: StatementKindEnum.Do, do: { body: body, token: do_token, @@ -1075,7 +1076,7 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement } return { - kind: StatementKind.Assignment, + kind: StatementKindEnum.Assignment, assignment: { token: table_name, local: false, @@ -1139,7 +1140,7 @@ function parse_function(stream: TokenStream): Statement | Error } return { - kind: StatementKind.Assignment, + kind: StatementKindEnum.Assignment, assignment: { token: name, local: false, @@ -1195,7 +1196,7 @@ function parse_statement(stream: TokenStream, end_tokens: Array): Sta case TokenKind.Semicolon: stream.next(); - return { kind: StatementKind.Empty }; + return { kind: StatementKindEnum.Empty }; default: { From 0c88c3dd040d52e63fa0b9aa77d242344f01afcf Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:10:36 +0000 Subject: [PATCH 20/71] fix: Moved OpCode to its own file and renamed it OpCodeEnum. --- src/compiler.mts | 196 ++++++++++---------- src/engine.mts | 101 +++++----- src/opcode.mts | 167 ++++++----------- src/opcode/definition/enum/op-code.enum.mts | 63 +++++++ 4 files changed, 265 insertions(+), 262 deletions(-) create mode 100644 src/opcode/definition/enum/op-code.enum.mts diff --git a/src/compiler.mts b/src/compiler.mts index 07bba8b..ceb71af 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -4,29 +4,29 @@ import type { Op, Program } from "./opcode.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; -import { OpCode } from "./opcode.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; +import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { const ops: Array = []; - ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); + ops.push({ code: OpCodeEnum.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); for (const parameter of parameters.reverse()) { - ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }); - ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }); + ops.push({ code: OpCodeEnum.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }); + ops.push({ code: OpCodeEnum.Store, arg: make_string(parameter.data), debug: parameter.debug }); } ops.push(...compile_block(chunk, functions)); - ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }); - ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }); + ops.push({ code: OpCodeEnum.Push, arg: nil, debug: token.debug }); + ops.push({ code: OpCodeEnum.Return, arg: make_number(0), debug: token.debug }); functions.push(ops); @@ -45,18 +45,18 @@ function compile_value(value: Value | undefined, functions: Array>): A switch (value.kind) { case ValueKindEnum.NilLiteral: - return [{ code: OpCode.Push, arg: nil, debug: debug }]; + return [{ code: OpCodeEnum.Push, arg: nil, debug: debug }]; case ValueKindEnum.BooleanLiteral: - return [{ code: OpCode.Push, arg: make_boolean(value.boolean ?? false), debug: debug }]; + return [{ code: OpCodeEnum.Push, arg: make_boolean(value.boolean ?? false), debug: debug }]; case ValueKindEnum.NumberLiteral: - return [{ code: OpCode.Push, arg: make_number(value.number ?? 0), debug: debug }]; + return [{ code: OpCodeEnum.Push, arg: make_number(value.number ?? 0), debug: debug }]; case ValueKindEnum.StringLiteral: - return [{ code: OpCode.Push, arg: make_string(value.string ?? ""), debug: debug }]; + return [{ code: OpCodeEnum.Push, arg: make_string(value.string ?? ""), debug: debug }]; case ValueKindEnum.FunctionLike: { return [{ - code: OpCode.Push, + code: OpCodeEnum.Push, arg: { data_type: VariableKind.Function, function_id: compile_function( @@ -74,7 +74,7 @@ function compile_value(value: Value | undefined, functions: Array>): A { const output: Array = []; - output.push({ code: OpCode.NewTable, debug: debug }); + output.push({ code: OpCodeEnum.NewTable, debug: debug }); for (const [key, expression] of [...value.table?.entries() ?? []].reverse()) { @@ -82,7 +82,7 @@ function compile_value(value: Value | undefined, functions: Array>): A output.push(...compile_expression(key, functions)); } - output.push({ code: OpCode.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }); + output.push({ code: OpCodeEnum.StoreIndex, arg: make_number(value.table?.size ?? 0), debug: debug }); return output; } @@ -90,7 +90,7 @@ function compile_value(value: Value | undefined, functions: Array>): A case ValueKindEnum.Variable: { return [{ - code: OpCode.Load, + code: OpCodeEnum.Load, arg: { data_type: VariableKind.String, string: value.identifier ?? "" }, debug: debug, }]; @@ -100,7 +100,7 @@ function compile_value(value: Value | undefined, functions: Array>): A function compile_operation( expression: Expression, - operation: OpCode, + operation: OpCodeEnum, functions: Array> ): Array { @@ -140,8 +140,8 @@ function compile_call( } ops.push(...compile_expression(func, functions)); - ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }); - ops.push({ code: OpCode.Call, debug: debug }); + ops.push({ code: OpCodeEnum.Push, arg: make_number(args.length), debug: debug }); + ops.push({ code: OpCodeEnum.Call, debug: debug }); return ops; } @@ -161,14 +161,14 @@ function compile_index( ops.push(...compile_expression(index, functions)); ops.push(...compile_expression(target, functions)); - ops.push({ code: OpCode.LoadIndex, debug: target.token.debug }); + ops.push({ code: OpCodeEnum.LoadIndex, debug: target.token.debug }); return ops; } function compile_unary_operation( expression: Expression | undefined, - operation: OpCode, + operation: OpCodeEnum, functions: Array> ): Array { @@ -202,58 +202,58 @@ function compile_expression(expression: Expression | undefined, functions: Array return compile_index(expression.expression, expression.index, functions); case ExpressionKind.Addition: - return compile_operation(expression, OpCode.Add, functions); + return compile_operation(expression, OpCodeEnum.Add, functions); case ExpressionKind.Subtract: - return compile_operation(expression, OpCode.Subtract, functions); + return compile_operation(expression, OpCodeEnum.Subtract, functions); case ExpressionKind.Multiplication: - return compile_operation(expression, OpCode.Multiply, functions); + return compile_operation(expression, OpCodeEnum.Multiply, functions); case ExpressionKind.Division: - return compile_operation(expression, OpCode.Divide, functions); + return compile_operation(expression, OpCodeEnum.Divide, functions); case ExpressionKind.FloorDivision: - return compile_operation(expression, OpCode.FloorDivide, functions); + return compile_operation(expression, OpCodeEnum.FloorDivide, functions); case ExpressionKind.Modulo: - return compile_operation(expression, OpCode.Modulo, functions); + return compile_operation(expression, OpCodeEnum.Modulo, functions); case ExpressionKind.Exponent: - return compile_operation(expression, OpCode.Exponent, functions); + return compile_operation(expression, OpCodeEnum.Exponent, functions); case ExpressionKind.Concat: - return compile_operation(expression, OpCode.Concat, functions); + return compile_operation(expression, OpCodeEnum.Concat, functions); case ExpressionKind.BitAnd: - return compile_operation(expression, OpCode.BitAnd, functions); + return compile_operation(expression, OpCodeEnum.BitAnd, functions); case ExpressionKind.BitOr: - return compile_operation(expression, OpCode.BitOr, functions); + return compile_operation(expression, OpCodeEnum.BitOr, functions); case ExpressionKind.BitXOr: - return compile_operation(expression, OpCode.BitXOr, functions); + return compile_operation(expression, OpCodeEnum.BitXOr, functions); case ExpressionKind.BitShiftLeft: - return compile_operation(expression, OpCode.BitShiftLeft, functions); + return compile_operation(expression, OpCodeEnum.BitShiftLeft, functions); case ExpressionKind.BitShiftRight: - return compile_operation(expression, OpCode.BitShiftRight, functions); + return compile_operation(expression, OpCodeEnum.BitShiftRight, functions); case ExpressionKind.Equals: - return compile_operation(expression, OpCode.Equals, functions); + return compile_operation(expression, OpCodeEnum.Equals, functions); case ExpressionKind.NotEquals: - return compile_operation(expression, OpCode.NotEquals, functions); + return compile_operation(expression, OpCodeEnum.NotEquals, functions); case ExpressionKind.LessThan: - return compile_operation(expression, OpCode.LessThan, functions); + return compile_operation(expression, OpCodeEnum.LessThan, functions); case ExpressionKind.LessThanEquals: - return compile_operation(expression, OpCode.LessThanEquals, functions); + return compile_operation(expression, OpCodeEnum.LessThanEquals, functions); case ExpressionKind.GreaterThan: - return compile_operation(expression, OpCode.GreaterThan, functions); + return compile_operation(expression, OpCodeEnum.GreaterThan, functions); case ExpressionKind.GreaterThanEquals: - return compile_operation(expression, OpCode.GreaterThanEquals, functions); + return compile_operation(expression, OpCodeEnum.GreaterThanEquals, functions); case ExpressionKind.And: - return compile_operation(expression, OpCode.And, functions); + return compile_operation(expression, OpCodeEnum.And, functions); case ExpressionKind.Or: - return compile_operation(expression, OpCode.Or, functions); + return compile_operation(expression, OpCodeEnum.Or, functions); case ExpressionKind.Not: - return compile_unary_operation(expression, OpCode.Not, functions); + return compile_unary_operation(expression, OpCodeEnum.Not, functions); case ExpressionKind.Negate: - return compile_unary_operation(expression, OpCode.Negate, functions); + return compile_unary_operation(expression, OpCodeEnum.Negate, functions); case ExpressionKind.Length: - return compile_unary_operation(expression, OpCode.Length, functions); + return compile_unary_operation(expression, OpCodeEnum.Length, functions); case ExpressionKind.BitNot: - return compile_unary_operation(expression, OpCode.BitNot, functions); + return compile_unary_operation(expression, OpCodeEnum.BitNot, functions); } } @@ -267,14 +267,14 @@ function compile_assignment(assignment: Assignment | undefined, functions: Array const ops: Array = []; const debug = assignment.token.debug; - ops.push({ code: OpCode.StartStackChange, debug: debug }); + ops.push({ code: OpCodeEnum.StartStackChange, debug: debug }); for (const rhs of assignment.rhs) { ops.push(...compile_expression(rhs, functions)); } - ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }); + ops.push({ code: OpCodeEnum.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }); for (const lhs of assignment.lhs) { @@ -293,10 +293,10 @@ function compile_assignment(assignment: Assignment | undefined, functions: Array if (assignment.local) { - ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }); + ops.push({ code: OpCodeEnum.MakeLocal, arg: identifier, debug: debug }); } - ops.push({ code: OpCode.Store, arg: identifier, debug: debug }); + ops.push({ code: OpCodeEnum.Store, arg: identifier, debug: debug }); break; } @@ -305,10 +305,10 @@ function compile_assignment(assignment: Assignment | undefined, functions: Array // FIXME: Throw error here if `assignment.local` is true. I think? ops.push(...compile_expression(lhs.expression, functions)); - ops.push({ code: OpCode.Swap, debug: debug }); + ops.push({ code: OpCodeEnum.Swap, debug: debug }); ops.push(...compile_expression(lhs.index, functions)); - ops.push({ code: OpCode.StoreIndex, debug: debug }); - ops.push({ code: OpCode.Pop, debug: debug }); + ops.push({ code: OpCodeEnum.StoreIndex, debug: debug }); + ops.push({ code: OpCodeEnum.Pop, debug: debug }); break; } @@ -331,7 +331,7 @@ function compile_local(local: Local | undefined): Array (name): Op => { return { - code: OpCode.MakeLocal, + code: OpCodeEnum.MakeLocal, arg: { data_type: VariableKind.String, string: name.data, @@ -381,7 +381,7 @@ function compile_inverted_conditional_jump(condition: Expression | undefined, ju default: { ops.push(...compile_expression(condition, functions)); - ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }); + ops.push({ code: OpCodeEnum.JumpIf, arg: make_number(jump_by), debug: debug }); break; } } @@ -428,7 +428,7 @@ function compile_conditional_jump(condition: Expression | undefined, jump_by: nu default: { ops.push(...compile_expression(condition, functions)); - ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }); + ops.push({ code: OpCodeEnum.JumpIfNot, arg: make_number(jump_by), debug: debug }); break; } } @@ -468,7 +468,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): 0 ) + else_chunk.length; - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: token.debug }); + ops.push({ code: OpCodeEnum.Jump, arg: make_number(offset), debug: token.debug }); if_else_chunks.push(ops); } @@ -476,7 +476,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): const ops: Array = []; const body = compile_block(if_block.body, functions); - ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)); ops.push(...body); @@ -488,7 +488,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): 0 ) + else_chunk.length; - ops.push({ code: OpCode.Jump, arg: make_number(offset), debug: debug }); + ops.push({ code: OpCodeEnum.Jump, arg: make_number(offset), debug: debug }); for (const if_else_chunk of if_else_chunks) { @@ -496,7 +496,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): } ops.push(...else_chunk); - ops.push({ code: OpCode.EndBlock, debug: debug }); + ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); return ops; } @@ -505,11 +505,11 @@ function replace_breaks(code: Array, offset_from_end: number): void { for (const [i, op] of code.entries()) { - if (op.code === OpCode.Break) + if (op.code === OpCodeEnum.Break) { const offset = code.length - i - 1 + offset_from_end; - op.code = OpCode.Jump; + op.code = OpCodeEnum.Jump; op.arg = make_number(offset); } } @@ -528,11 +528,11 @@ function compile_while(while_block: While | undefined, functions: Array>): A const debug = for_block.token.debug; - ops.push({ code: OpCode.StartBlock, debug: debug }); - ops.push({ code: OpCode.StartStackChange, debug: debug }); + ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); + ops.push({ code: OpCodeEnum.StartStackChange, debug: debug }); ops.push(...compile_expression(for_block.iterator, functions)); - ops.push({ code: OpCode.EndStackChange, arg: make_number(3), debug: debug }); + ops.push({ code: OpCodeEnum.EndStackChange, arg: make_number(3), debug: debug }); const after_creating_itorator = ops.length; - ops.push({ code: OpCode.StartStackChange, debug: debug }); - ops.push({ code: OpCode.IterNext, debug: debug }); - ops.push({ code: OpCode.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }); + ops.push({ code: OpCodeEnum.StartStackChange, debug: debug }); + ops.push({ code: OpCodeEnum.IterNext, debug: debug }); + ops.push({ code: OpCodeEnum.IterJumpIfDone, arg: make_number(body.length + for_block.items.length + 3), debug: debug }); - ops.push({ code: OpCode.EndStackChange, arg: make_number(for_block.items.length), debug: debug }); + ops.push({ code: OpCodeEnum.EndStackChange, arg: make_number(for_block.items.length), debug: debug }); for (const [i, item] of [...for_block.items].reverse().entries()) { if (i === for_block.items.length - 1) { - ops.push({ code: OpCode.IterUpdateState, debug: debug }); + ops.push({ code: OpCodeEnum.IterUpdateState, debug: debug }); } - ops.push({ code: OpCode.Store, arg: make_string(item.data), debug: item.debug }); + ops.push({ code: OpCodeEnum.Store, arg: make_string(item.data), debug: item.debug }); } ops.push(...body); - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); + ops.push({ code: OpCodeEnum.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); - ops.push({ code: OpCode.EndStackChange, arg: make_number(0), debug: debug }); - ops.push({ code: OpCode.Pop, arg: make_number(3), debug: debug }); - ops.push({ code: OpCode.EndBlock, debug: debug }); + ops.push({ code: OpCodeEnum.EndStackChange, arg: make_number(0), debug: debug }); + ops.push({ code: OpCodeEnum.Pop, arg: make_number(3), debug: debug }); + ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); return ops; } @@ -588,7 +588,7 @@ function compile_step(step: Expression | undefined, functions: Array>) { if (step === undefined) { - return [{ code: OpCode.Push, arg: make_number(1), debug: { line: 0, column: 0 } }]; + return [{ code: OpCodeEnum.Push, arg: make_number(1), debug: { line: 0, column: 0 } }]; } return compile_expression(step, functions); @@ -609,25 +609,25 @@ function compile_numeric_for(numeric_for_block: NumericFor | undefined, function replace_breaks(body, step.length + 4); - ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); ops.push(...compile_expression(numeric_for_block.start, functions)); const after_creating_itorator = ops.length; - ops.push({ code: OpCode.Dup, debug: debug }); + ops.push({ code: OpCodeEnum.Dup, debug: debug }); ops.push(...compile_expression(numeric_for_block.end, functions)); - ops.push({ code: OpCode.NotEquals, debug: debug }); - ops.push({ code: OpCode.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }); + ops.push({ code: OpCodeEnum.NotEquals, debug: debug }); + ops.push({ code: OpCodeEnum.JumpIfNot, arg: make_number(body.length + step.length + 4), debug: debug }); - ops.push({ code: OpCode.Store, arg: make_string(index), debug: debug }); + ops.push({ code: OpCodeEnum.Store, arg: make_string(index), debug: debug }); ops.push(...body); - ops.push({ code: OpCode.Load, arg: make_string(index), debug: debug }); + ops.push({ code: OpCodeEnum.Load, arg: make_string(index), debug: debug }); ops.push(...step); - ops.push({ code: OpCode.Add, debug: debug }); - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); + ops.push({ code: OpCodeEnum.Add, debug: debug }); + ops.push({ code: OpCodeEnum.Jump, arg: make_number(-ops.length + after_creating_itorator - 1), debug: debug }); - ops.push({ code: OpCode.Pop, debug: debug }); - ops.push({ code: OpCode.EndBlock, debug: debug }); + ops.push({ code: OpCodeEnum.Pop, debug: debug }); + ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); return ops; } @@ -642,13 +642,13 @@ function compile_repeat(repeat: Repeat | undefined, functions: Array>) const ops: Array = []; const debug = repeat.token.debug; - ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); ops.push(...compile_block(repeat.body, functions)); ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)); - ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }); + ops.push({ code: OpCodeEnum.Jump, arg: make_number(-ops.length), debug: debug }); - ops.push({ code: OpCode.EndBlock, debug: debug }); + ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); return ops; } @@ -663,9 +663,9 @@ function compile_do(do_block: Do | undefined, functions: Array>): Arra const ops: Array = []; const debug = do_block.token.debug; - ops.push({ code: OpCode.StartBlock, debug: debug }); + ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); ops.push(...compile_block(do_block.body, functions)); - ops.push({ code: OpCode.EndBlock, debug: debug }); + ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); return ops; } @@ -687,7 +687,7 @@ function compile_return(return_block: Return | undefined, functions: Array>): Array if (has_last_expression) { - code.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }); + code.push({ code: OpCodeEnum.Pop, debug: { line: 0, column: 0 } }); } return code; @@ -737,7 +737,7 @@ function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult } else { - ops.push({ code: OpCode.Pop, debug: statement.expression.token.debug }); + ops.push({ code: OpCodeEnum.Pop, debug: statement.expression.token.debug }); } break; @@ -769,7 +769,7 @@ function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult ops.push(...compile_return(statement.return, functions)); break; case StatementKindEnum.Break: - ops.push({ code: OpCode.Break, debug: { line: 0, column: 0 } }); + ops.push({ code: OpCodeEnum.Break, debug: { line: 0, column: 0 } }); break; } } @@ -815,14 +815,14 @@ export function compile(chunk: Chunk, extend?: Array): Program if (extend?.length ?? 0 > 0) { - ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }); + ops.push({ code: OpCodeEnum.Pop, debug: { line: 0, column: 0 } }); } ops.push(...code); if (!has_last_expression) { - ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }); + ops.push({ code: OpCodeEnum.Push, arg: nil, debug: { line: 0, column: 0 } }); } return { diff --git a/src/engine.mts b/src/engine.mts index dba133f..70102eb 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,7 +1,7 @@ import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; import type { Op } from "./opcode.mjs"; -import { OpCode, op_code_name } from "./opcode.mjs"; +import { op_code_name } from "./opcode.mjs"; import { make_boolean, make_number, make_string, make_table } from "./runtime.mjs"; import { TokenStream } from "./lexer.mjs"; import { parse } from "./parser.mjs"; @@ -21,6 +21,7 @@ import type { VariableFunction } from "./variable/definition/interface/variable- import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; import { equals } from "./variable/equals.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; +import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; function index(val: Variable | undefined): string | number | undefined { @@ -378,7 +379,7 @@ export class Engine switch (code) { - case OpCode.Pop: + case OpCodeEnum.Pop: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; @@ -386,7 +387,7 @@ export class Engine break; } - case OpCode.Dup: + case OpCodeEnum.Dup: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; const items = this.stack.splice(this.stack.length - count, count); @@ -395,7 +396,7 @@ export class Engine break; } - case OpCode.Swap: + case OpCodeEnum.Swap: { const x = this.stack.splice(this.stack.length - 2, 1); @@ -403,13 +404,13 @@ export class Engine break; } - case OpCode.IterUpdateState: + case OpCodeEnum.IterUpdateState: { this.stack[this.stack.length - 2] = this.stack_get(-1); break; } - case OpCode.IterNext: + case OpCodeEnum.IterNext: { const state = this.stack_get(-1); const control = this.stack_get(-2); @@ -441,7 +442,7 @@ export class Engine break; } - case OpCode.IterJumpIfDone: + case OpCodeEnum.IterJumpIfDone: { if (isVariableKind(arg, VariableKind.Number) && isNil(this.stack_get(-1))) { @@ -451,7 +452,7 @@ export class Engine break; } - case OpCode.Add: + case OpCodeEnum.Add: this.operation( (x, y) => { @@ -460,7 +461,7 @@ export class Engine ); break; - case OpCode.Subtract: + case OpCodeEnum.Subtract: this.operation( (x, y) => { @@ -469,7 +470,7 @@ export class Engine ); break; - case OpCode.Multiply: + case OpCodeEnum.Multiply: this.operation( (x, y) => { @@ -478,7 +479,7 @@ export class Engine ); break; - case OpCode.Divide: + case OpCodeEnum.Divide: this.operation( (x, y) => { @@ -487,7 +488,7 @@ export class Engine ); break; - case OpCode.FloorDivide: + case OpCodeEnum.FloorDivide: this.operation( (x, y) => { @@ -496,7 +497,7 @@ export class Engine ); break; - case OpCode.Modulo: + case OpCodeEnum.Modulo: this.operation( (x, y) => { @@ -505,7 +506,7 @@ export class Engine ); break; - case OpCode.Exponent: + case OpCodeEnum.Exponent: this.operation( (x, y) => { @@ -515,7 +516,7 @@ export class Engine break; - case OpCode.LessThan: + case OpCodeEnum.LessThan: this.compare( (x, y) => { @@ -524,7 +525,7 @@ export class Engine ); break; - case OpCode.LessThanEquals: + case OpCodeEnum.LessThanEquals: this.compare( (x, y) => { @@ -533,7 +534,7 @@ export class Engine ); break; - case OpCode.GreaterThan: + case OpCodeEnum.GreaterThan: this.compare( (x, y) => { @@ -542,7 +543,7 @@ export class Engine ); break; - case OpCode.GreaterThanEquals: + case OpCodeEnum.GreaterThanEquals: this.compare( (x, y) => { @@ -552,7 +553,7 @@ export class Engine break; - case OpCode.BitAnd: + case OpCodeEnum.BitAnd: this.operation( (x, y) => { @@ -561,7 +562,7 @@ export class Engine ); break; - case OpCode.BitOr: + case OpCodeEnum.BitOr: this.operation( (x, y) => { @@ -570,7 +571,7 @@ export class Engine ); break; - case OpCode.BitXOr: + case OpCodeEnum.BitXOr: this.operation( (x, y) => { @@ -579,7 +580,7 @@ export class Engine ); break; - case OpCode.BitShiftLeft: + case OpCodeEnum.BitShiftLeft: this.operation( (x, y) => { @@ -588,7 +589,7 @@ export class Engine ); break; - case OpCode.BitShiftRight: + case OpCodeEnum.BitShiftRight: this.operation( (x, y) => { @@ -598,7 +599,7 @@ export class Engine break; - case OpCode.Concat: + case OpCodeEnum.Concat: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -609,7 +610,7 @@ export class Engine break; } - case OpCode.Equals: + case OpCodeEnum.Equals: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -618,7 +619,7 @@ export class Engine break; } - case OpCode.NotEquals: + case OpCodeEnum.NotEquals: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -627,7 +628,7 @@ export class Engine break; } - case OpCode.And: + case OpCodeEnum.And: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -638,7 +639,7 @@ export class Engine break; } - case OpCode.Or: + case OpCodeEnum.Or: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -649,23 +650,23 @@ export class Engine break; } - case OpCode.Not: + case OpCodeEnum.Not: this.stack.push(make_boolean(!is_true(this.stack_pop()))); break; - case OpCode.BitNot: + case OpCodeEnum.BitNot: this.stack.push(make_number(~this.stack_pop_kind(VariableKind.Number).number)); break; - case OpCode.Negate: + case OpCodeEnum.Negate: this.stack.push(make_number(-this.stack_pop_kind(VariableKind.Number).number)); break; - case OpCode.IsNotNil: + case OpCodeEnum.IsNotNil: this.stack.push(make_boolean(!isNil(this.stack_pop()))); break; - case OpCode.Jump: + case OpCodeEnum.Jump: if (isVariableKind(arg, VariableKind.Number)) { this.ip = this.ip + arg.number; @@ -673,7 +674,7 @@ export class Engine break; - case OpCode.JumpIfNot: + case OpCodeEnum.JumpIfNot: if (isVariableKind(arg, VariableKind.Number) && !is_true(this.stack_pop())) { this.ip = this.ip + arg.number; @@ -681,7 +682,7 @@ export class Engine break; - case OpCode.JumpIf: + case OpCodeEnum.JumpIf: if (isVariableKind(arg, VariableKind.Number) && is_true(this.stack_pop())) { this.ip = this.ip + arg.number; @@ -689,7 +690,7 @@ export class Engine break; - case OpCode.MakeLocal: + case OpCodeEnum.MakeLocal: const last_locals = this.locals_stack.at(-1); if (last_locals) @@ -699,19 +700,19 @@ export class Engine break; - case OpCode.NewTable: + case OpCodeEnum.NewTable: this.stack.push(make_table()); break; - case OpCode.StartBlock: + case OpCodeEnum.StartBlock: this.locals_stack.push(new Map()); break; - case OpCode.EndBlock: + case OpCodeEnum.EndBlock: this.locals_stack.pop(); break; - case OpCode.Length: + case OpCodeEnum.Length: { const variable = this.stack_pop_maybe(); @@ -731,7 +732,7 @@ export class Engine break; } - case OpCode.Return: + case OpCodeEnum.Return: { this.ip = this.call_stack.pop() ?? this.program.length; this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()); @@ -739,7 +740,7 @@ export class Engine break; } - case OpCode.LoadIndex: + case OpCodeEnum.LoadIndex: { const table = this.stack_pop_maybe(); @@ -768,7 +769,7 @@ export class Engine break; } - case OpCode.StoreIndex: + case OpCodeEnum.StoreIndex: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; const table = this.stack_get(-1 - count * 2); @@ -791,7 +792,7 @@ export class Engine break; } - case OpCode.Store: + case OpCodeEnum.Store: { const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; const value = this.stack_pop_maybe(); @@ -814,7 +815,7 @@ export class Engine break; } - case OpCode.Push: + case OpCodeEnum.Push: { if (this.locals_stack.length > 0 && isVariableKind(arg, VariableKind.Function)) { @@ -825,7 +826,7 @@ export class Engine break; } - case OpCode.Load: + case OpCodeEnum.Load: { const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; const local = [...this.locals_capture, ...this.locals_stack] @@ -848,7 +849,7 @@ export class Engine break; } - case OpCode.Call: + case OpCodeEnum.Call: { const x = this.stack_pop_maybe(); const count = isVariableKind(x, VariableKind.Number) ? x.number : 0; @@ -903,7 +904,7 @@ export class Engine break; } - case OpCode.ArgumentCount: + case OpCodeEnum.ArgumentCount: { const x = this.stack_pop_maybe(); const got = isVariableKind(x, VariableKind.Number) ? x.number : 0; @@ -913,13 +914,13 @@ export class Engine break; } - case OpCode.StartStackChange: + case OpCodeEnum.StartStackChange: { this.assign_height_stack.push(this.stack.length); break; } - case OpCode.EndStackChange: + case OpCodeEnum.EndStackChange: { const got = this.stack.length - (this.assign_height_stack.pop() ?? 0); const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0; diff --git a/src/opcode.mts b/src/opcode.mts index 21d8e9d..c89222a 100644 --- a/src/opcode.mts +++ b/src/opcode.mts @@ -1,127 +1,66 @@ import type { Variable } from "./variable/definition/type/variable.type.mjs"; import type { Debug } from "./lexer.mjs"; +import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; -export enum OpCode { - Load = "load", - Store = "store", - Push = "push", - Pop = "pop", - Dup = "dup", - Swap = "swap", - - IterUpdateState = "iter_update_state", - IterNext = "iter_next", - IterJumpIfDone = "iter_jump_if_done", - - NewTable = "new_table", - LoadIndex = "load_index", - StoreIndex = "store_index", - - Add = "add", - Subtract = "subtract", - Multiply = "multiply", - Divide = "divide", - FloorDivide = "floor_divide", - Modulo = "modulo", - Exponent = "exponent", - Concat = "concat", - - BitAnd = "bit_and", - BitOr = "bit_or", - BitXOr = "bit_xor", - BitNot = "bit_not", - BitShiftLeft = "bit_shift_left", - BitShiftRight = "bit_shift_right", - - Equals = "equals", - NotEquals = "not_equals", - LessThan = "less_than", - LessThanEquals = "less_than_equals", - GreaterThan = "greater_than", - GreaterThanEquals = "greater_than_equals", - - And = "and", - Or = "or", - Not = "not", - - Negate = "negate", - Length = "length", - IsNotNil = "is_not_nil", - - StartBlock = "start_block", - EndBlock = "end_block", - MakeLocal = "make_local", - Call = "call", - Return = "return", - Jump = "jump", - JumpIfNot = "jump_if_not", - JumpIf = "jump_if", - - StartStackChange = "start_stack_change", - EndStackChange = "end_stack_change", - ArgumentCount = "argument_count", - Break = "break", -} - -export function op_code_name(op_code: OpCode): string +export function op_code_name(op_code: OpCodeEnum): string { switch (op_code) { - case OpCode.Load: return "Load"; - case OpCode.Store: return "Store"; - case OpCode.Push: return "Push"; - case OpCode.Pop: return "Pop"; - case OpCode.Dup: return "Dup"; - case OpCode.Swap: return "Swap"; - case OpCode.IterUpdateState: return "IterUpdateState"; - case OpCode.IterNext: return "IterNext"; - case OpCode.IterJumpIfDone: return "IterJumpIfDone"; - case OpCode.NewTable: return "NewTable"; - case OpCode.LoadIndex: return "LoadIndex"; - case OpCode.StoreIndex: return "StoreIndex"; - case OpCode.Add: return "Add"; - case OpCode.Subtract: return "Subtract"; - case OpCode.Multiply: return "Multiply"; - case OpCode.Divide: return "Divide"; - case OpCode.FloorDivide: return "FloorDivide"; - case OpCode.Modulo: return "Modulo"; - case OpCode.Exponent: return "Exponent"; - case OpCode.Concat: return "Concat"; - case OpCode.BitAnd: return "BitAnd"; - case OpCode.BitOr: return "BitOr"; - case OpCode.BitXOr: return "BitXOr"; - case OpCode.BitNot: return "BitNot"; - case OpCode.BitShiftLeft: return "BitShiftLeft"; - case OpCode.BitShiftRight: return "BitShiftRight"; - case OpCode.Equals: return "Equals"; - case OpCode.NotEquals: return "NotEquals"; - case OpCode.LessThan: return "LessThan"; - case OpCode.LessThanEquals: return "LessThanEquals"; - case OpCode.GreaterThan: return "GreaterThan"; - case OpCode.GreaterThanEquals: return "GreaterThanEquals"; - case OpCode.And: return "And"; - case OpCode.Or: return "Or"; - case OpCode.Not: return "Not"; - case OpCode.Negate: return "Negate"; - case OpCode.Length: return "Length"; - case OpCode.IsNotNil: return "IsNotNil"; - case OpCode.StartBlock: return "StartBlock"; - case OpCode.EndBlock: return "EndBlock"; - case OpCode.MakeLocal: return "MakeLocal"; - case OpCode.Call: return "Call"; - case OpCode.Return: return "Return"; - case OpCode.Jump: return "Jump"; - case OpCode.JumpIfNot: return "JumpIfNot"; - case OpCode.JumpIf: return "JumpIf"; - case OpCode.StartStackChange: return "StartStackChange"; - case OpCode.EndStackChange: return "EndStackChange"; - case OpCode.ArgumentCount: return "ArgumentCount"; - case OpCode.Break: return "Break[Debug]"; + case OpCodeEnum.Load: return "Load"; + case OpCodeEnum.Store: return "Store"; + case OpCodeEnum.Push: return "Push"; + case OpCodeEnum.Pop: return "Pop"; + case OpCodeEnum.Dup: return "Dup"; + case OpCodeEnum.Swap: return "Swap"; + case OpCodeEnum.IterUpdateState: return "IterUpdateState"; + case OpCodeEnum.IterNext: return "IterNext"; + case OpCodeEnum.IterJumpIfDone: return "IterJumpIfDone"; + case OpCodeEnum.NewTable: return "NewTable"; + case OpCodeEnum.LoadIndex: return "LoadIndex"; + case OpCodeEnum.StoreIndex: return "StoreIndex"; + case OpCodeEnum.Add: return "Add"; + case OpCodeEnum.Subtract: return "Subtract"; + case OpCodeEnum.Multiply: return "Multiply"; + case OpCodeEnum.Divide: return "Divide"; + case OpCodeEnum.FloorDivide: return "FloorDivide"; + case OpCodeEnum.Modulo: return "Modulo"; + case OpCodeEnum.Exponent: return "Exponent"; + case OpCodeEnum.Concat: return "Concat"; + case OpCodeEnum.BitAnd: return "BitAnd"; + case OpCodeEnum.BitOr: return "BitOr"; + case OpCodeEnum.BitXOr: return "BitXOr"; + case OpCodeEnum.BitNot: return "BitNot"; + case OpCodeEnum.BitShiftLeft: return "BitShiftLeft"; + case OpCodeEnum.BitShiftRight: return "BitShiftRight"; + case OpCodeEnum.Equals: return "Equals"; + case OpCodeEnum.NotEquals: return "NotEquals"; + case OpCodeEnum.LessThan: return "LessThan"; + case OpCodeEnum.LessThanEquals: return "LessThanEquals"; + case OpCodeEnum.GreaterThan: return "GreaterThan"; + case OpCodeEnum.GreaterThanEquals: return "GreaterThanEquals"; + case OpCodeEnum.And: return "And"; + case OpCodeEnum.Or: return "Or"; + case OpCodeEnum.Not: return "Not"; + case OpCodeEnum.Negate: return "Negate"; + case OpCodeEnum.Length: return "Length"; + case OpCodeEnum.IsNotNil: return "IsNotNil"; + case OpCodeEnum.StartBlock: return "StartBlock"; + case OpCodeEnum.EndBlock: return "EndBlock"; + case OpCodeEnum.MakeLocal: return "MakeLocal"; + case OpCodeEnum.Call: return "Call"; + case OpCodeEnum.Return: return "Return"; + case OpCodeEnum.Jump: return "Jump"; + case OpCodeEnum.JumpIfNot: return "JumpIfNot"; + case OpCodeEnum.JumpIf: return "JumpIf"; + case OpCodeEnum.StartStackChange: return "StartStackChange"; + case OpCodeEnum.EndStackChange: return "EndStackChange"; + case OpCodeEnum.ArgumentCount: return "ArgumentCount"; + case OpCodeEnum.Break: return "Break[Debug]"; } } export interface Op { - code: OpCode; + code: OpCodeEnum; arg?: Variable; debug: Debug; } diff --git a/src/opcode/definition/enum/op-code.enum.mts b/src/opcode/definition/enum/op-code.enum.mts new file mode 100644 index 0000000..5589165 --- /dev/null +++ b/src/opcode/definition/enum/op-code.enum.mts @@ -0,0 +1,63 @@ +const enum OpCodeEnum { + Load = "load", + Store = "store", + Push = "push", + Pop = "pop", + Dup = "dup", + Swap = "swap", + + IterUpdateState = "iter_update_state", + IterNext = "iter_next", + IterJumpIfDone = "iter_jump_if_done", + + NewTable = "new_table", + LoadIndex = "load_index", + StoreIndex = "store_index", + + Add = "add", + Subtract = "subtract", + Multiply = "multiply", + Divide = "divide", + FloorDivide = "floor_divide", + Modulo = "modulo", + Exponent = "exponent", + Concat = "concat", + + BitAnd = "bit_and", + BitOr = "bit_or", + BitXOr = "bit_xor", + BitNot = "bit_not", + BitShiftLeft = "bit_shift_left", + BitShiftRight = "bit_shift_right", + + Equals = "equals", + NotEquals = "not_equals", + LessThan = "less_than", + LessThanEquals = "less_than_equals", + GreaterThan = "greater_than", + GreaterThanEquals = "greater_than_equals", + + And = "and", + Or = "or", + Not = "not", + + Negate = "negate", + Length = "length", + IsNotNil = "is_not_nil", + + StartBlock = "start_block", + EndBlock = "end_block", + MakeLocal = "make_local", + Call = "call", + Return = "return", + Jump = "jump", + JumpIfNot = "jump_if_not", + JumpIf = "jump_if", + + StartStackChange = "start_stack_change", + EndStackChange = "end_stack_change", + ArgumentCount = "argument_count", + Break = "break", +} + +export { OpCodeEnum }; From e9cba403678313b0c59bdddefaebbb1aaded6586 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:12:56 +0000 Subject: [PATCH 21/71] fix: Moved State to its file and renamed it StateEnum. --- src/lexer.mts | 86 ++++++++++-------------- src/lexer/definition/enum/state.enum.mts | 15 +++++ 2 files changed, 52 insertions(+), 49 deletions(-) create mode 100644 src/lexer/definition/enum/state.enum.mts diff --git a/src/lexer.mts b/src/lexer.mts index a2bf6f9..9eef02a 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,16 +1,4 @@ -export enum State { - Initial = "initial", - Identifier = "identifier", - StringLiteral = "string-literal", - StringLiteralEscape = "string-literal-escape", - MultiLineString = "multi-line-string", - NumberLiteral = "number-literal", - NumberLiteralDot = "number-literal-dot", - NumberLiteralExpSign = "number-literal-exp-sign", - NumberLiteralExp = "number-literal-exp", - NumberHex = "number-hex", - Comment = "comment", -} +import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; export enum TokenKind { EOF = "EOF", @@ -217,7 +205,7 @@ export class TokenStream private readonly processing_stream: Array; private readonly peek_queue: Array; - private state: State; + private state: StateEnum; private end_of_stream: boolean = false; private buffer: string; private token_start_debug: Debug; @@ -227,7 +215,7 @@ export class TokenStream constructor() { - this.state = State.Initial; + this.state = StateEnum.Initial; this.processing_stream = []; this.buffer = ""; this.token_start_debug = { line: 0, column: 0 }; @@ -311,14 +299,14 @@ this.consume(); if (double === "--") { - this.state = State.Comment; + this.state = StateEnum.Comment; return; } if (double === "[[") { - this.state = State.MultiLineString; + this.state = StateEnum.MultiLineString; this.start_token(); this.consume(); this.consume(); @@ -368,7 +356,7 @@ this.consume(); { this.start_token(); this.consume(); - this.state = State.StringLiteral; + this.state = StateEnum.StringLiteral; return; } @@ -376,7 +364,7 @@ this.consume(); if (/[a-zA-Z_]/.test(c)) { this.start_token(); - this.state = State.Identifier; + this.state = StateEnum.Identifier; return; } @@ -384,7 +372,7 @@ this.consume(); if (/[0-9]/.test(c)) { this.start_token(); - this.state = State.NumberLiteral; + this.state = StateEnum.NumberLiteral; } } @@ -402,14 +390,14 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; return; } if (c === "\\") { - this.state = State.StringLiteralEscape; + this.state = StateEnum.StringLiteralEscape; return; } @@ -422,7 +410,7 @@ this.consume(); const c = this.current(); this.consume(); - this.state = State.StringLiteral; + this.state = StateEnum.StringLiteral; switch (c) { @@ -452,7 +440,7 @@ this.consume(); }); this.consume(); - this.state = State.Initial; + this.state = StateEnum.Initial; return; } @@ -474,7 +462,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; return; } @@ -499,7 +487,7 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = State.NumberLiteralDot; + this.state = StateEnum.NumberLiteralDot; return; } @@ -508,7 +496,7 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = State.NumberLiteralExp; + this.state = StateEnum.NumberLiteralExp; return; } @@ -523,13 +511,13 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; return; } this.buffer = this.buffer + c; - this.state = State.NumberHex; + this.state = StateEnum.NumberHex; this.consume(); return; @@ -541,7 +529,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; } private number_dot() @@ -559,7 +547,7 @@ this.consume(); if (c === "e" || c === "E") { this.buffer = this.buffer + c; - this.state = State.NumberLiteralExpSign; + this.state = StateEnum.NumberLiteralExpSign; this.consume(); return; @@ -571,7 +559,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; } private number_exp_sign() @@ -582,7 +570,7 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = State.NumberLiteralExp; + this.state = StateEnum.NumberLiteralExp; return; } @@ -593,7 +581,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; } private number_exp() @@ -614,7 +602,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; } private number_hex() @@ -635,7 +623,7 @@ this.consume(); debug: this.token_start_debug, }); - this.state = State.Initial; + this.state = StateEnum.Initial; } private comment() @@ -646,7 +634,7 @@ this.consume(); if (c === "\n") { - this.state = State.Initial; + this.state = StateEnum.Initial; } } @@ -656,7 +644,7 @@ this.consume(); { this.peek_queue.push({ data: "", - kind: this.state === State.Initial + kind: this.state === StateEnum.Initial ? TokenKind.EOF : TokenKind.NotFinished, debug: { @@ -670,37 +658,37 @@ this.consume(); switch (this.state) { - case State.Initial: + case StateEnum.Initial: this.initial(); break; - case State.Identifier: + case StateEnum.Identifier: this.read_identifier(); break; - case State.StringLiteral: + case StateEnum.StringLiteral: this.read_string(); break; - case State.StringLiteralEscape: + case StateEnum.StringLiteralEscape: this.read_string_escape(); break; - case State.MultiLineString: + case StateEnum.MultiLineString: this.read_multi_line_string(); break; - case State.NumberLiteral: + case StateEnum.NumberLiteral: this.number(); break; - case State.NumberLiteralDot: + case StateEnum.NumberLiteralDot: this.number_dot(); break; - case State.NumberLiteralExpSign: + case StateEnum.NumberLiteralExpSign: this.number_exp_sign(); break; - case State.NumberLiteralExp: + case StateEnum.NumberLiteralExp: this.number_exp(); break; - case State.NumberHex: + case StateEnum.NumberHex: this.number_hex(); break; - case State.Comment: + case StateEnum.Comment: this.comment(); break; } diff --git a/src/lexer/definition/enum/state.enum.mts b/src/lexer/definition/enum/state.enum.mts new file mode 100644 index 0000000..a2f9cef --- /dev/null +++ b/src/lexer/definition/enum/state.enum.mts @@ -0,0 +1,15 @@ +const enum StateEnum { + Initial = "initial", + Identifier = "identifier", + StringLiteral = "string-literal", + StringLiteralEscape = "string-literal-escape", + MultiLineString = "multi-line-string", + NumberLiteral = "number-literal", + NumberLiteralDot = "number-literal-dot", + NumberLiteralExpSign = "number-literal-exp-sign", + NumberLiteralExp = "number-literal-exp", + NumberHex = "number-hex", + Comment = "comment", +} + +export { StateEnum }; From 889e50754fc1f4df97df0bcbe53d0b2bbf96d239 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 11:14:26 +0000 Subject: [PATCH 22/71] fix: Moved TokenKind to its own file and renamed it TokenKindEnum. --- src/lexer.mts | 310 +++++++----------- src/lexer/definition/enum/token-kind.enum.mts | 66 ++++ src/parser.mts | 283 ++++++++-------- 3 files changed, 331 insertions(+), 328 deletions(-) create mode 100644 src/lexer/definition/enum/token-kind.enum.mts diff --git a/src/lexer.mts b/src/lexer.mts index 9eef02a..ed33385 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,123 +1,59 @@ import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; +import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; -export enum TokenKind { - EOF = "EOF", - NotFinished = "not-finished", - - Identifier = "identifier", - StringLiteral = "string-literal", - BooleanLiteral = "boolean-literal", - NumberLiteral = "number-literal", - NilLiteral = "nil-literal", - - OpenBrace = "open-brace", - CloseBrace = "close-brace", - OpenSquare = "open-square", - CloseSquare = "close-square", - SquiglyOpen = "squigly-open", - SquiglyClose = "squigly-close", - - Addition = "addition", - Subtract = "subtract", - Multiply = "multiply", - Division = "division", - FloorDivision = "floor-division", - Modulo = "modulo", - Exponent = "exponent", - Concat = "concat", - Hash = "hash", - - BitAnd = "bit-and", - BitOr = "bit-or", - BitXOrNot = "bit-xor-not", - BitShiftLeft = "bit-shift-left", - BitShiftRight = "bit-shift-right", - - Equals = "equals", - NotEquals = "not-equals", - LessThan = "less-than", - LessThanEquals = "less-than-equals", - GreaterThan = "greater-than", - GreaterThanEquals = "greater-than-equals", - And = "and", - Or = "or", - Not = "not", - - Assign = "assign", - Semicolon = "semicolon", - Comma = "comma", - Dot = "dot", - - Function = "function", - If = "if", - While = "while", - For = "for", - Repeat = "repeat", - In = "in", - Do = "do", - Then = "then", - ElseIf = "elseif", - Else = "else", - Until = "until", - End = "end", - Return = "return", - Break = "break", - Local = "local", -} - -export function token_kind_to_string(kind: TokenKind): string +export function token_kind_to_string(kind: TokenKindEnum): string { switch (kind) { - case TokenKind.EOF: return "EOF"; - case TokenKind.NotFinished: return "NotFinished"; - case TokenKind.Identifier: return "Identifier"; - case TokenKind.StringLiteral: return "StringLiteral"; - case TokenKind.BooleanLiteral: return "BooleanLiteral"; - case TokenKind.NumberLiteral: return "NumberLiteral"; - case TokenKind.NilLiteral: return "nil"; - case TokenKind.OpenBrace: return "("; - case TokenKind.CloseBrace: return ")"; - case TokenKind.OpenSquare: return "["; - case TokenKind.CloseSquare: return "]"; - case TokenKind.SquiglyOpen: return "{"; - case TokenKind.SquiglyClose: return "}"; - case TokenKind.Addition: return "+"; - case TokenKind.Subtract: return "-"; - case TokenKind.Multiply: return "*"; - case TokenKind.Division: return "/"; - case TokenKind.FloorDivision: return "//"; - case TokenKind.Modulo: return "%"; - case TokenKind.Exponent: return "^"; - case TokenKind.BitAnd: return "&"; - case TokenKind.BitOr: return "|"; - case TokenKind.BitXOrNot: return "~"; - case TokenKind.BitShiftLeft: return "<<"; - case TokenKind.BitShiftRight: return ">>"; - case TokenKind.LessThan: return "<"; - case TokenKind.GreaterThan: return ">"; - case TokenKind.And: return "and"; - case TokenKind.Or: return "or"; - case TokenKind.Not: return "not"; - case TokenKind.Assign: return "="; - case TokenKind.Semicolon: return ";"; - case TokenKind.Comma: return ","; - case TokenKind.Dot: return "."; - case TokenKind.Function: return "function"; - case TokenKind.If: return "if"; - case TokenKind.While: return "while"; - case TokenKind.For: return "for"; - case TokenKind.Repeat: return "repeat"; - case TokenKind.In: return "in"; - case TokenKind.Do: return "do"; - case TokenKind.Then: return "then"; - case TokenKind.ElseIf: return "elseif"; - case TokenKind.Else: return "else"; - case TokenKind.Until: return "until"; - case TokenKind.End: return "end"; - case TokenKind.Return: return "return"; - case TokenKind.Break: return "break"; - case TokenKind.Local: return "local"; + case TokenKindEnum.EOF: return "EOF"; + case TokenKindEnum.NotFinished: return "NotFinished"; + case TokenKindEnum.Identifier: return "Identifier"; + case TokenKindEnum.StringLiteral: return "StringLiteral"; + case TokenKindEnum.BooleanLiteral: return "BooleanLiteral"; + case TokenKindEnum.NumberLiteral: return "NumberLiteral"; + case TokenKindEnum.NilLiteral: return "nil"; + case TokenKindEnum.OpenBrace: return "("; + case TokenKindEnum.CloseBrace: return ")"; + case TokenKindEnum.OpenSquare: return "["; + case TokenKindEnum.CloseSquare: return "]"; + case TokenKindEnum.SquiglyOpen: return "{"; + case TokenKindEnum.SquiglyClose: return "}"; + case TokenKindEnum.Addition: return "+"; + case TokenKindEnum.Subtract: return "-"; + case TokenKindEnum.Multiply: return "*"; + case TokenKindEnum.Division: return "/"; + case TokenKindEnum.FloorDivision: return "//"; + case TokenKindEnum.Modulo: return "%"; + case TokenKindEnum.Exponent: return "^"; + case TokenKindEnum.BitAnd: return "&"; + case TokenKindEnum.BitOr: return "|"; + case TokenKindEnum.BitXOrNot: return "~"; + case TokenKindEnum.BitShiftLeft: return "<<"; + case TokenKindEnum.BitShiftRight: return ">>"; + case TokenKindEnum.LessThan: return "<"; + case TokenKindEnum.GreaterThan: return ">"; + case TokenKindEnum.And: return "and"; + case TokenKindEnum.Or: return "or"; + case TokenKindEnum.Not: return "not"; + case TokenKindEnum.Assign: return "="; + case TokenKindEnum.Semicolon: return ";"; + case TokenKindEnum.Comma: return ","; + case TokenKindEnum.Dot: return "."; + case TokenKindEnum.FunctionLike: return "function"; + case TokenKindEnum.If: return "if"; + case TokenKindEnum.While: return "while"; + case TokenKindEnum.For: return "for"; + case TokenKindEnum.Repeat: return "repeat"; + case TokenKindEnum.In: return "in"; + case TokenKindEnum.Do: return "do"; + case TokenKindEnum.Then: return "then"; + case TokenKindEnum.ElseIf: return "elseif"; + case TokenKindEnum.Else: return "else"; + case TokenKindEnum.Until: return "until"; + case TokenKindEnum.End: return "end"; + case TokenKindEnum.Return: return "return"; + case TokenKindEnum.Break: return "break"; + case TokenKindEnum.Local: return "local"; default: return "unknown token"; @@ -131,73 +67,73 @@ export interface Debug { export interface Token { data: string; - kind: TokenKind; + kind: TokenKindEnum; debug: Debug; } -const single_token_map: Map = new Map([ - ["(", TokenKind.OpenBrace], - [")", TokenKind.CloseBrace], - ["[", TokenKind.OpenSquare], - ["]", TokenKind.CloseSquare], - ["{", TokenKind.SquiglyOpen], - ["}", TokenKind.SquiglyClose], - - ["+", TokenKind.Addition], - ["-", TokenKind.Subtract], - ["*", TokenKind.Multiply], - ["/", TokenKind.Division], - ["%", TokenKind.Modulo], - ["^", TokenKind.Exponent], - ["&", TokenKind.BitAnd], - ["|", TokenKind.BitOr], - ["~", TokenKind.BitXOrNot], - - ["<", TokenKind.LessThan], - [">", TokenKind.GreaterThan], - - ["=", TokenKind.Assign], - [";", TokenKind.Semicolon], - [",", TokenKind.Comma], - [".", TokenKind.Dot], - ["#", TokenKind.Hash], +const single_token_map: Map = new Map([ + ["(", TokenKindEnum.OpenBrace], + [")", TokenKindEnum.CloseBrace], + ["[", TokenKindEnum.OpenSquare], + ["]", TokenKindEnum.CloseSquare], + ["{", TokenKindEnum.SquiglyOpen], + ["}", TokenKindEnum.SquiglyClose], + + ["+", TokenKindEnum.Addition], + ["-", TokenKindEnum.Subtract], + ["*", TokenKindEnum.Multiply], + ["/", TokenKindEnum.Division], + ["%", TokenKindEnum.Modulo], + ["^", TokenKindEnum.Exponent], + ["&", TokenKindEnum.BitAnd], + ["|", TokenKindEnum.BitOr], + ["~", TokenKindEnum.BitXOrNot], + + ["<", TokenKindEnum.LessThan], + [">", TokenKindEnum.GreaterThan], + + ["=", TokenKindEnum.Assign], + [";", TokenKindEnum.Semicolon], + [",", TokenKindEnum.Comma], + [".", TokenKindEnum.Dot], + ["#", TokenKindEnum.Hash], ]); -const double_token_map: Map = new Map([ - ["==", TokenKind.Equals], - ["<=", TokenKind.LessThanEquals], - [">=", TokenKind.GreaterThanEquals], - ["~=", TokenKind.NotEquals], - ["..", TokenKind.Concat], - ["//", TokenKind.FloorDivision], - ["<<", TokenKind.BitShiftLeft], - [">>", TokenKind.BitShiftRight], +const double_token_map: Map = new Map([ + ["==", TokenKindEnum.Equals], + ["<=", TokenKindEnum.LessThanEquals], + [">=", TokenKindEnum.GreaterThanEquals], + ["~=", TokenKindEnum.NotEquals], + ["..", TokenKindEnum.Concat], + ["//", TokenKindEnum.FloorDivision], + ["<<", TokenKindEnum.BitShiftLeft], + [">>", TokenKindEnum.BitShiftRight], ]); -const keyword_map: Map = new Map([ - ["function", TokenKind.Function], - ["if", TokenKind.If], - ["while", TokenKind.While], - ["for", TokenKind.For], - ["repeat", TokenKind.Repeat], - ["in", TokenKind.In], - ["do", TokenKind.Do], - ["then", TokenKind.Then], - ["elseif", TokenKind.ElseIf], - ["else", TokenKind.Else], - ["until", TokenKind.Until], - ["end", TokenKind.End], - ["return", TokenKind.Return], - ["break", TokenKind.Break], - - ["and", TokenKind.And], - ["or", TokenKind.Or], - ["not", TokenKind.Not], - - ["true", TokenKind.BooleanLiteral], - ["false", TokenKind.BooleanLiteral], - ["nil", TokenKind.NilLiteral], - ["local", TokenKind.Local], +const keyword_map: Map = new Map([ + ["function", TokenKindEnum.FunctionLike], + ["if", TokenKindEnum.If], + ["while", TokenKindEnum.While], + ["for", TokenKindEnum.For], + ["repeat", TokenKindEnum.Repeat], + ["in", TokenKindEnum.In], + ["do", TokenKindEnum.Do], + ["then", TokenKindEnum.Then], + ["elseif", TokenKindEnum.ElseIf], + ["else", TokenKindEnum.Else], + ["until", TokenKindEnum.Until], + ["end", TokenKindEnum.End], + ["return", TokenKindEnum.Return], + ["break", TokenKindEnum.Break], + + ["and", TokenKindEnum.And], + ["or", TokenKindEnum.Or], + ["not", TokenKindEnum.Not], + + ["true", TokenKindEnum.BooleanLiteral], + ["false", TokenKindEnum.BooleanLiteral], + ["nil", TokenKindEnum.NilLiteral], + ["local", TokenKindEnum.Local], ]); export class TokenStream @@ -274,7 +210,7 @@ export class TokenStream { this.peek_queue.push({ data: "", - kind: TokenKind.EOF, + kind: TokenKindEnum.EOF, debug: { line: this.line, column: this.column, @@ -386,7 +322,7 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKind.StringLiteral, + kind: TokenKindEnum.StringLiteral, debug: this.token_start_debug, }); @@ -435,7 +371,7 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKind.StringLiteral, + kind: TokenKindEnum.StringLiteral, debug: this.token_start_debug, }); @@ -458,7 +394,7 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: kind ?? TokenKind.Identifier, + kind: kind ?? TokenKindEnum.Identifier, debug: this.token_start_debug, }); @@ -507,7 +443,7 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -525,7 +461,7 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -555,7 +491,7 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -577,7 +513,7 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -598,7 +534,7 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -619,7 +555,7 @@ this.consume(); this.peek_queue.push({ data: parseInt(this.buffer.slice(2), 16).toString(), - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, debug: this.token_start_debug, }); @@ -645,8 +581,8 @@ this.consume(); this.peek_queue.push({ data: "", kind: this.state === StateEnum.Initial - ? TokenKind.EOF - : TokenKind.NotFinished, + ? TokenKindEnum.EOF + : TokenKindEnum.NotFinished, debug: { line: this.line, column: this.column, diff --git a/src/lexer/definition/enum/token-kind.enum.mts b/src/lexer/definition/enum/token-kind.enum.mts new file mode 100644 index 0000000..587680c --- /dev/null +++ b/src/lexer/definition/enum/token-kind.enum.mts @@ -0,0 +1,66 @@ +const enum TokenKindEnum { + EOF = "EOF", + NotFinished = "not-finished", + + Identifier = "identifier", + StringLiteral = "string-literal", + BooleanLiteral = "boolean-literal", + NumberLiteral = "number-literal", + NilLiteral = "nil-literal", + + OpenBrace = "open-brace", + CloseBrace = "close-brace", + OpenSquare = "open-square", + CloseSquare = "close-square", + SquiglyOpen = "squigly-open", + SquiglyClose = "squigly-close", + + Addition = "addition", + Subtract = "subtract", + Multiply = "multiply", + Division = "division", + FloorDivision = "floor-division", + Modulo = "modulo", + Exponent = "exponent", + Concat = "concat", + Hash = "hash", + + BitAnd = "bit-and", + BitOr = "bit-or", + BitXOrNot = "bit-xor-not", + BitShiftLeft = "bit-shift-left", + BitShiftRight = "bit-shift-right", + + Equals = "equals", + NotEquals = "not-equals", + LessThan = "less-than", + LessThanEquals = "less-than-equals", + GreaterThan = "greater-than", + GreaterThanEquals = "greater-than-equals", + And = "and", + Or = "or", + Not = "not", + + Assign = "assign", + Semicolon = "semicolon", + Comma = "comma", + Dot = "dot", + + FunctionLike = "function", + If = "if", + While = "while", + For = "for", + Repeat = "repeat", + In = "in", + Do = "do", + Then = "then", + ElseIf = "elseif", + Else = "else", + Until = "until", + End = "end", + Return = "return", + Break = "break", + Local = "local", +} + +export { TokenKindEnum }; diff --git a/src/parser.mts b/src/parser.mts index a289987..be8dc87 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -2,26 +2,27 @@ import type { Chunk, ElseIfBlock, Expression, Statement, Value } from "./ast.mjs import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; -import { type Token, TokenKind, type TokenStream, token_kind_to_string } from "./lexer.mjs"; +import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; +import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; const UNARY = [ - TokenKind.Not, - TokenKind.Subtract, - TokenKind.Hash, + TokenKindEnum.Not, + TokenKindEnum.Subtract, + TokenKindEnum.Hash, ]; const ORDERS = [ - [TokenKind.Or], - [TokenKind.And], - [TokenKind.LessThan, TokenKind.LessThanEquals, TokenKind.GreaterThan, TokenKind.GreaterThanEquals, TokenKind.Equals, TokenKind.NotEquals], - [TokenKind.BitOr], - [TokenKind.BitAnd], - [TokenKind.BitXOrNot], - [TokenKind.BitShiftLeft, TokenKind.BitShiftRight], - [TokenKind.Concat], - [TokenKind.Addition, TokenKind.Subtract], - [TokenKind.Multiply, TokenKind.Division, TokenKind.FloorDivision, TokenKind.Modulo], - [TokenKind.Exponent], + [TokenKindEnum.Or], + [TokenKindEnum.And], + [TokenKindEnum.LessThan, TokenKindEnum.LessThanEquals, TokenKindEnum.GreaterThan, TokenKindEnum.GreaterThanEquals, TokenKindEnum.Equals, TokenKindEnum.NotEquals], + [TokenKindEnum.BitOr], + [TokenKindEnum.BitAnd], + [TokenKindEnum.BitXOrNot], + [TokenKindEnum.BitShiftLeft, TokenKindEnum.BitShiftRight], + [TokenKindEnum.Concat], + [TokenKindEnum.Addition, TokenKindEnum.Subtract], + [TokenKindEnum.Multiply, TokenKindEnum.Division, TokenKindEnum.FloorDivision, TokenKindEnum.Modulo], + [TokenKindEnum.Exponent], ]; function error(token: Token, message: string): Error @@ -31,7 +32,7 @@ function error(token: Token, message: string): Error ); } -function expect(stream: TokenStream, kind: TokenKind): Token | Error +function expect(stream: TokenStream, kind: TokenKindEnum): Token | Error { const token = stream.peek(); @@ -46,7 +47,7 @@ function expect(stream: TokenStream, kind: TokenKind): Token | Error return stream.next(); } -function consume(stream: TokenStream, kind: TokenKind): boolean +function consume(stream: TokenStream, kind: TokenKindEnum): boolean { const token = stream.peek(); @@ -62,7 +63,7 @@ function consume(stream: TokenStream, kind: TokenKind): boolean function parse_table_key(stream: TokenStream): Expression | Error { - if (consume(stream, TokenKind.OpenSquare)) + if (consume(stream, TokenKindEnum.OpenSquare)) { const element = parse_expression(stream); @@ -71,7 +72,7 @@ function parse_table_key(stream: TokenStream): Expression | Error return element; } - const close_square = expect(stream, TokenKind.CloseSquare); + const close_square = expect(stream, TokenKindEnum.CloseSquare); if (close_square instanceof Error) { @@ -104,7 +105,7 @@ function parse_table_key(stream: TokenStream): Expression | Error function parse_table(stream: TokenStream): Value | Error { - const squigly_open = expect(stream, TokenKind.SquiglyOpen); + const squigly_open = expect(stream, TokenKindEnum.SquiglyOpen); if (squigly_open instanceof Error) { @@ -114,7 +115,7 @@ function parse_table(stream: TokenStream): Value | Error const elements: Map = new Map(); let current_numeric_key = 1; - while (stream.peek().kind !== TokenKind.SquiglyClose) + while (stream.peek().kind !== TokenKindEnum.SquiglyClose) { const element = parse_table_key(stream); @@ -123,7 +124,7 @@ function parse_table(stream: TokenStream): Value | Error return element; } - if (consume(stream, TokenKind.Assign)) + if (consume(stream, TokenKindEnum.Assign)) { const value = parse_expression(stream); @@ -137,7 +138,7 @@ function parse_table(stream: TokenStream): Value | Error else { const key_token = { - kind: TokenKind.NumberLiteral, + kind: TokenKindEnum.NumberLiteral, data: current_numeric_key.toString(), debug: element.token.debug, }; @@ -156,13 +157,13 @@ function parse_table(stream: TokenStream): Value | Error elements.set(key, element); } - if (!consume(stream, TokenKind.Comma)) + if (!consume(stream, TokenKindEnum.Comma)) { break; } } - const close_squigly = expect(stream, TokenKind.SquiglyClose); + const close_squigly = expect(stream, TokenKindEnum.SquiglyClose); if (close_squigly instanceof Error) { @@ -183,20 +184,20 @@ function parse_value(stream: TokenStream): Value | Error // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { - case TokenKind.NumberLiteral: + case TokenKindEnum.NumberLiteral: return { kind: ValueKindEnum.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; - case TokenKind.BooleanLiteral: + case TokenKindEnum.BooleanLiteral: return { kind: ValueKindEnum.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; - case TokenKind.StringLiteral: + case TokenKindEnum.StringLiteral: return { kind: ValueKindEnum.StringLiteral, token: stream.next(), string: token.data }; - case TokenKind.NilLiteral: + case TokenKindEnum.NilLiteral: return { kind: ValueKindEnum.NilLiteral, token: stream.next() }; - case TokenKind.Identifier: + case TokenKindEnum.Identifier: return { kind: ValueKindEnum.Variable, token: stream.next(), identifier: token.data }; - case TokenKind.SquiglyOpen: + case TokenKindEnum.SquiglyOpen: return parse_table(stream); - case TokenKind.Function: + case TokenKindEnum.FunctionLike: return parse_function_value(stream.next(), stream); default: @@ -204,15 +205,15 @@ function parse_value(stream: TokenStream): Value | Error } } -function unary_type_to_expression_kind(kind: TokenKind): ExpressionKind +function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKind { // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (kind) { - case TokenKind.Not: return ExpressionKind.Not; - case TokenKind.Subtract: return ExpressionKind.Negate; - case TokenKind.Hash: return ExpressionKind.Length; - case TokenKind.BitXOrNot: return ExpressionKind.BitNot; + case TokenKindEnum.Not: return ExpressionKind.Not; + case TokenKindEnum.Subtract: return ExpressionKind.Negate; + case TokenKindEnum.Hash: return ExpressionKind.Length; + case TokenKindEnum.BitXOrNot: return ExpressionKind.BitNot; default: throw new Error(); @@ -240,7 +241,7 @@ function parse_unary_operator(stream: TokenStream): Expression | Error function parse_value_expression(stream: TokenStream): Expression | Error { - if (consume(stream, TokenKind.OpenBrace)) + if (consume(stream, TokenKindEnum.OpenBrace)) { const sub_expression = parse_expression(stream); @@ -249,7 +250,7 @@ function parse_value_expression(stream: TokenStream): Expression | Error return sub_expression; } - const close_brace = expect(stream, TokenKind.CloseBrace); + const close_brace = expect(stream, TokenKindEnum.CloseBrace); if (close_brace instanceof Error) { @@ -285,7 +286,7 @@ function parse_call(func: Expression, stream: TokenStream): Expression | Error const open_brace = stream.next(); const args: Array = []; - while (stream.peek().kind !== TokenKind.CloseBrace) + while (stream.peek().kind !== TokenKindEnum.CloseBrace) { const argument = parse_expression(stream); @@ -296,13 +297,13 @@ function parse_call(func: Expression, stream: TokenStream): Expression | Error args.push(argument); - if (!consume(stream, TokenKind.Comma)) + if (!consume(stream, TokenKindEnum.Comma)) { break; } } - const close_brace = expect(stream, TokenKind.CloseBrace); + const close_brace = expect(stream, TokenKindEnum.CloseBrace); if (close_brace instanceof Error) { @@ -327,7 +328,7 @@ function parse_index(table: Expression, stream: TokenStream): Expression | Error return index; } - const close_square = expect(stream, TokenKind.CloseSquare); + const close_square = expect(stream, TokenKindEnum.CloseSquare); if (close_square instanceof Error) { @@ -348,7 +349,7 @@ function parse_index(table: Expression, stream: TokenStream): Expression | Error function parse_dot(table: Expression, stream: TokenStream): Expression | Error { const dot = stream.next(); - const index = expect(stream, TokenKind.Identifier); + const index = expect(stream, TokenKindEnum.Identifier); if (index instanceof Error) { @@ -393,17 +394,17 @@ function parse_access_expression(expression: Expression, stream: TokenStream): E // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (stream.peek().kind) { - case TokenKind.OpenBrace: + case TokenKindEnum.OpenBrace: return parse_call(expression, stream); - case TokenKind.OpenSquare: + case TokenKindEnum.OpenSquare: return parse_index(expression, stream); - case TokenKind.Dot: + case TokenKindEnum.Dot: return parse_dot(expression, stream); - case TokenKind.SquiglyOpen: - case TokenKind.StringLiteral: + case TokenKindEnum.SquiglyOpen: + case TokenKindEnum.StringLiteral: return parse_single_argument_call(expression, stream); } @@ -411,33 +412,33 @@ function parse_access_expression(expression: Expression, stream: TokenStream): E } function operation_type_to_expression_kind( - operation_type: TokenKind + operation_type: TokenKindEnum ): ExpressionKind { // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (operation_type) { - case TokenKind.Addition: return ExpressionKind.Addition; - case TokenKind.Subtract: return ExpressionKind.Subtract; - case TokenKind.Multiply: return ExpressionKind.Multiplication; - case TokenKind.Division: return ExpressionKind.Division; - case TokenKind.FloorDivision: return ExpressionKind.FloorDivision; - case TokenKind.Modulo: return ExpressionKind.Modulo; - case TokenKind.Exponent: return ExpressionKind.Exponent; - case TokenKind.Concat: return ExpressionKind.Concat; - case TokenKind.BitAnd: return ExpressionKind.BitAnd; - case TokenKind.BitOr: return ExpressionKind.BitOr; - case TokenKind.BitXOrNot: return ExpressionKind.BitXOr; - case TokenKind.BitShiftLeft: return ExpressionKind.BitShiftLeft; - case TokenKind.BitShiftRight: return ExpressionKind.BitShiftRight; - case TokenKind.LessThan: return ExpressionKind.LessThan; - case TokenKind.LessThanEquals: return ExpressionKind.LessThanEquals; - case TokenKind.GreaterThan: return ExpressionKind.GreaterThan; - case TokenKind.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; - case TokenKind.Equals: return ExpressionKind.Equals; - case TokenKind.NotEquals: return ExpressionKind.NotEquals; - case TokenKind.And: return ExpressionKind.And; - case TokenKind.Or: return ExpressionKind.Or; + case TokenKindEnum.Addition: return ExpressionKind.Addition; + case TokenKindEnum.Subtract: return ExpressionKind.Subtract; + case TokenKindEnum.Multiply: return ExpressionKind.Multiplication; + case TokenKindEnum.Division: return ExpressionKind.Division; + case TokenKindEnum.FloorDivision: return ExpressionKind.FloorDivision; + case TokenKindEnum.Modulo: return ExpressionKind.Modulo; + case TokenKindEnum.Exponent: return ExpressionKind.Exponent; + case TokenKindEnum.Concat: return ExpressionKind.Concat; + case TokenKindEnum.BitAnd: return ExpressionKind.BitAnd; + case TokenKindEnum.BitOr: return ExpressionKind.BitOr; + case TokenKindEnum.BitXOrNot: return ExpressionKind.BitXOr; + case TokenKindEnum.BitShiftLeft: return ExpressionKind.BitShiftLeft; + case TokenKindEnum.BitShiftRight: return ExpressionKind.BitShiftRight; + case TokenKindEnum.LessThan: return ExpressionKind.LessThan; + case TokenKindEnum.LessThanEquals: return ExpressionKind.LessThanEquals; + case TokenKindEnum.GreaterThan: return ExpressionKind.GreaterThan; + case TokenKindEnum.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; + case TokenKindEnum.Equals: return ExpressionKind.Equals; + case TokenKindEnum.NotEquals: return ExpressionKind.NotEquals; + case TokenKindEnum.And: return ExpressionKind.And; + case TokenKindEnum.Or: return ExpressionKind.Or; default: throw new Error(); @@ -493,7 +494,7 @@ function parse_operation( function parse_expression(stream: TokenStream): Expression | Error { - if (stream.peek().kind === TokenKind.BitXOrNot) + if (stream.peek().kind === TokenKindEnum.BitXOrNot) { return parse_unary_operator(stream); } @@ -528,10 +529,10 @@ function parse_local_statement(local: Token, values: Array): Stateme function parse_assign_or_expression(stream: TokenStream): Statement | Error { - const local = expect(stream, TokenKind.Local); + const local = expect(stream, TokenKindEnum.Local); const lhs: Array = []; - while (lhs.length === 0 || consume(stream, TokenKind.Comma)) + while (lhs.length === 0 || consume(stream, TokenKindEnum.Comma)) { const lvalue = parse_expression(stream); @@ -543,7 +544,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error lhs.push(lvalue); } - const assign = expect(stream, TokenKind.Assign); + const assign = expect(stream, TokenKindEnum.Assign); if (assign instanceof Error) { @@ -558,7 +559,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error const rhs: Array = []; - while (rhs.length === 0 || consume(stream, TokenKind.Comma)) + while (rhs.length === 0 || consume(stream, TokenKindEnum.Comma)) { const rvalue = parse_expression(stream); @@ -583,7 +584,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error function parse_return(stream: TokenStream): Statement | Error { - const ret = expect(stream, TokenKind.Return); + const ret = expect(stream, TokenKindEnum.Return); if (ret instanceof Error) { @@ -592,7 +593,7 @@ function parse_return(stream: TokenStream): Statement | Error const values: Array = []; - while (values.length === 0 || consume(stream, TokenKind.Comma)) + while (values.length === 0 || consume(stream, TokenKindEnum.Comma)) { const value = parse_expression(stream); @@ -632,7 +633,7 @@ function parse_return(stream: TokenStream): Statement | Error function parse_break(stream: TokenStream): Statement | Error { - const break_token = expect(stream, TokenKind.Break); + const break_token = expect(stream, TokenKindEnum.Break); if (break_token instanceof Error) { @@ -646,7 +647,7 @@ function parse_break(stream: TokenStream): Statement | Error function parse_if(stream: TokenStream): Statement | Error { - const if_token = expect(stream, TokenKind.If); + const if_token = expect(stream, TokenKindEnum.If); if (if_token instanceof Error) { @@ -660,14 +661,14 @@ function parse_if(stream: TokenStream): Statement | Error return condition; } - const then = expect(stream, TokenKind.Then); + const then = expect(stream, TokenKindEnum.Then); if (then instanceof Error) { return then; } - const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End); + const body = parse(stream, TokenKindEnum.Else, TokenKindEnum.ElseIf, TokenKindEnum.End); if (body instanceof Error) { @@ -677,7 +678,7 @@ function parse_if(stream: TokenStream): Statement | Error const else_if_bodies: Array = []; let else_body: Chunk | undefined = undefined; - while (consume(stream, TokenKind.ElseIf)) + while (consume(stream, TokenKindEnum.ElseIf)) { const condition = parse_expression(stream); @@ -686,14 +687,14 @@ function parse_if(stream: TokenStream): Statement | Error return condition; } - const then = expect(stream, TokenKind.Then); + const then = expect(stream, TokenKindEnum.Then); if (then instanceof Error) { return then; } - const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else); + const chunk = parse(stream, TokenKindEnum.End, TokenKindEnum.ElseIf, TokenKindEnum.Else); if (chunk instanceof Error) { @@ -707,9 +708,9 @@ function parse_if(stream: TokenStream): Statement | Error }); } - if (consume(stream, TokenKind.Else)) + if (consume(stream, TokenKindEnum.Else)) { - const chunk = parse(stream, TokenKind.End); + const chunk = parse(stream, TokenKindEnum.End); if (chunk instanceof Error) { @@ -719,7 +720,7 @@ function parse_if(stream: TokenStream): Statement | Error else_body = chunk; } - const end = expect(stream, TokenKind.End); + const end = expect(stream, TokenKindEnum.End); if (end instanceof Error) { @@ -740,7 +741,7 @@ function parse_if(stream: TokenStream): Statement | Error function parse_while(stream: TokenStream): Statement | Error { - const while_token = expect(stream, TokenKind.While); + const while_token = expect(stream, TokenKindEnum.While); if (while_token instanceof Error) { @@ -754,21 +755,21 @@ function parse_while(stream: TokenStream): Statement | Error return condition; } - const do_token = expect(stream, TokenKind.Do); + const do_token = expect(stream, TokenKindEnum.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKind.End); + const body = parse(stream, TokenKindEnum.End); if (body instanceof Error) { return body; } - consume(stream, TokenKind.End); + consume(stream, TokenKindEnum.End); return { kind: StatementKindEnum.While, @@ -789,7 +790,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error return start; } - const comma = expect(stream, TokenKind.Comma); + const comma = expect(stream, TokenKindEnum.Comma); if (comma instanceof Error) { @@ -805,7 +806,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error let step: Expression | undefined = undefined; - if (consume(stream, TokenKind.Comma)) + if (consume(stream, TokenKindEnum.Comma)) { const expression = parse_expression(stream); @@ -817,21 +818,21 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error step = expression; } - const do_token = expect(stream, TokenKind.Do); + const do_token = expect(stream, TokenKindEnum.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKind.End); + const body = parse(stream, TokenKindEnum.End); if (body instanceof Error) { return body; } - consume(stream, TokenKind.End); + consume(stream, TokenKindEnum.End); return { kind: StatementKindEnum.NumericFor, @@ -847,7 +848,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error function parse_for(stream: TokenStream): Statement | Error { - const for_token = expect(stream, TokenKind.For); + const for_token = expect(stream, TokenKindEnum.For); if (for_token instanceof Error) { @@ -856,9 +857,9 @@ function parse_for(stream: TokenStream): Statement | Error const items: Array = []; - while (items.length === 0 || consume(stream, TokenKind.Comma)) + while (items.length === 0 || consume(stream, TokenKindEnum.Comma)) { - const item = expect(stream, TokenKind.Identifier); + const item = expect(stream, TokenKindEnum.Identifier); if (item instanceof Error) { @@ -868,7 +869,7 @@ function parse_for(stream: TokenStream): Statement | Error items.push(item); } - if (consume(stream, TokenKind.Assign)) + if (consume(stream, TokenKindEnum.Assign)) { const token = items[0]; @@ -880,7 +881,7 @@ function parse_for(stream: TokenStream): Statement | Error return parse_numeric_for(token, stream); } - const in_token = expect(stream, TokenKind.In); + const in_token = expect(stream, TokenKindEnum.In); if (in_token instanceof Error) { @@ -894,21 +895,21 @@ function parse_for(stream: TokenStream): Statement | Error return iterator; } - const do_token = expect(stream, TokenKind.Do); + const do_token = expect(stream, TokenKindEnum.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKind.End); + const body = parse(stream, TokenKindEnum.End); if (body instanceof Error) { return body; } - consume(stream, TokenKind.End); + consume(stream, TokenKindEnum.End); return { kind: StatementKindEnum.For, @@ -923,21 +924,21 @@ function parse_for(stream: TokenStream): Statement | Error function parse_repeat(stream: TokenStream): Statement | Error { - const repeat_token = expect(stream, TokenKind.Repeat); + const repeat_token = expect(stream, TokenKindEnum.Repeat); if (repeat_token instanceof Error) { return repeat_token; } - const body = parse(stream, TokenKind.Until); + const body = parse(stream, TokenKindEnum.Until); if (body instanceof Error) { return body; } - const until_token = expect(stream, TokenKind.Until); + const until_token = expect(stream, TokenKindEnum.Until); if (until_token instanceof Error) { @@ -963,21 +964,21 @@ function parse_repeat(stream: TokenStream): Statement | Error function parse_do(stream: TokenStream): Statement | Error { - const do_token = expect(stream, TokenKind.Do); + const do_token = expect(stream, TokenKindEnum.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKind.End); + const body = parse(stream, TokenKindEnum.End); if (body instanceof Error) { return body; } - const end_token = expect(stream, TokenKind.End); + const end_token = expect(stream, TokenKindEnum.End); if (end_token instanceof Error) { @@ -995,7 +996,7 @@ function parse_do(stream: TokenStream): Statement | Error function parse_function_params(stream: TokenStream): Array | Error { - const open_brace = expect(stream, TokenKind.OpenBrace); + const open_brace = expect(stream, TokenKindEnum.OpenBrace); if (open_brace instanceof Error) { @@ -1004,9 +1005,9 @@ function parse_function_params(stream: TokenStream): Array | Error const params: Array = []; - while (stream.peek().kind !== TokenKind.CloseBrace) + while (stream.peek().kind !== TokenKindEnum.CloseBrace) { - const param = expect(stream, TokenKind.Identifier); + const param = expect(stream, TokenKindEnum.Identifier); if (param instanceof Error) { @@ -1015,13 +1016,13 @@ function parse_function_params(stream: TokenStream): Array | Error params.push(param); - if (!consume(stream, TokenKind.Comma)) + if (!consume(stream, TokenKindEnum.Comma)) { break; } } - const close_brace = expect(stream, TokenKind.CloseBrace); + const close_brace = expect(stream, TokenKindEnum.CloseBrace); if (close_brace instanceof Error) { @@ -1040,14 +1041,14 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value return params; } - const body = parse(stream, TokenKind.End); + const body = parse(stream, TokenKindEnum.End); if (body instanceof Error) { return body; } - consume(stream, TokenKind.End); + consume(stream, TokenKindEnum.End); return { kind: ValueKindEnum.FunctionLike, @@ -1061,7 +1062,7 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value function parse_local_function(table_name: Token, stream: TokenStream): Statement | Error { - const local_name = expect(stream, TokenKind.Identifier); + const local_name = expect(stream, TokenKindEnum.Identifier); if (local_name instanceof Error) { @@ -1113,21 +1114,21 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement function parse_function(stream: TokenStream): Statement | Error { - const function_token = expect(stream, TokenKind.Function); + const function_token = expect(stream, TokenKindEnum.FunctionLike); if (function_token instanceof Error) { return function_token; } - const name = expect(stream, TokenKind.Identifier); + const name = expect(stream, TokenKindEnum.Identifier); if (name instanceof Error) { return name; } - if (consume(stream, TokenKind.Dot)) + if (consume(stream, TokenKindEnum.Dot)) { return parse_local_function(name, stream); } @@ -1162,38 +1163,38 @@ function parse_function(stream: TokenStream): Statement | Error }; } -function parse_statement(stream: TokenStream, end_tokens: Array): Statement | Error | undefined +function parse_statement(stream: TokenStream, end_tokens: Array): Statement | Error | undefined { const token = stream.peek(); // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { - case TokenKind.Identifier: - case TokenKind.NilLiteral: - case TokenKind.StringLiteral: - case TokenKind.NumberLiteral: - case TokenKind.BooleanLiteral: - case TokenKind.SquiglyOpen: - case TokenKind.Local: + case TokenKindEnum.Identifier: + case TokenKindEnum.NilLiteral: + case TokenKindEnum.StringLiteral: + case TokenKindEnum.NumberLiteral: + case TokenKindEnum.BooleanLiteral: + case TokenKindEnum.SquiglyOpen: + case TokenKindEnum.Local: return parse_assign_or_expression(stream); - case TokenKind.Return: + case TokenKindEnum.Return: return parse_return(stream); - case TokenKind.Break: + case TokenKindEnum.Break: return parse_break(stream); - case TokenKind.If: + case TokenKindEnum.If: return parse_if(stream); - case TokenKind.While: + case TokenKindEnum.While: return parse_while(stream); - case TokenKind.For: + case TokenKindEnum.For: return parse_for(stream); - case TokenKind.Repeat: + case TokenKindEnum.Repeat: return parse_repeat(stream); - case TokenKind.Do: + case TokenKindEnum.Do: return parse_do(stream); - case TokenKind.Function: + case TokenKindEnum.FunctionLike: return parse_function(stream); - case TokenKind.Semicolon: + case TokenKindEnum.Semicolon: stream.next(); return { kind: StatementKindEnum.Empty }; @@ -1218,13 +1219,13 @@ function parse_statement(stream: TokenStream, end_tokens: Array): Sta } } -export function parse(stream: TokenStream, ...end_tokens: Array): Chunk | Error +export function parse(stream: TokenStream, ...end_tokens: Array): Chunk | Error { const chunk: Chunk = { statements: [] }; if (end_tokens.length === 0) { - end_tokens.push(TokenKind.EOF); + end_tokens.push(TokenKindEnum.EOF); } for (;;) From 922285a8ba0e6064cad68672157213b67bc22f1e Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:01:26 +0000 Subject: [PATCH 23/71] fix: Refactored opcode.mjs in multiple files. --- src/compiler.mts | 103 +++++++++--------- src/engine.mts | 10 +- .../definition/interface/op.interface.mts | 11 ++ .../interface/program.interface.mts | 8 ++ .../op-code-name/op-code-name.mts} | 18 +-- 5 files changed, 80 insertions(+), 70 deletions(-) create mode 100644 src/opcode/definition/interface/op.interface.mts create mode 100644 src/opcode/definition/interface/program.interface.mts rename src/{opcode.mts => opcode/op-code-name/op-code-name.mts} (85%) diff --git a/src/compiler.mts b/src/compiler.mts index ceb71af..a94c050 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,6 +1,5 @@ import type { Chunk, Expression, Value } from "./ast.mjs"; import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; -import type { Op, Program } from "./opcode.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; @@ -11,10 +10,12 @@ import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; +import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; +import type { ProgramInterface } from "./opcode/definition/interface/program.interface.mjs"; -function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number +function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { - const ops: Array = []; + const ops: Array = []; ops.push({ code: OpCodeEnum.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); @@ -33,7 +34,7 @@ function compile_function(chunk: Chunk, token: Token, parameters: Array, return functions.length - 1; } -function compile_value(value: Value | undefined, functions: Array>): Array +function compile_value(value: Value | undefined, functions: Array>): Array { if (value === undefined) { @@ -72,7 +73,7 @@ function compile_value(value: Value | undefined, functions: Array>): A case ValueKindEnum.TableLiteral: { - const output: Array = []; + const output: Array = []; output.push({ code: OpCodeEnum.NewTable, debug: debug }); @@ -101,8 +102,8 @@ function compile_value(value: Value | undefined, functions: Array>): A function compile_operation( expression: Expression, operation: OpCodeEnum, - functions: Array> -): Array + functions: Array> +): Array { const { lhs, rhs } = expression; @@ -111,7 +112,7 @@ function compile_operation( throw new Error(); } - const ops: Array = []; + const ops: Array = []; ops.push(...compile_expression(rhs, functions)); ops.push(...compile_expression(lhs, functions)); @@ -123,8 +124,8 @@ function compile_operation( function compile_call( func: Expression | undefined, args: Array | undefined, - functions: Array> -): Array + functions: Array> +): Array { if (func === undefined || args === undefined) { @@ -132,7 +133,7 @@ function compile_call( } const debug = func.token.debug; - const ops: Array = []; + const ops: Array = []; for (const arg of args) { @@ -149,15 +150,15 @@ function compile_call( function compile_index( target: Expression | undefined, index: Expression | undefined, - functions: Array> -): Array + functions: Array> +): Array { if (target === undefined || index === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; ops.push(...compile_expression(index, functions)); ops.push(...compile_expression(target, functions)); @@ -169,15 +170,15 @@ function compile_index( function compile_unary_operation( expression: Expression | undefined, operation: OpCodeEnum, - functions: Array> -): Array + functions: Array> +): Array { if (expression === undefined || expression.expression === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; ops.push(...compile_expression(expression.expression, functions)); ops.push({ code: operation, debug: expression.token.debug }); @@ -185,7 +186,7 @@ function compile_unary_operation( return ops; } -function compile_expression(expression: Expression | undefined, functions: Array>): Array +function compile_expression(expression: Expression | undefined, functions: Array>): Array { if (expression === undefined) { @@ -257,14 +258,14 @@ function compile_expression(expression: Expression | undefined, functions: Array } } -function compile_assignment(assignment: Assignment | undefined, functions: Array>): Array +function compile_assignment(assignment: Assignment | undefined, functions: Array>): Array { if (assignment === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const debug = assignment.token.debug; ops.push({ code: OpCodeEnum.StartStackChange, debug: debug }); @@ -320,7 +321,7 @@ function compile_assignment(assignment: Assignment | undefined, functions: Array return ops; } -function compile_local(local: Local | undefined): Array +function compile_local(local: Local | undefined): Array { if (local === undefined) { @@ -328,7 +329,7 @@ function compile_local(local: Local | undefined): Array } return local.names.map( - (name): Op => + (name): OpInterface => { return { code: OpCodeEnum.MakeLocal, @@ -342,14 +343,14 @@ function compile_local(local: Local | undefined): Array ); } -function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array +function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const debug = condition.token.debug; switch (condition.kind) @@ -389,14 +390,14 @@ function compile_inverted_conditional_jump(condition: Expression | undefined, ju return ops; } -function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array +function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const debug = condition.token.debug; switch (condition.kind) @@ -436,25 +437,25 @@ function compile_conditional_jump(condition: Expression | undefined, jump_by: nu return ops; } -function compile_if(if_block: IfBlock | undefined, functions: Array>): Array +function compile_if(if_block: IfBlock | undefined, functions: Array>): Array { if (if_block === undefined) { throw new Error(); } - const else_chunk: Array = []; + const else_chunk: Array = []; if (if_block.else_body !== undefined) { else_chunk.push(...compile_block(if_block.else_body, functions)); } - const if_else_chunks: Array> = []; + const if_else_chunks: Array> = []; for (const { body, condition, token } of if_block.else_if_bodies.reverse()) { - const ops: Array = []; + const ops: Array = []; const if_else_body = compile_block(body, functions); ops.push(...compile_conditional_jump(condition, if_else_body.length + 1, functions)); @@ -473,7 +474,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): } const debug = if_block.token.debug; - const ops: Array = []; + const ops: Array = []; const body = compile_block(if_block.body, functions); ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); @@ -501,7 +502,7 @@ function compile_if(if_block: IfBlock | undefined, functions: Array>): return ops; } -function replace_breaks(code: Array, offset_from_end: number): void +function replace_breaks(code: Array, offset_from_end: number): void { for (const [i, op] of code.entries()) { @@ -515,7 +516,7 @@ function replace_breaks(code: Array, offset_from_end: number): void } } -function compile_while(while_block: While | undefined, functions: Array>): Array +function compile_while(while_block: While | undefined, functions: Array>): Array { if (while_block === undefined) { @@ -523,7 +524,7 @@ function compile_while(while_block: While | undefined, functions: Array = []; + const ops: Array = []; const body = compile_block(while_block.body, functions); replace_breaks(body, 1); @@ -537,14 +538,14 @@ function compile_while(while_block: While | undefined, functions: Array>): Array +function compile_for(for_block: For | undefined, functions: Array>): Array { if (for_block === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const body = compile_block(for_block.body, functions); replace_breaks(body, 1); @@ -584,7 +585,7 @@ function compile_for(for_block: For | undefined, functions: Array>): A return ops; } -function compile_step(step: Expression | undefined, functions: Array>): Array +function compile_step(step: Expression | undefined, functions: Array>): Array { if (step === undefined) { @@ -594,14 +595,14 @@ function compile_step(step: Expression | undefined, functions: Array>) return compile_expression(step, functions); } -function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Array>): Array +function compile_numeric_for(numeric_for_block: NumericFor | undefined, functions: Array>): Array { if (numeric_for_block === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const body = compile_block(numeric_for_block.body, functions); const step = compile_step(numeric_for_block.step, functions); const index = numeric_for_block.index.data; @@ -632,14 +633,14 @@ function compile_numeric_for(numeric_for_block: NumericFor | undefined, function return ops; } -function compile_repeat(repeat: Repeat | undefined, functions: Array>): Array +function compile_repeat(repeat: Repeat | undefined, functions: Array>): Array { if (repeat === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const debug = repeat.token.debug; ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); @@ -653,14 +654,14 @@ function compile_repeat(repeat: Repeat | undefined, functions: Array>) return ops; } -function compile_do(do_block: Do | undefined, functions: Array>): Array +function compile_do(do_block: Do | undefined, functions: Array>): Array { if (do_block === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; const debug = do_block.token.debug; ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); @@ -670,14 +671,14 @@ function compile_do(do_block: Do | undefined, functions: Array>): Arra return ops; } -function compile_return(return_block: Return | undefined, functions: Array>): Array +function compile_return(return_block: Return | undefined, functions: Array>): Array { if (return_block === undefined) { throw new Error(); } - const ops: Array = []; + const ops: Array = []; for (const value of return_block.values) { @@ -694,11 +695,11 @@ function compile_return(return_block: Return | undefined, functions: Array; + code: Array; has_last_expression: boolean; } -function compile_block(chunk: Chunk, functions: Array>): Array +function compile_block(chunk: Chunk, functions: Array>): Array { const { code, has_last_expression } = compile_chunk(chunk, functions); @@ -710,7 +711,7 @@ function compile_block(chunk: Chunk, functions: Array>): Array return code; } -function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult +function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult { const ops = []; let has_last_expression = false; @@ -780,7 +781,7 @@ function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult }; } -function link(code: Array, function_id: number, location: number): void +function link(code: Array, function_id: number, location: number): void { for (const op of code) { @@ -792,10 +793,10 @@ function link(code: Array, function_id: number, location: number): void } } -export function compile(chunk: Chunk, extend?: Array): Program +export function compile(chunk: Chunk, extend?: Array): ProgramInterface { const ops = [...(extend ?? [])]; - const functions: Array> = []; + const functions: Array> = []; const { code, has_last_expression } = compile_chunk(chunk, functions); const function_locations: Array = []; diff --git a/src/engine.mts b/src/engine.mts index 70102eb..233b8cf 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,7 +1,5 @@ import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; -import type { Op } from "./opcode.mjs"; -import { op_code_name } from "./opcode.mjs"; import { make_boolean, make_number, make_string, make_table } from "./runtime.mjs"; import { TokenStream } from "./lexer.mjs"; import { parse } from "./parser.mjs"; @@ -22,6 +20,8 @@ import type { VariableNativeFunction } from "./variable/definition/interface/var import { equals } from "./variable/equals.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; +import { op_code_name } from "./opcode/op-code-name/op-code-name.mjs"; +import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; function index(val: Variable | undefined): string | number | undefined { @@ -68,7 +68,7 @@ export interface LuaOptions export class Engine { - private program: Array; + private program: Array; private readonly globals: VariableTableMapType; private start_ip: number = 0; @@ -363,7 +363,7 @@ export class Engine } } - private runtime_error(op: Op | undefined, message: string): never + private runtime_error(op: OpInterface | undefined, message: string): never { if (op === undefined) { @@ -373,7 +373,7 @@ export class Engine throw new RuntimeError(message, {}, op.debug); } - private async run_instruction(op: Op): Promise + private async run_instruction(op: OpInterface): Promise { const { code, arg } = op; diff --git a/src/opcode/definition/interface/op.interface.mts b/src/opcode/definition/interface/op.interface.mts new file mode 100644 index 0000000..f3098b3 --- /dev/null +++ b/src/opcode/definition/interface/op.interface.mts @@ -0,0 +1,11 @@ +import type { Variable } from "../../../_index.mjs"; +import type { Debug } from "../../../lexer.mjs"; +import type { OpCodeEnum } from "../enum/op-code.enum.mjs"; + +interface OpInterface { + code: OpCodeEnum; + arg?: Variable; + debug: Debug; +} + +export type { OpInterface }; diff --git a/src/opcode/definition/interface/program.interface.mts b/src/opcode/definition/interface/program.interface.mts new file mode 100644 index 0000000..27897ea --- /dev/null +++ b/src/opcode/definition/interface/program.interface.mts @@ -0,0 +1,8 @@ +import type { OpInterface } from "./op.interface.mjs"; + +interface ProgramInterface { + code: Array; + start: number; +} + +export type { ProgramInterface }; diff --git a/src/opcode.mts b/src/opcode/op-code-name/op-code-name.mts similarity index 85% rename from src/opcode.mts rename to src/opcode/op-code-name/op-code-name.mts index c89222a..7868e04 100644 --- a/src/opcode.mts +++ b/src/opcode/op-code-name/op-code-name.mts @@ -1,8 +1,7 @@ -import type { Variable } from "./variable/definition/type/variable.type.mjs"; -import type { Debug } from "./lexer.mjs"; -import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; +import { OpCodeEnum } from "../definition/enum/op-code.enum.mjs"; -export function op_code_name(op_code: OpCodeEnum): string +// eslint-disable-next-line complexity +function op_code_name(op_code: OpCodeEnum): string { switch (op_code) { @@ -59,13 +58,4 @@ export function op_code_name(op_code: OpCodeEnum): string } } -export interface Op { - code: OpCodeEnum; - arg?: Variable; - debug: Debug; -} - -export interface Program { - code: Array; - start: number; -} +export { op_code_name }; From 4267637b1e4dad9c557ca760482d2e3bb8c6eb6b Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:02:40 +0000 Subject: [PATCH 24/71] fix: Moved LuaFunction to its own file and renamed it LuaFunctionInterface. --- src/ast.mts | 9 ++------- .../definition/interface/lua-function.interface.mts | 10 ++++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 src/ast/definition/interface/lua-function.interface.mts diff --git a/src/ast.mts b/src/ast.mts index a5f175e..4bc7d1e 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,12 +1,7 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; +import type { LuaFunctionInterface } from "./ast/definition/interface/lua-function.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface LuaFunction -{ - parameters: Array; - body: Chunk; -} - export interface Value { kind: ValueKind; @@ -16,7 +11,7 @@ export interface Value boolean?: boolean | undefined; string?: string | undefined; table?: Map | undefined; - function?: LuaFunction | undefined; + function?: LuaFunctionInterface | undefined; identifier?: string | undefined; } diff --git a/src/ast/definition/interface/lua-function.interface.mts b/src/ast/definition/interface/lua-function.interface.mts new file mode 100644 index 0000000..948ea5d --- /dev/null +++ b/src/ast/definition/interface/lua-function.interface.mts @@ -0,0 +1,10 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; + +interface LuaFunctionInterface +{ + parameters: Array; + body: Chunk; +} + +export type { LuaFunctionInterface }; From 5672cb949f0fa22c34cf098d4361286823fdc64b Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:04:47 +0000 Subject: [PATCH 25/71] fix: Moved Value to its own file and renamed it ValueInterface. --- src/ast.mts | 14 ------- .../definition/interface/value.interface.mts | 19 +++++++++ src/compiler.mts | 5 ++- src/optimizer.mts | 41 ++++++++++--------- src/parser.mts | 9 ++-- 5 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 src/ast/definition/interface/value.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 4bc7d1e..fa73fe5 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,20 +1,6 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; -import type { LuaFunctionInterface } from "./ast/definition/interface/lua-function.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Value -{ - kind: ValueKind; - token: Token; - - number?: number | undefined; - boolean?: boolean | undefined; - string?: string | undefined; - table?: Map | undefined; - function?: LuaFunctionInterface | undefined; - identifier?: string | undefined; -} - export interface Expression { kind: ExpressionKind; diff --git a/src/ast/definition/interface/value.interface.mts b/src/ast/definition/interface/value.interface.mts new file mode 100644 index 0000000..a4ff64d --- /dev/null +++ b/src/ast/definition/interface/value.interface.mts @@ -0,0 +1,19 @@ +import type { Expression } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ValueKindEnum } from "../enum/value-kind.enum.mjs"; +import type { LuaFunctionInterface } from "./lua-function.interface.mjs"; + +interface ValueInterface +{ + kind: ValueKindEnum; + token: Token; + + number?: number | undefined; + boolean?: boolean | undefined; + string?: string | undefined; + table?: Map | undefined; + function?: LuaFunctionInterface | undefined; + identifier?: string | undefined; +} + +export type { ValueInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index a94c050..3ccfc5a 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,4 @@ -import type { Chunk, Expression, Value } from "./ast.mjs"; +import type { Chunk, Expression } from "./ast.mjs"; import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; @@ -12,6 +12,7 @@ import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import type { ProgramInterface } from "./opcode/definition/interface/program.interface.mjs"; +import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -34,7 +35,7 @@ function compile_function(chunk: Chunk, token: Token, parameters: Array, return functions.length - 1; } -function compile_value(value: Value | undefined, functions: Array>): Array +function compile_value(value: ValueInterface | undefined, functions: Array>): Array { if (value === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 3ed3a04..98a48ac 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,7 +1,8 @@ -import type { Assignment, Chunk, Expression, For, IfBlock, NumericFor, Repeat, Statement, Value, While } from "./ast.mjs"; +import type { Assignment, Chunk, Expression, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; const CONSTANT_VALUES = [ ValueKindEnum.NilLiteral, @@ -13,8 +14,8 @@ const CONSTANT_VALUES = [ function compute_arithmetic_operation( expression: Expression, operation: (a: number, b: number) => number, - constants: Map -): Value | undefined + constants: Map +): ValueInterface | undefined { const lhs = compute_constant_expression(expression.lhs, constants); const rhs = compute_constant_expression(expression.rhs, constants); @@ -34,8 +35,8 @@ function compute_arithmetic_operation( function compute_comparison_operation( expression: Expression, operation: (a: number | string, b: number | string) => boolean, - constants: Map -): Value | undefined + constants: Map +): ValueInterface | undefined { const lhs = compute_constant_expression(expression.lhs, constants); const rhs = compute_constant_expression(expression.rhs, constants); @@ -55,8 +56,8 @@ function compute_comparison_operation( function compute_logical_operation( expression: Expression, operation: ExpressionKind.And | ExpressionKind.Or, - constants: Map -): Value | undefined + constants: Map +): ValueInterface | undefined { const lhs = compute_constant_expression(expression.lhs, constants); const rhs = compute_constant_expression(expression.rhs, constants); @@ -79,8 +80,8 @@ function compute_logical_operation( // @TODO: Fix complexity warning function compute_constant_expression( expression: Expression | undefined, - constants: Map -): Value | undefined + constants: Map +): ValueInterface | undefined { if (expression === undefined) { @@ -334,7 +335,7 @@ function compute_constant_expression( function optimize_expression( expression: Expression | undefined, - constants: Map + constants: Map ): void { if (expression === undefined) @@ -370,7 +371,7 @@ function optimize_expression( } } -function mark_local_constants(assignment: Assignment, constants: Map): void +function mark_local_constants(assignment: Assignment, constants: Map): void { for (const [index, rhs] of assignment.rhs.entries()) { @@ -408,7 +409,7 @@ function mark_local_constants(assignment: Assignment, constants: Map): void +function unmark_constants_if_reassigned(assignment: Assignment, constants: Map): void { for (const lhs of assignment.lhs) { @@ -430,7 +431,7 @@ function unmark_constants_if_reassigned(assignment: Assignment, constants: Map + constants: Map ): void { if (assignment === undefined) @@ -455,7 +456,7 @@ function optimize_assignment( function remove_constant_local_assignments( chunk: Chunk, - constants: Map + constants: Map ): void { for (const statement of chunk.statements) @@ -494,7 +495,7 @@ function remove_constant_local_assignments( ); } -function optimize_if(if_block: IfBlock | undefined, constants: Map): void +function optimize_if(if_block: IfBlock | undefined, constants: Map): void { if (if_block === undefined) { @@ -505,7 +506,7 @@ function optimize_if(if_block: IfBlock | undefined, constants: Map): void +function optimize_while(while_block: While | undefined, constants: Map): void { if (while_block === undefined) { @@ -516,7 +517,7 @@ function optimize_while(while_block: While | undefined, constants: Map): void +function optimize_for(for_block: For | undefined, constants: Map): void { if (for_block === undefined) { @@ -527,7 +528,7 @@ function optimize_for(for_block: For | undefined, constants: Map) optimize_chunk(for_block.body, constants); } -function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map): void +function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constants: Map): void { if (numeric_for_block === undefined) { @@ -540,7 +541,7 @@ function optimize_numeric_for(numeric_for_block: NumericFor | undefined, constan optimize_chunk(numeric_for_block.body, constants); } -function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void +function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void { if (repeat_block === undefined) { @@ -551,7 +552,7 @@ function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void +export function optimize_chunk(chunk: Chunk, parent_constants?: Map): void { const constants = new Map(parent_constants); diff --git a/src/parser.mts b/src/parser.mts index be8dc87..4b50c1e 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,7 +1,8 @@ -import type { Chunk, ElseIfBlock, Expression, Statement, Value } from "./ast.mjs"; +import type { Chunk, ElseIfBlock, Expression, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; @@ -103,7 +104,7 @@ function parse_table_key(stream: TokenStream): Expression | Error }; } -function parse_table(stream: TokenStream): Value | Error +function parse_table(stream: TokenStream): ValueInterface | Error { const squigly_open = expect(stream, TokenKindEnum.SquiglyOpen); @@ -177,7 +178,7 @@ function parse_table(stream: TokenStream): Value | Error }; } -function parse_value(stream: TokenStream): Value | Error +function parse_value(stream: TokenStream): ValueInterface | Error { const token = stream.peek(); @@ -1032,7 +1033,7 @@ function parse_function_params(stream: TokenStream): Array | Error return params; } -function parse_function_value(function_token: Token, stream: TokenStream): Value | Error +function parse_function_value(function_token: Token, stream: TokenStream): ValueInterface | Error { const params = parse_function_params(stream); From e9e7fbbc8914b3581c1fab6136cbbbd259cf2083 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:06:47 +0000 Subject: [PATCH 26/71] fix: Moved Expression to its own file and renamed it ExpressionInterface. --- src/ast.mts | 38 +++++++------------ .../interface/expression.interface.mts | 17 +++++++++ .../definition/interface/value.interface.mts | 4 +- src/compiler.mts | 23 +++++------ src/optimizer.mts | 15 ++++---- src/parser.mts | 37 +++++++++--------- 6 files changed, 71 insertions(+), 63 deletions(-) create mode 100644 src/ast/definition/interface/expression.interface.mts diff --git a/src/ast.mts b/src/ast.mts index fa73fe5..3744f0a 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,24 +1,12 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; +import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Expression -{ - kind: ExpressionKind; - token: Token; - - lhs?: Expression; - rhs?: Expression; - value?: Value; - expression?: Expression; - index?: Expression; - arguments?: Array; -} - export interface Assignment { local: boolean; - lhs: Array; - rhs: Array; + lhs: Array; + rhs: Array; token: Token; } @@ -31,13 +19,13 @@ export interface Local export interface ElseIfBlock { body: Chunk; - condition: Expression; + condition: ExpressionInterface; token: Token; } export interface IfBlock { - condition: Expression; + condition: ExpressionInterface; body: Chunk; else_if_bodies: Array; else_body?: Chunk | undefined; @@ -46,7 +34,7 @@ export interface IfBlock export interface While { - condition: Expression; + condition: ExpressionInterface; body: Chunk; token: Token; } @@ -54,7 +42,7 @@ export interface While export interface For { items: Array; - iterator: Expression; + iterator: ExpressionInterface; body: Chunk; token: Token; } @@ -62,16 +50,16 @@ export interface For export interface NumericFor { index: Token; - start: Expression; - end: Expression; - step: Expression | undefined; + start: ExpressionInterface; + end: ExpressionInterface; + step: ExpressionInterface | undefined; body: Chunk; } export interface Repeat { body: Chunk; - condition: Expression; + condition: ExpressionInterface; token: Token; } @@ -83,14 +71,14 @@ export interface Do export interface Return { - values: Array; + values: Array; token: Token; } export interface Statement { kind: StatementKindEnum; - expression?: Expression | undefined; + expression?: ExpressionInterface | undefined; assignment?: Assignment | undefined; local?: Local | undefined; if?: IfBlock | undefined; diff --git a/src/ast/definition/interface/expression.interface.mts b/src/ast/definition/interface/expression.interface.mts new file mode 100644 index 0000000..6ab992f --- /dev/null +++ b/src/ast/definition/interface/expression.interface.mts @@ -0,0 +1,17 @@ +import type { TokenStream } from "../../../lexer.mjs"; +import type { ExpressionKind } from "../enum/expression-kind.enum.mjs"; +import type { ValueInterface } from "./value.interface.mjs"; + +interface ExpressionInterface +{ + kind: ExpressionKind; + token: TokenStream; + lhs?: ExpressionInterface; + rhs?: ExpressionInterface; + value?: ValueInterface; + expression?: ExpressionInterface; + index?: ExpressionInterface; + arguments?: Array; +} + +export type { ExpressionInterface }; diff --git a/src/ast/definition/interface/value.interface.mts b/src/ast/definition/interface/value.interface.mts index a4ff64d..2affeb8 100644 --- a/src/ast/definition/interface/value.interface.mts +++ b/src/ast/definition/interface/value.interface.mts @@ -1,6 +1,6 @@ -import type { Expression } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; import type { ValueKindEnum } from "../enum/value-kind.enum.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; import type { LuaFunctionInterface } from "./lua-function.interface.mjs"; interface ValueInterface @@ -11,7 +11,7 @@ interface ValueInterface number?: number | undefined; boolean?: boolean | undefined; string?: string | undefined; - table?: Map | undefined; + table?: Map | undefined; function?: LuaFunctionInterface | undefined; identifier?: string | undefined; } diff --git a/src/compiler.mts b/src/compiler.mts index 3ccfc5a..c9ae4ff 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,4 @@ -import type { Chunk, Expression } from "./ast.mjs"; +import type { Chunk } from "./ast.mjs"; import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Assignment, Local, Return } from "./ast.mjs"; @@ -13,6 +13,7 @@ import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import type { ProgramInterface } from "./opcode/definition/interface/program.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; +import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -101,7 +102,7 @@ function compile_value(value: ValueInterface | undefined, functions: Array> ): Array @@ -123,8 +124,8 @@ function compile_operation( } function compile_call( - func: Expression | undefined, - args: Array | undefined, + func: ExpressionInterface | undefined, + args: Array | undefined, functions: Array> ): Array { @@ -149,8 +150,8 @@ function compile_call( } function compile_index( - target: Expression | undefined, - index: Expression | undefined, + target: ExpressionInterface | undefined, + index: ExpressionInterface | undefined, functions: Array> ): Array { @@ -169,7 +170,7 @@ function compile_index( } function compile_unary_operation( - expression: Expression | undefined, + expression: ExpressionInterface | undefined, operation: OpCodeEnum, functions: Array> ): Array @@ -187,7 +188,7 @@ function compile_unary_operation( return ops; } -function compile_expression(expression: Expression | undefined, functions: Array>): Array +function compile_expression(expression: ExpressionInterface | undefined, functions: Array>): Array { if (expression === undefined) { @@ -344,7 +345,7 @@ function compile_local(local: Local | undefined): Array ); } -function compile_inverted_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array +function compile_inverted_conditional_jump(condition: ExpressionInterface | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) { @@ -391,7 +392,7 @@ function compile_inverted_conditional_jump(condition: Expression | undefined, ju return ops; } -function compile_conditional_jump(condition: Expression | undefined, jump_by: number, functions: Array>): Array +function compile_conditional_jump(condition: ExpressionInterface | undefined, jump_by: number, functions: Array>): Array { if (condition === undefined) { @@ -586,7 +587,7 @@ function compile_for(for_block: For | undefined, functions: Array>): Array +function compile_step(step: ExpressionInterface | undefined, functions: Array>): Array { if (step === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 98a48ac..dbb937f 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,7 +1,8 @@ -import type { Assignment, Chunk, Expression, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; +import type { Assignment, Chunk, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; const CONSTANT_VALUES = [ @@ -12,7 +13,7 @@ const CONSTANT_VALUES = [ ]; function compute_arithmetic_operation( - expression: Expression, + expression: ExpressionInterface, operation: (a: number, b: number) => number, constants: Map ): ValueInterface | undefined @@ -33,7 +34,7 @@ function compute_arithmetic_operation( } function compute_comparison_operation( - expression: Expression, + expression: ExpressionInterface, operation: (a: number | string, b: number | string) => boolean, constants: Map ): ValueInterface | undefined @@ -54,7 +55,7 @@ function compute_comparison_operation( } function compute_logical_operation( - expression: Expression, + expression: ExpressionInterface, operation: ExpressionKind.And | ExpressionKind.Or, constants: Map ): ValueInterface | undefined @@ -79,7 +80,7 @@ function compute_logical_operation( // @TODO: Fix complexity warning function compute_constant_expression( - expression: Expression | undefined, + expression: ExpressionInterface | undefined, constants: Map ): ValueInterface | undefined { @@ -334,7 +335,7 @@ function compute_constant_expression( } function optimize_expression( - expression: Expression | undefined, + expression: ExpressionInterface | undefined, constants: Map ): void { @@ -471,7 +472,7 @@ function remove_constant_local_assignments( for (const name of constants.keys()) { const index = assignment.lhs.findIndex( - (x: Expression): boolean => + (x: ExpressionInterface): boolean => { return x.value?.identifier === name; } diff --git a/src/parser.mts b/src/parser.mts index 4b50c1e..c3a37cf 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,7 +1,8 @@ -import type { Chunk, ElseIfBlock, Expression, Statement } from "./ast.mjs"; +import type { Chunk, ElseIfBlock, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; @@ -62,7 +63,7 @@ function consume(stream: TokenStream, kind: TokenKindEnum): boolean return true; } -function parse_table_key(stream: TokenStream): Expression | Error +function parse_table_key(stream: TokenStream): ExpressionInterface | Error { if (consume(stream, TokenKindEnum.OpenSquare)) { @@ -113,7 +114,7 @@ function parse_table(stream: TokenStream): ValueInterface | Error return squigly_open; } - const elements: Map = new Map(); + const elements: Map = new Map(); let current_numeric_key = 1; while (stream.peek().kind !== TokenKindEnum.SquiglyClose) @@ -221,7 +222,7 @@ function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKind } } -function parse_unary_operator(stream: TokenStream): Expression | Error +function parse_unary_operator(stream: TokenStream): ExpressionInterface | Error { const operator_token = stream.next(); const operator = unary_type_to_expression_kind(operator_token.kind); @@ -240,7 +241,7 @@ function parse_unary_operator(stream: TokenStream): Expression | Error }; } -function parse_value_expression(stream: TokenStream): Expression | Error +function parse_value_expression(stream: TokenStream): ExpressionInterface | Error { if (consume(stream, TokenKindEnum.OpenBrace)) { @@ -282,10 +283,10 @@ function parse_value_expression(stream: TokenStream): Expression | Error return parse_access_expression(expression_value, stream); } -function parse_call(func: Expression, stream: TokenStream): Expression | Error +function parse_call(func: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const open_brace = stream.next(); - const args: Array = []; + const args: Array = []; while (stream.peek().kind !== TokenKindEnum.CloseBrace) { @@ -319,7 +320,7 @@ function parse_call(func: Expression, stream: TokenStream): Expression | Error }, stream); } -function parse_index(table: Expression, stream: TokenStream): Expression | Error +function parse_index(table: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const open_square = stream.next(); const index = parse_expression(stream); @@ -347,7 +348,7 @@ function parse_index(table: Expression, stream: TokenStream): Expression | Error ); } -function parse_dot(table: Expression, stream: TokenStream): Expression | Error +function parse_dot(table: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const dot = stream.next(); const index = expect(stream, TokenKindEnum.Identifier); @@ -373,7 +374,7 @@ function parse_dot(table: Expression, stream: TokenStream): Expression | Error }, stream); } -function parse_single_argument_call(func: Expression, stream: TokenStream): Expression | Error +function parse_single_argument_call(func: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const argument = parse_expression(stream); @@ -390,7 +391,7 @@ function parse_single_argument_call(func: Expression, stream: TokenStream): Expr }; } -function parse_access_expression(expression: Expression, stream: TokenStream): Expression | Error +function parse_access_expression(expression: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (stream.peek().kind) @@ -449,7 +450,7 @@ function operation_type_to_expression_kind( function parse_operation( stream: TokenStream, order: number -): Expression | Error +): ExpressionInterface | Error { if (order >= ORDERS.length) { @@ -493,7 +494,7 @@ function parse_operation( return lhs; } -function parse_expression(stream: TokenStream): Expression | Error +function parse_expression(stream: TokenStream): ExpressionInterface | Error { if (stream.peek().kind === TokenKindEnum.BitXOrNot) { @@ -503,7 +504,7 @@ function parse_expression(stream: TokenStream): Expression | Error return parse_operation(stream, 0); } -function parse_local_statement(local: Token, values: Array): Statement | Error +function parse_local_statement(local: Token, values: Array): Statement | Error { const names: Array = []; @@ -531,7 +532,7 @@ function parse_local_statement(local: Token, values: Array): Stateme function parse_assign_or_expression(stream: TokenStream): Statement | Error { const local = expect(stream, TokenKindEnum.Local); - const lhs: Array = []; + const lhs: Array = []; while (lhs.length === 0 || consume(stream, TokenKindEnum.Comma)) { @@ -558,7 +559,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error return parse_local_statement(local, lhs); } - const rhs: Array = []; + const rhs: Array = []; while (rhs.length === 0 || consume(stream, TokenKindEnum.Comma)) { @@ -592,7 +593,7 @@ function parse_return(stream: TokenStream): Statement | Error return ret; } - const values: Array = []; + const values: Array = []; while (values.length === 0 || consume(stream, TokenKindEnum.Comma)) { @@ -805,7 +806,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error return end; } - let step: Expression | undefined = undefined; + let step: ExpressionInterface | undefined = undefined; if (consume(stream, TokenKindEnum.Comma)) { From 5439b02803e25872da005f1bae4546c0e6dd7f70 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:08:13 +0000 Subject: [PATCH 27/71] fix: Moved Assignment to its own file and renamed it AssignmentInterface. --- src/ast.mts | 11 ++--------- .../definition/interface/assignment.interface.mts | 13 +++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 9 +++++---- 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 src/ast/definition/interface/assignment.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 3744f0a..85e3afe 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,15 +1,8 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; +import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Assignment -{ - local: boolean; - lhs: Array; - rhs: Array; - token: Token; -} - export interface Local { names: Array; @@ -79,7 +72,7 @@ export interface Statement { kind: StatementKindEnum; expression?: ExpressionInterface | undefined; - assignment?: Assignment | undefined; + assignment?: AssignmentInterface | undefined; local?: Local | undefined; if?: IfBlock | undefined; while?: While | undefined; diff --git a/src/ast/definition/interface/assignment.interface.mts b/src/ast/definition/interface/assignment.interface.mts new file mode 100644 index 0000000..9977ca7 --- /dev/null +++ b/src/ast/definition/interface/assignment.interface.mts @@ -0,0 +1,13 @@ +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface AssignmentInterface +{ + local: boolean; + lhs: Array; + rhs: Array; + token: Token; +} + +export type { AssignmentInterface }; + diff --git a/src/compiler.mts b/src/compiler.mts index c9ae4ff..0fc2390 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,7 +1,7 @@ import type { Chunk } from "./ast.mjs"; import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; -import type { Assignment, Local, Return } from "./ast.mjs"; +import type { Local, Return } from "./ast.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; @@ -14,6 +14,7 @@ import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs import type { ProgramInterface } from "./opcode/definition/interface/program.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -260,7 +261,7 @@ function compile_expression(expression: ExpressionInterface | undefined, functio } } -function compile_assignment(assignment: Assignment | undefined, functions: Array>): Array +function compile_assignment(assignment: AssignmentInterface | undefined, functions: Array>): Array { if (assignment === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index dbb937f..dda0fed 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,7 +1,8 @@ -import type { Assignment, Chunk, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; +import type { Chunk, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; @@ -372,7 +373,7 @@ function optimize_expression( } } -function mark_local_constants(assignment: Assignment, constants: Map): void +function mark_local_constants(assignment: AssignmentInterface, constants: Map): void { for (const [index, rhs] of assignment.rhs.entries()) { @@ -410,7 +411,7 @@ function mark_local_constants(assignment: Assignment, constants: Map): void +function unmark_constants_if_reassigned(assignment: AssignmentInterface, constants: Map): void { for (const lhs of assignment.lhs) { @@ -431,7 +432,7 @@ function unmark_constants_if_reassigned(assignment: Assignment, constants: Map ): void { From 89b277f32748bd3c94d78a903586d694f521a152 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:09:46 +0000 Subject: [PATCH 28/71] fix: Moved Local to its own file and renamed it LocalInterface. --- src/ast.mts | 9 ++------- src/ast/definition/interface/local.interface.mts | 9 +++++++++ src/compiler.mts | 5 +++-- 3 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/ast/definition/interface/local.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 85e3afe..f157379 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,14 +1,9 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Local -{ - names: Array; - token: Token; -} - export interface ElseIfBlock { body: Chunk; @@ -73,7 +68,7 @@ export interface Statement kind: StatementKindEnum; expression?: ExpressionInterface | undefined; assignment?: AssignmentInterface | undefined; - local?: Local | undefined; + local?: LocalInterface | undefined; if?: IfBlock | undefined; while?: While | undefined; for?: For | undefined; diff --git a/src/ast/definition/interface/local.interface.mts b/src/ast/definition/interface/local.interface.mts new file mode 100644 index 0000000..69cdd60 --- /dev/null +++ b/src/ast/definition/interface/local.interface.mts @@ -0,0 +1,9 @@ +import type { Token } from "../../../lexer.mjs"; + +interface LocalInterface +{ + names: Array; + token: Token; +} + +export type { LocalInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index 0fc2390..d79b2c6 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,7 +1,7 @@ import type { Chunk } from "./ast.mjs"; import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; -import type { Local, Return } from "./ast.mjs"; +import type { Return } from "./ast.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; @@ -15,6 +15,7 @@ import type { ProgramInterface } from "./opcode/definition/interface/program.int import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; +import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -324,7 +325,7 @@ function compile_assignment(assignment: AssignmentInterface | undefined, functio return ops; } -function compile_local(local: Local | undefined): Array +function compile_local(local: LocalInterface | undefined): Array { if (local === undefined) { From a4fc6e53348bcc943e23a87e77b944510dda84ef Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:11:12 +0000 Subject: [PATCH 29/71] fix: Moved ElseIfBlock to its own file and renamed it ElseIfBlockInterface. --- src/ast.mts | 10 ++-------- .../definition/interface/else-if-block.interface.mts | 12 ++++++++++++ src/parser.mts | 5 +++-- 3 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 src/ast/definition/interface/else-if-block.interface.mts diff --git a/src/ast.mts b/src/ast.mts index f157379..beeff2d 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,21 +1,15 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; +import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface ElseIfBlock -{ - body: Chunk; - condition: ExpressionInterface; - token: Token; -} - export interface IfBlock { condition: ExpressionInterface; body: Chunk; - else_if_bodies: Array; + else_if_bodies: Array; else_body?: Chunk | undefined; token: Token; } diff --git a/src/ast/definition/interface/else-if-block.interface.mts b/src/ast/definition/interface/else-if-block.interface.mts new file mode 100644 index 0000000..3147bfc --- /dev/null +++ b/src/ast/definition/interface/else-if-block.interface.mts @@ -0,0 +1,12 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface ElseIfBlockInterface +{ + body: Chunk; + condition: ExpressionInterface; + token: Token; +} + +export type { ElseIfBlockInterface }; diff --git a/src/parser.mts b/src/parser.mts index c3a37cf..0a7a952 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,7 +1,8 @@ -import type { Chunk, ElseIfBlock, Statement } from "./ast.mjs"; +import type { Chunk, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; @@ -677,7 +678,7 @@ function parse_if(stream: TokenStream): Statement | Error return body; } - const else_if_bodies: Array = []; + const else_if_bodies: Array = []; let else_body: Chunk | undefined = undefined; while (consume(stream, TokenKindEnum.ElseIf)) From de4b7ce88fe00de79c10286227585a35534e8831 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:12:37 +0000 Subject: [PATCH 30/71] fix: Moved IfBlock to its own file and renamed it IfBlockInterface. --- src/ast.mts | 12 ++---------- .../definition/interface/if-block.interface.mts | 15 +++++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 5 +++-- 4 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 src/ast/definition/interface/if-block.interface.mts diff --git a/src/ast.mts b/src/ast.mts index beeff2d..8ab914f 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -2,18 +2,10 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enu import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface IfBlock -{ - condition: ExpressionInterface; - body: Chunk; - else_if_bodies: Array; - else_body?: Chunk | undefined; - token: Token; -} - export interface While { condition: ExpressionInterface; @@ -63,7 +55,7 @@ export interface Statement expression?: ExpressionInterface | undefined; assignment?: AssignmentInterface | undefined; local?: LocalInterface | undefined; - if?: IfBlock | undefined; + if?: IfBlockInterface | undefined; while?: While | undefined; for?: For | undefined; numeric_for?: NumericFor | undefined; diff --git a/src/ast/definition/interface/if-block.interface.mts b/src/ast/definition/interface/if-block.interface.mts new file mode 100644 index 0000000..ef73178 --- /dev/null +++ b/src/ast/definition/interface/if-block.interface.mts @@ -0,0 +1,15 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ElseIfBlockInterface } from "./else-if-block.interface.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface IfBlockInterface +{ + condition: ExpressionInterface; + body: Chunk; + else_if_bodies: Array; + else_body?: Chunk | undefined; + token: Token; +} + +export type { IfBlockInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index d79b2c6..d95fb13 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,5 @@ import type { Chunk } from "./ast.mjs"; -import type { Do, For, IfBlock, NumericFor, Repeat, While } from "./ast.mjs"; +import type { Do, For, NumericFor, Repeat, While } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -16,6 +16,7 @@ import type { ValueInterface } from "./ast/definition/interface/value.interface. import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; +import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -441,7 +442,7 @@ function compile_conditional_jump(condition: ExpressionInterface | undefined, ju return ops; } -function compile_if(if_block: IfBlock | undefined, functions: Array>): Array +function compile_if(if_block: IfBlockInterface | undefined, functions: Array>): Array { if (if_block === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index dda0fed..3f79900 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,9 +1,10 @@ -import type { Chunk, For, IfBlock, NumericFor, Repeat, Statement, While } from "./ast.mjs"; +import type { Chunk, For, NumericFor, Repeat, Statement, While } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; const CONSTANT_VALUES = [ @@ -497,7 +498,7 @@ function remove_constant_local_assignments( ); } -function optimize_if(if_block: IfBlock | undefined, constants: Map): void +function optimize_if(if_block: IfBlockInterface | undefined, constants: Map): void { if (if_block === undefined) { From c70a5b20b9670f62bcc228b6a9527d99ecdb1e7a Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:13:58 +0000 Subject: [PATCH 31/71] fix: Moved While to its own file and renamed it WhileInterface. --- src/ast.mts | 11 ++--------- src/ast/definition/interface/while.interface.mts | 12 ++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 5 +++-- 4 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 src/ast/definition/interface/while.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 8ab914f..e9fcb8b 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,18 +1,11 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; -import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; +import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface While -{ - condition: ExpressionInterface; - body: Chunk; - token: Token; -} - export interface For { items: Array; @@ -56,7 +49,7 @@ export interface Statement assignment?: AssignmentInterface | undefined; local?: LocalInterface | undefined; if?: IfBlockInterface | undefined; - while?: While | undefined; + while?: WhileInterface | undefined; for?: For | undefined; numeric_for?: NumericFor | undefined; repeat?: Repeat | undefined; diff --git a/src/ast/definition/interface/while.interface.mts b/src/ast/definition/interface/while.interface.mts new file mode 100644 index 0000000..0fa2d20 --- /dev/null +++ b/src/ast/definition/interface/while.interface.mts @@ -0,0 +1,12 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface WhileInterface +{ + condition: ExpressionInterface; + body: Chunk; + token: Token; +} + +export type { WhileInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index d95fb13..2917f54 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,5 @@ import type { Chunk } from "./ast.mjs"; -import type { Do, For, NumericFor, Repeat, While } from "./ast.mjs"; +import type { Do, For, NumericFor, Repeat } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -17,6 +17,7 @@ import type { ExpressionInterface } from "./ast/definition/interface/expression. import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; +import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -521,7 +522,7 @@ function replace_breaks(code: Array, offset_from_end: number): void } } -function compile_while(while_block: While | undefined, functions: Array>): Array +function compile_while(while_block: WhileInterface | undefined, functions: Array>): Array { if (while_block === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 3f79900..6c6a02d 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,4 +1,4 @@ -import type { Chunk, For, NumericFor, Repeat, Statement, While } from "./ast.mjs"; +import type { Chunk, For, NumericFor, Repeat, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -6,6 +6,7 @@ import type { AssignmentInterface } from "./ast/definition/interface/assignment. import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; +import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; const CONSTANT_VALUES = [ ValueKindEnum.NilLiteral, @@ -509,7 +510,7 @@ function optimize_if(if_block: IfBlockInterface | undefined, constants: Map): void +function optimize_while(while_block: WhileInterface | undefined, constants: Map): void { if (while_block === undefined) { From 454218683e7c0a8494f7638d5d8de7c87b62cd32 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:15:14 +0000 Subject: [PATCH 32/71] fix: Moved For to its own file and renamed it ForInterface. --- src/ast.mts | 11 ++--------- src/ast/definition/interface/for.interface.mts | 13 +++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 5 +++-- 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 src/ast/definition/interface/for.interface.mts diff --git a/src/ast.mts b/src/ast.mts index e9fcb8b..96a3df4 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,19 +1,12 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface For -{ - items: Array; - iterator: ExpressionInterface; - body: Chunk; - token: Token; -} - export interface NumericFor { index: Token; @@ -50,7 +43,7 @@ export interface Statement local?: LocalInterface | undefined; if?: IfBlockInterface | undefined; while?: WhileInterface | undefined; - for?: For | undefined; + for?: ForInterface | undefined; numeric_for?: NumericFor | undefined; repeat?: Repeat | undefined; do?: Do | undefined; diff --git a/src/ast/definition/interface/for.interface.mts b/src/ast/definition/interface/for.interface.mts new file mode 100644 index 0000000..563175b --- /dev/null +++ b/src/ast/definition/interface/for.interface.mts @@ -0,0 +1,13 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface ForInterface +{ + items: Array; + iterator: ExpressionInterface; + body: Chunk; + token: Token; +} + +export type { ForInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index 2917f54..8fe2c5c 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,5 @@ import type { Chunk } from "./ast.mjs"; -import type { Do, For, NumericFor, Repeat } from "./ast.mjs"; +import type { Do, NumericFor, Repeat } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -18,6 +18,7 @@ import type { AssignmentInterface } from "./ast/definition/interface/assignment. import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; +import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -544,7 +545,7 @@ function compile_while(while_block: WhileInterface | undefined, functions: Array return ops; } -function compile_for(for_block: For | undefined, functions: Array>): Array +function compile_for(for_block: ForInterface | undefined, functions: Array>): Array { if (for_block === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 6c6a02d..52e5e51 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,9 +1,10 @@ -import type { Chunk, For, NumericFor, Repeat, Statement } from "./ast.mjs"; +import type { Chunk, NumericFor, Repeat, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; @@ -521,7 +522,7 @@ function optimize_while(while_block: WhileInterface | undefined, constants: Map< optimize_chunk(while_block.body, constants); } -function optimize_for(for_block: For | undefined, constants: Map): void +function optimize_for(for_block: ForInterface | undefined, constants: Map): void { if (for_block === undefined) { From ded0dba7fca648422b9a8adb3bb4d12d79498c97 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:16:28 +0000 Subject: [PATCH 33/71] fix: Moved NumericFor to its own file and renamed it NumericForInterface. --- src/ast.mts | 12 ++---------- .../definition/interface/numeric-for.interface.mts | 14 ++++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 5 +++-- 4 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 src/ast/definition/interface/numeric-for.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 96a3df4..cc35184 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -4,18 +4,10 @@ import type { ExpressionInterface } from "./ast/definition/interface/expression. import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; +import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface NumericFor -{ - index: Token; - start: ExpressionInterface; - end: ExpressionInterface; - step: ExpressionInterface | undefined; - body: Chunk; -} - export interface Repeat { body: Chunk; @@ -44,7 +36,7 @@ export interface Statement if?: IfBlockInterface | undefined; while?: WhileInterface | undefined; for?: ForInterface | undefined; - numeric_for?: NumericFor | undefined; + numeric_for?: NumericForInterface | undefined; repeat?: Repeat | undefined; do?: Do | undefined; return?: Return | undefined; diff --git a/src/ast/definition/interface/numeric-for.interface.mts b/src/ast/definition/interface/numeric-for.interface.mts new file mode 100644 index 0000000..36f3fd4 --- /dev/null +++ b/src/ast/definition/interface/numeric-for.interface.mts @@ -0,0 +1,14 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface NumericForInterface +{ + index: Token; + start: ExpressionInterface; + end: ExpressionInterface; + step: ExpressionInterface | undefined; + body: Chunk; +} + +export type { NumericForInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index 8fe2c5c..d54343c 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,5 @@ import type { Chunk } from "./ast.mjs"; -import type { Do, NumericFor, Repeat } from "./ast.mjs"; +import type { Do, Repeat } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -19,6 +19,7 @@ import type { LocalInterface } from "./ast/definition/interface/local.interface. import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; +import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -602,7 +603,7 @@ function compile_step(step: ExpressionInterface | undefined, functions: Array>): Array +function compile_numeric_for(numeric_for_block: NumericForInterface | undefined, functions: Array>): Array { if (numeric_for_block === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 52e5e51..17011d6 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,4 +1,4 @@ -import type { Chunk, NumericFor, Repeat, Statement } from "./ast.mjs"; +import type { Chunk, Repeat, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -6,6 +6,7 @@ import type { AssignmentInterface } from "./ast/definition/interface/assignment. import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; +import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; @@ -533,7 +534,7 @@ function optimize_for(for_block: ForInterface | undefined, constants: Map): void +function optimize_numeric_for(numeric_for_block: NumericForInterface | undefined, constants: Map): void { if (numeric_for_block === undefined) { From f48c39b486858518f9ec8b7ee1cccfd91f25408f Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:17:51 +0000 Subject: [PATCH 34/71] fix: Moved Repeat to its own file and renamed it RepeatInterface. --- src/ast.mts | 10 ++-------- src/ast/definition/interface/repeat.interface.mts | 12 ++++++++++++ src/compiler.mts | 5 +++-- src/optimizer.mts | 5 +++-- 4 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 src/ast/definition/interface/repeat.interface.mts diff --git a/src/ast.mts b/src/ast.mts index cc35184..fa2fea4 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -5,16 +5,10 @@ import type { ForInterface } from "./ast/definition/interface/for.interface.mjs" import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; +import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Repeat -{ - body: Chunk; - condition: ExpressionInterface; - token: Token; -} - export interface Do { body: Chunk; @@ -37,7 +31,7 @@ export interface Statement while?: WhileInterface | undefined; for?: ForInterface | undefined; numeric_for?: NumericForInterface | undefined; - repeat?: Repeat | undefined; + repeat?: RepeatInterface | undefined; do?: Do | undefined; return?: Return | undefined; } diff --git a/src/ast/definition/interface/repeat.interface.mts b/src/ast/definition/interface/repeat.interface.mts new file mode 100644 index 0000000..468a1fb --- /dev/null +++ b/src/ast/definition/interface/repeat.interface.mts @@ -0,0 +1,12 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface RepeatInterface +{ + body: Chunk; + condition: ExpressionInterface; + token: Token; +} + +export type { RepeatInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index d54343c..6487238 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,5 @@ import type { Chunk } from "./ast.mjs"; -import type { Do, Repeat } from "./ast.mjs"; +import type { Do } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -20,6 +20,7 @@ import type { IfBlockInterface } from "./ast/definition/interface/if-block.inter import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; +import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -641,7 +642,7 @@ function compile_numeric_for(numeric_for_block: NumericForInterface | undefined, return ops; } -function compile_repeat(repeat: Repeat | undefined, functions: Array>): Array +function compile_repeat(repeat: RepeatInterface | undefined, functions: Array>): Array { if (repeat === undefined) { diff --git a/src/optimizer.mts b/src/optimizer.mts index 17011d6..adb1b3b 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,4 +1,4 @@ -import type { Chunk, Repeat, Statement } from "./ast.mjs"; +import type { Chunk, Statement } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -7,6 +7,7 @@ import type { ExpressionInterface } from "./ast/definition/interface/expression. import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; +import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; @@ -547,7 +548,7 @@ function optimize_numeric_for(numeric_for_block: NumericForInterface | undefined optimize_chunk(numeric_for_block.body, constants); } -function optimize_repeat(repeat_block: Repeat | undefined, constants: Map): void +function optimize_repeat(repeat_block: RepeatInterface | undefined, constants: Map): void { if (repeat_block === undefined) { From 22c039cf574c004774a45f1024868418dce8f216 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:18:58 +0000 Subject: [PATCH 35/71] fix: Moved Do to its own file and renamed it DoInterface. --- src/ast.mts | 9 ++------- src/ast/definition/interface/do.interface.mts | 10 ++++++++++ src/compiler.mts | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/ast/definition/interface/do.interface.mts diff --git a/src/ast.mts b/src/ast.mts index fa2fea4..b6dc492 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,5 +1,6 @@ import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; +import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; @@ -9,12 +10,6 @@ import type { RepeatInterface } from "./ast/definition/interface/repeat.interfac import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; import type { Token } from "./lexer.mjs"; -export interface Do -{ - body: Chunk; - token: Token; -} - export interface Return { values: Array; @@ -32,7 +27,7 @@ export interface Statement for?: ForInterface | undefined; numeric_for?: NumericForInterface | undefined; repeat?: RepeatInterface | undefined; - do?: Do | undefined; + do?: DoInterface | undefined; return?: Return | undefined; } diff --git a/src/ast/definition/interface/do.interface.mts b/src/ast/definition/interface/do.interface.mts new file mode 100644 index 0000000..13ab3c8 --- /dev/null +++ b/src/ast/definition/interface/do.interface.mts @@ -0,0 +1,10 @@ +import type { Chunk } from "../../../ast.mjs"; +import type { Token } from "../../../lexer.mjs"; + +interface DoInterface +{ + body: Chunk; + token: Token; +} + +export type { DoInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index 6487238..7dcbbe3 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,4 @@ import type { Chunk } from "./ast.mjs"; -import type { Do } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import type { Return } from "./ast.mjs"; @@ -21,6 +20,7 @@ import type { WhileInterface } from "./ast/definition/interface/while.interface. import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; +import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -663,7 +663,7 @@ function compile_repeat(repeat: RepeatInterface | undefined, functions: Array>): Array +function compile_do(do_block: DoInterface | undefined, functions: Array>): Array { if (do_block === undefined) { From 866f77f4a288401a693bc4686ce80435b9c78bf7 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:20:28 +0000 Subject: [PATCH 36/71] fix: Moved Return to its own file and renamed it ReturnInterface. --- src/ast.mts | 10 ++-------- src/ast/definition/interface/return.interface.mts | 10 ++++++++++ src/compiler.mts | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 src/ast/definition/interface/return.interface.mts diff --git a/src/ast.mts b/src/ast.mts index b6dc492..2b04132 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -7,14 +7,8 @@ import type { IfBlockInterface } from "./ast/definition/interface/if-block.inter import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; +import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; -import type { Token } from "./lexer.mjs"; - -export interface Return -{ - values: Array; - token: Token; -} export interface Statement { @@ -28,7 +22,7 @@ export interface Statement numeric_for?: NumericForInterface | undefined; repeat?: RepeatInterface | undefined; do?: DoInterface | undefined; - return?: Return | undefined; + return?: ReturnInterface | undefined; } export interface Chunk diff --git a/src/ast/definition/interface/return.interface.mts b/src/ast/definition/interface/return.interface.mts new file mode 100644 index 0000000..4a2676e --- /dev/null +++ b/src/ast/definition/interface/return.interface.mts @@ -0,0 +1,10 @@ +import type { Token } from "../../../lexer.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; + +interface ReturnInterface +{ + values: Array; + token: Token; +} + +export type { ReturnInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index 7dcbbe3..b50fb2b 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,6 +1,5 @@ import type { Chunk } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; -import type { Return } from "./ast.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; @@ -21,6 +20,7 @@ import type { ForInterface } from "./ast/definition/interface/for.interface.mjs" import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; +import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number { @@ -680,7 +680,7 @@ function compile_do(do_block: DoInterface | undefined, functions: Array>): Array +function compile_return(return_block: ReturnInterface | undefined, functions: Array>): Array { if (return_block === undefined) { From 9b62c8e847069e2809d412ec5b7e811edf125dc1 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:22:08 +0000 Subject: [PATCH 37/71] fix: Moved Statement to its own file and renamed it StatementInterface. --- src/ast.mts | 29 ++----------------- .../interface/statement.interface.mts | 28 ++++++++++++++++++ src/optimizer.mts | 5 ++-- src/parser.mts | 29 ++++++++++--------- 4 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 src/ast/definition/interface/statement.interface.mts diff --git a/src/ast.mts b/src/ast.mts index 2b04132..ae64252 100644 --- a/src/ast.mts +++ b/src/ast.mts @@ -1,31 +1,6 @@ -import type { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; -import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; -import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; -import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; -import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; -import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; -import type { LocalInterface } from "./ast/definition/interface/local.interface.mjs"; -import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; -import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; -import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; -import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; - -export interface Statement -{ - kind: StatementKindEnum; - expression?: ExpressionInterface | undefined; - assignment?: AssignmentInterface | undefined; - local?: LocalInterface | undefined; - if?: IfBlockInterface | undefined; - while?: WhileInterface | undefined; - for?: ForInterface | undefined; - numeric_for?: NumericForInterface | undefined; - repeat?: RepeatInterface | undefined; - do?: DoInterface | undefined; - return?: ReturnInterface | undefined; -} +import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; export interface Chunk { - statements: Array; + statements: Array; } diff --git a/src/ast/definition/interface/statement.interface.mts b/src/ast/definition/interface/statement.interface.mts new file mode 100644 index 0000000..f32d795 --- /dev/null +++ b/src/ast/definition/interface/statement.interface.mts @@ -0,0 +1,28 @@ +import type { StatementKindEnum } from "../enum/statement-kind.enum.mjs"; +import type { AssignmentInterface } from "./assignment.interface.mjs"; +import type { DoInterface } from "./do.interface.mjs"; +import type { ExpressionInterface } from "./expression.interface.mjs"; +import type { ForInterface } from "./for.interface.mjs"; +import type { IfBlockInterface } from "./if-block.interface.mjs"; +import type { LocalInterface } from "./local.interface.mjs"; +import type { NumericForInterface } from "./numeric-for.interface.mjs"; +import type { RepeatInterface } from "./repeat.interface.mjs"; +import type { ReturnInterface } from "./return.interface.mjs"; +import type { WhileInterface } from "./while.interface.mjs"; + +interface StatementInterface +{ + kind: StatementKindEnum; + expression?: ExpressionInterface | undefined; + assignment?: AssignmentInterface | undefined; + local?: LocalInterface | undefined; + if?: IfBlockInterface | undefined; + while?: WhileInterface | undefined; + for?: ForInterface | undefined; + numeric_for?: NumericForInterface | undefined; + repeat?: RepeatInterface | undefined; + do?: DoInterface | undefined; + return?: ReturnInterface | undefined; +} + +export type { StatementInterface }; diff --git a/src/optimizer.mts b/src/optimizer.mts index adb1b3b..859ddbc 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,4 +1,4 @@ -import type { Chunk, Statement } from "./ast.mjs"; +import type { Chunk } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -8,6 +8,7 @@ import type { ForInterface } from "./ast/definition/interface/for.interface.mjs" import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; import type { NumericForInterface } from "./ast/definition/interface/numeric-for.interface.mjs"; import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; +import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; @@ -495,7 +496,7 @@ function remove_constant_local_assignments( } chunk.statements = chunk.statements.filter( - (x: Statement): boolean => + (x: StatementInterface): boolean => { return x.assignment === undefined || x.assignment.lhs.length > 0; } diff --git a/src/parser.mts b/src/parser.mts index 0a7a952..348ad3d 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,9 +1,10 @@ -import type { Chunk, Statement } from "./ast.mjs"; +import type { Chunk } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; +import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; @@ -505,7 +506,7 @@ function parse_expression(stream: TokenStream): ExpressionInterface | Error return parse_operation(stream, 0); } -function parse_local_statement(local: Token, values: Array): Statement | Error +function parse_local_statement(local: Token, values: Array): StatementInterface | Error { const names: Array = []; @@ -530,7 +531,7 @@ function parse_local_statement(local: Token, values: Array) }; } -function parse_assign_or_expression(stream: TokenStream): Statement | Error +function parse_assign_or_expression(stream: TokenStream): StatementInterface | Error { const local = expect(stream, TokenKindEnum.Local); const lhs: Array = []; @@ -585,7 +586,7 @@ function parse_assign_or_expression(stream: TokenStream): Statement | Error }; } -function parse_return(stream: TokenStream): Statement | Error +function parse_return(stream: TokenStream): StatementInterface | Error { const ret = expect(stream, TokenKindEnum.Return); @@ -634,7 +635,7 @@ function parse_return(stream: TokenStream): Statement | Error }; } -function parse_break(stream: TokenStream): Statement | Error +function parse_break(stream: TokenStream): StatementInterface | Error { const break_token = expect(stream, TokenKindEnum.Break); @@ -648,7 +649,7 @@ function parse_break(stream: TokenStream): Statement | Error }; } -function parse_if(stream: TokenStream): Statement | Error +function parse_if(stream: TokenStream): StatementInterface | Error { const if_token = expect(stream, TokenKindEnum.If); @@ -742,7 +743,7 @@ function parse_if(stream: TokenStream): Statement | Error }; } -function parse_while(stream: TokenStream): Statement | Error +function parse_while(stream: TokenStream): StatementInterface | Error { const while_token = expect(stream, TokenKindEnum.While); @@ -784,7 +785,7 @@ function parse_while(stream: TokenStream): Statement | Error }; } -function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error +function parse_numeric_for(index: Token, stream: TokenStream): StatementInterface | Error { const start = parse_expression(stream); @@ -849,7 +850,7 @@ function parse_numeric_for(index: Token, stream: TokenStream): Statement | Error }; } -function parse_for(stream: TokenStream): Statement | Error +function parse_for(stream: TokenStream): StatementInterface | Error { const for_token = expect(stream, TokenKindEnum.For); @@ -925,7 +926,7 @@ function parse_for(stream: TokenStream): Statement | Error }; } -function parse_repeat(stream: TokenStream): Statement | Error +function parse_repeat(stream: TokenStream): StatementInterface | Error { const repeat_token = expect(stream, TokenKindEnum.Repeat); @@ -965,7 +966,7 @@ function parse_repeat(stream: TokenStream): Statement | Error }; } -function parse_do(stream: TokenStream): Statement | Error +function parse_do(stream: TokenStream): StatementInterface | Error { const do_token = expect(stream, TokenKindEnum.Do); @@ -1063,7 +1064,7 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value }; } -function parse_local_function(table_name: Token, stream: TokenStream): Statement | Error +function parse_local_function(table_name: Token, stream: TokenStream): StatementInterface | Error { const local_name = expect(stream, TokenKindEnum.Identifier); @@ -1115,7 +1116,7 @@ function parse_local_function(table_name: Token, stream: TokenStream): Statement }; } -function parse_function(stream: TokenStream): Statement | Error +function parse_function(stream: TokenStream): StatementInterface | Error { const function_token = expect(stream, TokenKindEnum.FunctionLike); @@ -1166,7 +1167,7 @@ function parse_function(stream: TokenStream): Statement | Error }; } -function parse_statement(stream: TokenStream, end_tokens: Array): Statement | Error | undefined +function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined { const token = stream.peek(); From caf7082fed3070b681a8745d4e7c9fc22ffabd50 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:24:00 +0000 Subject: [PATCH 38/71] fix: Moved Chunk to its own file and renamed it ChunkInterface. --- src/ast/definition/interface/chunk.interface.mts | 8 ++++++++ src/ast/definition/interface/do.interface.mts | 4 ++-- .../definition/interface/else-if-block.interface.mts | 4 ++-- src/ast/definition/interface/for.interface.mts | 4 ++-- src/ast/definition/interface/if-block.interface.mts | 6 +++--- .../definition/interface/lua-function.interface.mts | 4 ++-- src/ast/definition/interface/numeric-for.interface.mts | 4 ++-- src/ast/definition/interface/repeat.interface.mts | 4 ++-- src/ast/definition/interface/while.interface.mts | 4 ++-- src/compiler.mts | 10 +++++----- src/optimizer.mts | 6 +++--- src/parser.mts | 8 ++++---- 12 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 src/ast/definition/interface/chunk.interface.mts diff --git a/src/ast/definition/interface/chunk.interface.mts b/src/ast/definition/interface/chunk.interface.mts new file mode 100644 index 0000000..12a10f3 --- /dev/null +++ b/src/ast/definition/interface/chunk.interface.mts @@ -0,0 +1,8 @@ +import type { StatementInterface } from "./statement.interface.mjs"; + +interface ChunkInterface +{ + statements: Array; +} + +export type { ChunkInterface }; diff --git a/src/ast/definition/interface/do.interface.mts b/src/ast/definition/interface/do.interface.mts index 13ab3c8..6ebc2e8 100644 --- a/src/ast/definition/interface/do.interface.mts +++ b/src/ast/definition/interface/do.interface.mts @@ -1,9 +1,9 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; interface DoInterface { - body: Chunk; + body: ChunkInterface; token: Token; } diff --git a/src/ast/definition/interface/else-if-block.interface.mts b/src/ast/definition/interface/else-if-block.interface.mts index 3147bfc..3bae0dc 100644 --- a/src/ast/definition/interface/else-if-block.interface.mts +++ b/src/ast/definition/interface/else-if-block.interface.mts @@ -1,10 +1,10 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface ElseIfBlockInterface { - body: Chunk; + body: ChunkInterface; condition: ExpressionInterface; token: Token; } diff --git a/src/ast/definition/interface/for.interface.mts b/src/ast/definition/interface/for.interface.mts index 563175b..68b1744 100644 --- a/src/ast/definition/interface/for.interface.mts +++ b/src/ast/definition/interface/for.interface.mts @@ -1,12 +1,12 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface ForInterface { items: Array; iterator: ExpressionInterface; - body: Chunk; + body: ChunkInterface; token: Token; } diff --git a/src/ast/definition/interface/if-block.interface.mts b/src/ast/definition/interface/if-block.interface.mts index ef73178..91eb704 100644 --- a/src/ast/definition/interface/if-block.interface.mts +++ b/src/ast/definition/interface/if-block.interface.mts @@ -1,14 +1,14 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ElseIfBlockInterface } from "./else-if-block.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface IfBlockInterface { condition: ExpressionInterface; - body: Chunk; + body: ChunkInterface; else_if_bodies: Array; - else_body?: Chunk | undefined; + else_body?: ChunkInterface | undefined; token: Token; } diff --git a/src/ast/definition/interface/lua-function.interface.mts b/src/ast/definition/interface/lua-function.interface.mts index 948ea5d..267f173 100644 --- a/src/ast/definition/interface/lua-function.interface.mts +++ b/src/ast/definition/interface/lua-function.interface.mts @@ -1,10 +1,10 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; interface LuaFunctionInterface { parameters: Array; - body: Chunk; + body: ChunkInterface; } export type { LuaFunctionInterface }; diff --git a/src/ast/definition/interface/numeric-for.interface.mts b/src/ast/definition/interface/numeric-for.interface.mts index 36f3fd4..7b9a3de 100644 --- a/src/ast/definition/interface/numeric-for.interface.mts +++ b/src/ast/definition/interface/numeric-for.interface.mts @@ -1,5 +1,5 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface NumericForInterface @@ -8,7 +8,7 @@ interface NumericForInterface start: ExpressionInterface; end: ExpressionInterface; step: ExpressionInterface | undefined; - body: Chunk; + body: ChunkInterface; } export type { NumericForInterface }; diff --git a/src/ast/definition/interface/repeat.interface.mts b/src/ast/definition/interface/repeat.interface.mts index 468a1fb..09aec44 100644 --- a/src/ast/definition/interface/repeat.interface.mts +++ b/src/ast/definition/interface/repeat.interface.mts @@ -1,10 +1,10 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface RepeatInterface { - body: Chunk; + body: ChunkInterface; condition: ExpressionInterface; token: Token; } diff --git a/src/ast/definition/interface/while.interface.mts b/src/ast/definition/interface/while.interface.mts index 0fa2d20..fe63073 100644 --- a/src/ast/definition/interface/while.interface.mts +++ b/src/ast/definition/interface/while.interface.mts @@ -1,11 +1,11 @@ -import type { Chunk } from "../../../ast.mjs"; import type { Token } from "../../../lexer.mjs"; +import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface WhileInterface { condition: ExpressionInterface; - body: Chunk; + body: ChunkInterface; token: Token; } diff --git a/src/compiler.mts b/src/compiler.mts index b50fb2b..c86d17a 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,3 @@ -import type { Chunk } from "./ast.mjs"; import type { Token } from "./lexer.mjs"; import { make_boolean, make_number, make_string } from "./runtime.mjs"; @@ -21,8 +20,9 @@ import type { NumericForInterface } from "./ast/definition/interface/numeric-for import type { RepeatInterface } from "./ast/definition/interface/repeat.interface.mjs"; import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; +import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; -function compile_function(chunk: Chunk, token: Token, parameters: Array, functions: Array>): number +function compile_function(chunk: ChunkInterface, token: Token, parameters: Array, functions: Array>): number { const ops: Array = []; @@ -708,7 +708,7 @@ interface ChunkResult has_last_expression: boolean; } -function compile_block(chunk: Chunk, functions: Array>): Array +function compile_block(chunk: ChunkInterface, functions: Array>): Array { const { code, has_last_expression } = compile_chunk(chunk, functions); @@ -720,7 +720,7 @@ function compile_block(chunk: Chunk, functions: Array>): Arra return code; } -function compile_chunk(chunk: Chunk, functions: Array>): ChunkResult +function compile_chunk(chunk: ChunkInterface, functions: Array>): ChunkResult { const ops = []; let has_last_expression = false; @@ -802,7 +802,7 @@ function link(code: Array, function_id: number, location: number): } } -export function compile(chunk: Chunk, extend?: Array): ProgramInterface +export function compile(chunk: ChunkInterface, extend?: Array): ProgramInterface { const ops = [...(extend ?? [])]; const functions: Array> = []; diff --git a/src/optimizer.mts b/src/optimizer.mts index 859ddbc..e711a7a 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,8 +1,8 @@ -import type { Chunk } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; +import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { ForInterface } from "./ast/definition/interface/for.interface.mjs"; import type { IfBlockInterface } from "./ast/definition/interface/if-block.interface.mjs"; @@ -463,7 +463,7 @@ function optimize_assignment( } function remove_constant_local_assignments( - chunk: Chunk, + chunk: ChunkInterface, constants: Map ): void { @@ -560,7 +560,7 @@ function optimize_repeat(repeat_block: RepeatInterface | undefined, constants: M optimize_chunk(repeat_block.body, constants); } -export function optimize_chunk(chunk: Chunk, parent_constants?: Map): void +export function optimize_chunk(chunk: ChunkInterface, parent_constants?: Map): void { const constants = new Map(parent_constants); diff --git a/src/parser.mts b/src/parser.mts index 348ad3d..31ce6ea 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1,7 +1,7 @@ -import type { Chunk } from "./ast.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-block.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; @@ -680,7 +680,7 @@ function parse_if(stream: TokenStream): StatementInterface | Error } const else_if_bodies: Array = []; - let else_body: Chunk | undefined = undefined; + let else_body: ChunkInterface | undefined = undefined; while (consume(stream, TokenKindEnum.ElseIf)) { @@ -1223,9 +1223,9 @@ function parse_statement(stream: TokenStream, end_tokens: Array): } } -export function parse(stream: TokenStream, ...end_tokens: Array): Chunk | Error +export function parse(stream: TokenStream, ...end_tokens: Array): ChunkInterface | Error { - const chunk: Chunk = { statements: [] }; + const chunk: ChunkInterface = { statements: [] }; if (end_tokens.length === 0) { From 25ebc91b9c260d7b0cc46ef8984d34e8a8d0bd1c Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:24:27 +0000 Subject: [PATCH 39/71] fix: Deleted ast.mts as it became empty. --- src/ast.mts | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/ast.mts diff --git a/src/ast.mts b/src/ast.mts deleted file mode 100644 index ae64252..0000000 --- a/src/ast.mts +++ /dev/null @@ -1,6 +0,0 @@ -import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; - -export interface Chunk -{ - statements: Array; -} From 3f4039f2968613a8c137e9e583588d18016fd139 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:27:22 +0000 Subject: [PATCH 40/71] fix: Moved index to its own file. --- src/engine.mts | 21 +-------------------- src/engine/index/index.mts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 src/engine/index/index.mts diff --git a/src/engine.mts b/src/engine.mts index 233b8cf..fd8be6d 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -22,26 +22,7 @@ import { assertVariable } from "./variable/predicate/assert-variable.mjs"; import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; import { op_code_name } from "./opcode/op-code-name/op-code-name.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; - -function index(val: Variable | undefined): string | number | undefined -{ - if (val === undefined) - { - return undefined; - } - - if (val.data_type === VariableKind.String) - { - return val.string; - } - - if (val.data_type === VariableKind.Number) - { - return val.number; - } - - return undefined; -} +import { index } from "./engine/index/index.mjs"; function is_true(val: Variable | undefined): boolean { diff --git a/src/engine/index/index.mts b/src/engine/index/index.mts new file mode 100644 index 0000000..d62286c --- /dev/null +++ b/src/engine/index/index.mts @@ -0,0 +1,23 @@ +import { type Variable, VariableKind } from "../../_index.mjs"; + +function index(value: Variable | undefined): string | number | undefined +{ + if (value === undefined) + { + return undefined; + } + + if (value.data_type === VariableKind.String) + { + return value.string; + } + + if (value.data_type === VariableKind.Number) + { + return value.number; + } + + return undefined; +} + +export { index }; From ffe98e7c7fbb9647553ff7b60378308e9a563ea4 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:28:13 +0000 Subject: [PATCH 41/71] fix: Moved LuaOptions to its own file and renamed it LuaOptionsInterface. --- src/engine.mts | 15 ++++----------- src/engine/interface/lua-options.interface.mts | 11 +++++++++++ 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 src/engine/interface/lua-options.interface.mts diff --git a/src/engine.mts b/src/engine.mts index fd8be6d..7f24844 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -23,6 +23,7 @@ import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; import { op_code_name } from "./opcode/op-code-name/op-code-name.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import { index } from "./engine/index/index.mjs"; +import type { LuaOptionsInterface } from "./engine/interface/lua-options.interface.mjs"; function is_true(val: Variable | undefined): boolean { @@ -39,14 +40,6 @@ function is_true(val: Variable | undefined): boolean return true; } -export interface LuaOptions -{ - trace?: boolean; - trace_instructions?: boolean; - trace_stack?: boolean; - locals?: Map; -} - export class Engine { private program: Array; @@ -213,7 +206,7 @@ export class Engine return return_values; } - async run_for_steps(steps: number, options?: LuaOptions): Promise + async run_for_steps(steps: number, options?: LuaOptionsInterface): Promise { if (this.error !== undefined) { @@ -247,7 +240,7 @@ export class Engine return this.stack_get(0); } - async run(options?: LuaOptions): Promise + async run(options?: LuaOptionsInterface): Promise { const result = await this.run_for_steps(1000, options); @@ -914,7 +907,7 @@ export class Engine return undefined; } - async step(options?: LuaOptions): Promise + async step(options?: LuaOptionsInterface): Promise { if (this.error !== undefined) { diff --git a/src/engine/interface/lua-options.interface.mts b/src/engine/interface/lua-options.interface.mts new file mode 100644 index 0000000..205e62e --- /dev/null +++ b/src/engine/interface/lua-options.interface.mts @@ -0,0 +1,11 @@ +import type { Variable } from "../../_index.mjs"; + +interface LuaOptionsInterface +{ + trace?: boolean; + trace_instructions?: boolean; + trace_stack?: boolean; + locals?: Map; +} + +export type { LuaOptionsInterface }; From 80cf03181656f02cfab412e2b46da41ee429083f Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:28:48 +0000 Subject: [PATCH 42/71] fix: Made Engine class constructor scope explicit. --- src/engine.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine.mts b/src/engine.mts index 7f24844..28a5e29 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -55,7 +55,7 @@ export class Engine private error: Error | undefined; - constructor( + public constructor( script?: string, globals?: VariableTableMapType ) From cac07146bef09a3875d9e9e6718ace61b405195f Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:30:58 +0000 Subject: [PATCH 43/71] fix: Moved token_kind_to_string to its own file. --- src/lexer.mts | 59 ----------------- .../token-kind-to-string.mts | 63 +++++++++++++++++++ src/parser.mts | 3 +- 3 files changed, 65 insertions(+), 60 deletions(-) create mode 100644 src/lexer/token-kind-to-string/token-kind-to-string.mts diff --git a/src/lexer.mts b/src/lexer.mts index ed33385..c3388fe 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,65 +1,6 @@ import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; -export function token_kind_to_string(kind: TokenKindEnum): string -{ - switch (kind) - { - case TokenKindEnum.EOF: return "EOF"; - case TokenKindEnum.NotFinished: return "NotFinished"; - case TokenKindEnum.Identifier: return "Identifier"; - case TokenKindEnum.StringLiteral: return "StringLiteral"; - case TokenKindEnum.BooleanLiteral: return "BooleanLiteral"; - case TokenKindEnum.NumberLiteral: return "NumberLiteral"; - case TokenKindEnum.NilLiteral: return "nil"; - case TokenKindEnum.OpenBrace: return "("; - case TokenKindEnum.CloseBrace: return ")"; - case TokenKindEnum.OpenSquare: return "["; - case TokenKindEnum.CloseSquare: return "]"; - case TokenKindEnum.SquiglyOpen: return "{"; - case TokenKindEnum.SquiglyClose: return "}"; - case TokenKindEnum.Addition: return "+"; - case TokenKindEnum.Subtract: return "-"; - case TokenKindEnum.Multiply: return "*"; - case TokenKindEnum.Division: return "/"; - case TokenKindEnum.FloorDivision: return "//"; - case TokenKindEnum.Modulo: return "%"; - case TokenKindEnum.Exponent: return "^"; - case TokenKindEnum.BitAnd: return "&"; - case TokenKindEnum.BitOr: return "|"; - case TokenKindEnum.BitXOrNot: return "~"; - case TokenKindEnum.BitShiftLeft: return "<<"; - case TokenKindEnum.BitShiftRight: return ">>"; - case TokenKindEnum.LessThan: return "<"; - case TokenKindEnum.GreaterThan: return ">"; - case TokenKindEnum.And: return "and"; - case TokenKindEnum.Or: return "or"; - case TokenKindEnum.Not: return "not"; - case TokenKindEnum.Assign: return "="; - case TokenKindEnum.Semicolon: return ";"; - case TokenKindEnum.Comma: return ","; - case TokenKindEnum.Dot: return "."; - case TokenKindEnum.FunctionLike: return "function"; - case TokenKindEnum.If: return "if"; - case TokenKindEnum.While: return "while"; - case TokenKindEnum.For: return "for"; - case TokenKindEnum.Repeat: return "repeat"; - case TokenKindEnum.In: return "in"; - case TokenKindEnum.Do: return "do"; - case TokenKindEnum.Then: return "then"; - case TokenKindEnum.ElseIf: return "elseif"; - case TokenKindEnum.Else: return "else"; - case TokenKindEnum.Until: return "until"; - case TokenKindEnum.End: return "end"; - case TokenKindEnum.Return: return "return"; - case TokenKindEnum.Break: return "break"; - case TokenKindEnum.Local: return "local"; - - default: - return "unknown token"; - } -} - export interface Debug { line: number; column: number; diff --git a/src/lexer/token-kind-to-string/token-kind-to-string.mts b/src/lexer/token-kind-to-string/token-kind-to-string.mts new file mode 100644 index 0000000..792ff3b --- /dev/null +++ b/src/lexer/token-kind-to-string/token-kind-to-string.mts @@ -0,0 +1,63 @@ +import { TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; + +// eslint-disable-next-line complexity +function token_kind_to_string(kind: TokenKindEnum): string +{ + switch (kind) + { + case TokenKindEnum.EOF: return "EOF"; + case TokenKindEnum.NotFinished: return "NotFinished"; + case TokenKindEnum.Identifier: return "Identifier"; + case TokenKindEnum.StringLiteral: return "StringLiteral"; + case TokenKindEnum.BooleanLiteral: return "BooleanLiteral"; + case TokenKindEnum.NumberLiteral: return "NumberLiteral"; + case TokenKindEnum.NilLiteral: return "nil"; + case TokenKindEnum.OpenBrace: return "("; + case TokenKindEnum.CloseBrace: return ")"; + case TokenKindEnum.OpenSquare: return "["; + case TokenKindEnum.CloseSquare: return "]"; + case TokenKindEnum.SquiglyOpen: return "{"; + case TokenKindEnum.SquiglyClose: return "}"; + case TokenKindEnum.Addition: return "+"; + case TokenKindEnum.Subtract: return "-"; + case TokenKindEnum.Multiply: return "*"; + case TokenKindEnum.Division: return "/"; + case TokenKindEnum.FloorDivision: return "//"; + case TokenKindEnum.Modulo: return "%"; + case TokenKindEnum.Exponent: return "^"; + case TokenKindEnum.BitAnd: return "&"; + case TokenKindEnum.BitOr: return "|"; + case TokenKindEnum.BitXOrNot: return "~"; + case TokenKindEnum.BitShiftLeft: return "<<"; + case TokenKindEnum.BitShiftRight: return ">>"; + case TokenKindEnum.LessThan: return "<"; + case TokenKindEnum.GreaterThan: return ">"; + case TokenKindEnum.And: return "and"; + case TokenKindEnum.Or: return "or"; + case TokenKindEnum.Not: return "not"; + case TokenKindEnum.Assign: return "="; + case TokenKindEnum.Semicolon: return ";"; + case TokenKindEnum.Comma: return ","; + case TokenKindEnum.Dot: return "."; + case TokenKindEnum.FunctionLike: return "function"; + case TokenKindEnum.If: return "if"; + case TokenKindEnum.While: return "while"; + case TokenKindEnum.For: return "for"; + case TokenKindEnum.Repeat: return "repeat"; + case TokenKindEnum.In: return "in"; + case TokenKindEnum.Do: return "do"; + case TokenKindEnum.Then: return "then"; + case TokenKindEnum.ElseIf: return "elseif"; + case TokenKindEnum.Else: return "else"; + case TokenKindEnum.Until: return "until"; + case TokenKindEnum.End: return "end"; + case TokenKindEnum.Return: return "return"; + case TokenKindEnum.Break: return "break"; + case TokenKindEnum.Local: return "local"; + + default: + return "unknown token"; + } +} + +export { token_kind_to_string }; diff --git a/src/parser.mts b/src/parser.mts index 31ce6ea..d910240 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -6,8 +6,9 @@ import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-bl import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; -import { type Token, type TokenStream, token_kind_to_string } from "./lexer.mjs"; +import type { Token, TokenStream } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; +import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; const UNARY = [ TokenKindEnum.Not, From 3ab62a5f23c0d036648c523bae8a051cefbfdcd0 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:32:47 +0000 Subject: [PATCH 44/71] fix: Moved Debug to its own file and renamed it DebugInterface. --- src/lexer.mts | 10 +++------- src/lexer/definition/interface/debug.interface.mts | 6 ++++++ src/opcode/definition/interface/op.interface.mts | 4 ++-- src/runtime-error.mts | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 src/lexer/definition/interface/debug.interface.mts diff --git a/src/lexer.mts b/src/lexer.mts index c3388fe..15a0433 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,15 +1,11 @@ import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; - -export interface Debug { - line: number; - column: number; -} +import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; export interface Token { data: string; kind: TokenKindEnum; - debug: Debug; + debug: DebugInterface; } const single_token_map: Map = new Map([ @@ -85,7 +81,7 @@ export class TokenStream private state: StateEnum; private end_of_stream: boolean = false; private buffer: string; - private token_start_debug: Debug; + private token_start_debug: DebugInterface; private line: number; private column: number; diff --git a/src/lexer/definition/interface/debug.interface.mts b/src/lexer/definition/interface/debug.interface.mts new file mode 100644 index 0000000..4217c25 --- /dev/null +++ b/src/lexer/definition/interface/debug.interface.mts @@ -0,0 +1,6 @@ +interface DebugInterface { + line: number; + column: number; +} + +export type { DebugInterface }; diff --git a/src/opcode/definition/interface/op.interface.mts b/src/opcode/definition/interface/op.interface.mts index f3098b3..be755fc 100644 --- a/src/opcode/definition/interface/op.interface.mts +++ b/src/opcode/definition/interface/op.interface.mts @@ -1,11 +1,11 @@ import type { Variable } from "../../../_index.mjs"; -import type { Debug } from "../../../lexer.mjs"; +import type { DebugInterface } from "../../../lexer/definition/interface/debug.interface.mjs"; import type { OpCodeEnum } from "../enum/op-code.enum.mjs"; interface OpInterface { code: OpCodeEnum; arg?: Variable; - debug: Debug; + debug: DebugInterface; } export type { OpInterface }; diff --git a/src/runtime-error.mts b/src/runtime-error.mts index a27c8b4..7e4568d 100644 --- a/src/runtime-error.mts +++ b/src/runtime-error.mts @@ -1,11 +1,11 @@ -import type { Debug } from "./lexer.mjs"; +import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; class RuntimeError extends Error { protected readonly line: number | undefined = undefined; protected readonly column: number | undefined = undefined; - public constructor(message: string, options?: ErrorOptions, debug?: Debug) + public constructor(message: string, options?: ErrorOptions, debug?: DebugInterface) { super(message, options); From 14b061dd537b65a5c5ed9caa13972b297da697cd Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:35:17 +0000 Subject: [PATCH 45/71] fix: Moved Token to its own file and renamed it TokenInterface. --- .../interface/assignment.interface.mts | 4 ++-- src/ast/definition/interface/do.interface.mts | 4 ++-- .../interface/else-if-block.interface.mts | 4 ++-- .../definition/interface/for.interface.mts | 6 ++--- .../interface/if-block.interface.mts | 4 ++-- .../definition/interface/local.interface.mts | 6 ++--- .../interface/lua-function.interface.mts | 4 ++-- .../interface/numeric-for.interface.mts | 4 ++-- .../definition/interface/repeat.interface.mts | 4 ++-- .../definition/interface/return.interface.mts | 4 ++-- .../definition/interface/value.interface.mts | 5 ++-- .../definition/interface/while.interface.mts | 4 ++-- src/compiler.mts | 5 ++-- src/lexer.mts | 15 ++++-------- .../definition/interface/token.interface.mts | 10 ++++++++ src/parser.mts | 23 ++++++++++--------- 16 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 src/lexer/definition/interface/token.interface.mts diff --git a/src/ast/definition/interface/assignment.interface.mts b/src/ast/definition/interface/assignment.interface.mts index 9977ca7..6ce4e9c 100644 --- a/src/ast/definition/interface/assignment.interface.mts +++ b/src/ast/definition/interface/assignment.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface AssignmentInterface @@ -6,7 +6,7 @@ interface AssignmentInterface local: boolean; lhs: Array; rhs: Array; - token: Token; + token: TokenInterface; } export type { AssignmentInterface }; diff --git a/src/ast/definition/interface/do.interface.mts b/src/ast/definition/interface/do.interface.mts index 6ebc2e8..9b16b23 100644 --- a/src/ast/definition/interface/do.interface.mts +++ b/src/ast/definition/interface/do.interface.mts @@ -1,10 +1,10 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; interface DoInterface { body: ChunkInterface; - token: Token; + token: TokenInterface; } export type { DoInterface }; diff --git a/src/ast/definition/interface/else-if-block.interface.mts b/src/ast/definition/interface/else-if-block.interface.mts index 3bae0dc..c76b077 100644 --- a/src/ast/definition/interface/else-if-block.interface.mts +++ b/src/ast/definition/interface/else-if-block.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; @@ -6,7 +6,7 @@ interface ElseIfBlockInterface { body: ChunkInterface; condition: ExpressionInterface; - token: Token; + token: TokenInterface; } export type { ElseIfBlockInterface }; diff --git a/src/ast/definition/interface/for.interface.mts b/src/ast/definition/interface/for.interface.mts index 68b1744..476b163 100644 --- a/src/ast/definition/interface/for.interface.mts +++ b/src/ast/definition/interface/for.interface.mts @@ -1,13 +1,13 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface ForInterface { - items: Array; + items: Array; iterator: ExpressionInterface; body: ChunkInterface; - token: Token; + token: TokenInterface; } export type { ForInterface }; diff --git a/src/ast/definition/interface/if-block.interface.mts b/src/ast/definition/interface/if-block.interface.mts index 91eb704..1c73079 100644 --- a/src/ast/definition/interface/if-block.interface.mts +++ b/src/ast/definition/interface/if-block.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ElseIfBlockInterface } from "./else-if-block.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; @@ -9,7 +9,7 @@ interface IfBlockInterface body: ChunkInterface; else_if_bodies: Array; else_body?: ChunkInterface | undefined; - token: Token; + token: TokenInterface; } export type { IfBlockInterface }; diff --git a/src/ast/definition/interface/local.interface.mts b/src/ast/definition/interface/local.interface.mts index 69cdd60..785a001 100644 --- a/src/ast/definition/interface/local.interface.mts +++ b/src/ast/definition/interface/local.interface.mts @@ -1,9 +1,9 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; interface LocalInterface { - names: Array; - token: Token; + names: Array; + token: TokenInterface; } export type { LocalInterface }; diff --git a/src/ast/definition/interface/lua-function.interface.mts b/src/ast/definition/interface/lua-function.interface.mts index 267f173..f819cf6 100644 --- a/src/ast/definition/interface/lua-function.interface.mts +++ b/src/ast/definition/interface/lua-function.interface.mts @@ -1,9 +1,9 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; interface LuaFunctionInterface { - parameters: Array; + parameters: Array; body: ChunkInterface; } diff --git a/src/ast/definition/interface/numeric-for.interface.mts b/src/ast/definition/interface/numeric-for.interface.mts index 7b9a3de..b6e43d0 100644 --- a/src/ast/definition/interface/numeric-for.interface.mts +++ b/src/ast/definition/interface/numeric-for.interface.mts @@ -1,10 +1,10 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface NumericForInterface { - index: Token; + index: TokenInterface; start: ExpressionInterface; end: ExpressionInterface; step: ExpressionInterface | undefined; diff --git a/src/ast/definition/interface/repeat.interface.mts b/src/ast/definition/interface/repeat.interface.mts index 09aec44..a260fdf 100644 --- a/src/ast/definition/interface/repeat.interface.mts +++ b/src/ast/definition/interface/repeat.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; @@ -6,7 +6,7 @@ interface RepeatInterface { body: ChunkInterface; condition: ExpressionInterface; - token: Token; + token: TokenInterface; } export type { RepeatInterface }; diff --git a/src/ast/definition/interface/return.interface.mts b/src/ast/definition/interface/return.interface.mts index 4a2676e..4ad5020 100644 --- a/src/ast/definition/interface/return.interface.mts +++ b/src/ast/definition/interface/return.interface.mts @@ -1,10 +1,10 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; interface ReturnInterface { values: Array; - token: Token; + token: TokenInterface; } export type { ReturnInterface }; diff --git a/src/ast/definition/interface/value.interface.mts b/src/ast/definition/interface/value.interface.mts index 2affeb8..7617408 100644 --- a/src/ast/definition/interface/value.interface.mts +++ b/src/ast/definition/interface/value.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ValueKindEnum } from "../enum/value-kind.enum.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; import type { LuaFunctionInterface } from "./lua-function.interface.mjs"; @@ -6,8 +6,7 @@ import type { LuaFunctionInterface } from "./lua-function.interface.mjs"; interface ValueInterface { kind: ValueKindEnum; - token: Token; - + token: TokenInterface; number?: number | undefined; boolean?: boolean | undefined; string?: string | undefined; diff --git a/src/ast/definition/interface/while.interface.mts b/src/ast/definition/interface/while.interface.mts index fe63073..d2e0f3e 100644 --- a/src/ast/definition/interface/while.interface.mts +++ b/src/ast/definition/interface/while.interface.mts @@ -1,4 +1,4 @@ -import type { Token } from "../../../lexer.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ChunkInterface } from "./chunk.interface.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; @@ -6,7 +6,7 @@ interface WhileInterface { condition: ExpressionInterface; body: ChunkInterface; - token: Token; + token: TokenInterface; } export type { WhileInterface }; diff --git a/src/compiler.mts b/src/compiler.mts index c86d17a..8a3f01a 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,5 +1,3 @@ -import type { Token } from "./lexer.mjs"; - import { make_boolean, make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; @@ -21,8 +19,9 @@ import type { RepeatInterface } from "./ast/definition/interface/repeat.interfac import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; +import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; -function compile_function(chunk: ChunkInterface, token: Token, parameters: Array, functions: Array>): number +function compile_function(chunk: ChunkInterface, token: TokenInterface, parameters: Array, functions: Array>): number { const ops: Array = []; diff --git a/src/lexer.mts b/src/lexer.mts index 15a0433..7568b6a 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,12 +1,7 @@ import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; - -export interface Token { - data: string; - kind: TokenKindEnum; - debug: DebugInterface; -} +import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; const single_token_map: Map = new Map([ ["(", TokenKindEnum.OpenBrace], @@ -76,7 +71,7 @@ const keyword_map: Map = new Map([ export class TokenStream { private readonly processing_stream: Array; - private readonly peek_queue: Array; + private readonly peek_queue: Array; private state: StateEnum; private end_of_stream: boolean = false; @@ -573,17 +568,17 @@ this.consume(); this.end_of_stream = false; } - next(): Token + next(): TokenInterface { if (this.peek_queue.length === 0) { this.peek(); } - return this.peek_queue.shift() as Token; + return this.peek_queue.shift() as TokenInterface; } - peek(count = 1): Token + peek(count = 1): TokenInterface { while (this.peek_queue.length < count) { diff --git a/src/lexer/definition/interface/token.interface.mts b/src/lexer/definition/interface/token.interface.mts new file mode 100644 index 0000000..b49c97f --- /dev/null +++ b/src/lexer/definition/interface/token.interface.mts @@ -0,0 +1,10 @@ +import type { TokenKindEnum } from "../enum/token-kind.enum.mjs"; +import type { DebugInterface } from "./debug.interface.mjs"; + +interface TokenInterface { + data: string; + kind: TokenKindEnum; + debug: DebugInterface; +} + +export type { TokenInterface }; diff --git a/src/parser.mts b/src/parser.mts index d910240..f2ae8b2 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -6,8 +6,9 @@ import type { ElseIfBlockInterface } from "./ast/definition/interface/else-if-bl import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; import type { StatementInterface } from "./ast/definition/interface/statement.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; -import type { Token, TokenStream } from "./lexer.mjs"; +import type { TokenStream } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; +import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; const UNARY = [ @@ -30,14 +31,14 @@ const ORDERS = [ [TokenKindEnum.Exponent], ]; -function error(token: Token, message: string): Error +function error(token: TokenInterface, message: string): Error { return new Error( `${token.debug.line.toFixed(0)}:${token.debug.column.toFixed(0)}: ${message}` ); } -function expect(stream: TokenStream, kind: TokenKindEnum): Token | Error +function expect(stream: TokenStream, kind: TokenKindEnum): TokenInterface | Error { const token = stream.peek(); @@ -507,9 +508,9 @@ function parse_expression(stream: TokenStream): ExpressionInterface | Error return parse_operation(stream, 0); } -function parse_local_statement(local: Token, values: Array): StatementInterface | Error +function parse_local_statement(local: TokenInterface, values: Array): StatementInterface | Error { - const names: Array = []; + const names: Array = []; for (const expression of values) { @@ -786,7 +787,7 @@ function parse_while(stream: TokenStream): StatementInterface | Error }; } -function parse_numeric_for(index: Token, stream: TokenStream): StatementInterface | Error +function parse_numeric_for(index: TokenInterface, stream: TokenStream): StatementInterface | Error { const start = parse_expression(stream); @@ -860,7 +861,7 @@ function parse_for(stream: TokenStream): StatementInterface | Error return for_token; } - const items: Array = []; + const items: Array = []; while (items.length === 0 || consume(stream, TokenKindEnum.Comma)) { @@ -999,7 +1000,7 @@ function parse_do(stream: TokenStream): StatementInterface | Error }; } -function parse_function_params(stream: TokenStream): Array | Error +function parse_function_params(stream: TokenStream): Array | Error { const open_brace = expect(stream, TokenKindEnum.OpenBrace); @@ -1008,7 +1009,7 @@ function parse_function_params(stream: TokenStream): Array | Error return open_brace; } - const params: Array = []; + const params: Array = []; while (stream.peek().kind !== TokenKindEnum.CloseBrace) { @@ -1037,7 +1038,7 @@ function parse_function_params(stream: TokenStream): Array | Error return params; } -function parse_function_value(function_token: Token, stream: TokenStream): ValueInterface | Error +function parse_function_value(function_token: TokenInterface, stream: TokenStream): ValueInterface | Error { const params = parse_function_params(stream); @@ -1065,7 +1066,7 @@ function parse_function_value(function_token: Token, stream: TokenStream): Value }; } -function parse_local_function(table_name: Token, stream: TokenStream): StatementInterface | Error +function parse_local_function(table_name: TokenInterface, stream: TokenStream): StatementInterface | Error { const local_name = expect(stream, TokenKindEnum.Identifier); From 4978cf28b3a662bf179bcc7bca65154ffe128769 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:35:49 +0000 Subject: [PATCH 46/71] fix: Made TokenStream class constructor scope explicit. --- src/lexer.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer.mts b/src/lexer.mts index 7568b6a..fdfcfb5 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -81,7 +81,7 @@ export class TokenStream private line: number; private column: number; - constructor() + public constructor() { this.state = StateEnum.Initial; this.processing_stream = []; From fbf102f12c1d9cde2054a6268be0fcdef8faa10f Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:37:25 +0000 Subject: [PATCH 47/71] fix: Moved table_size to its own file. --- src/engine.mts | 3 ++- src/lib.mts | 18 +----------------- src/lib/table-size/table-size.mts | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 src/lib/table-size/table-size.mts diff --git a/src/engine.mts b/src/engine.mts index 28a5e29..38e86db 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -24,6 +24,7 @@ import { op_code_name } from "./opcode/op-code-name/op-code-name.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import { index } from "./engine/index/index.mjs"; import type { LuaOptionsInterface } from "./engine/interface/lua-options.interface.mjs"; +import { table_size } from "./lib/table-size/table-size.mjs"; function is_true(val: Variable | undefined): boolean { @@ -696,7 +697,7 @@ export class Engine this.stack.push(make_number(variable.string.length)); break; case VariableKind.Table: - this.stack.push(make_number(std.table_size(variable))); + this.stack.push(make_number(table_size(variable))); break; default: diff --git a/src/lib.mts b/src/lib.mts index 62c0ae6..ea9efbb 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -16,6 +16,7 @@ import type { VariableNativeFunction } from "./variable/definition/interface/var import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; import { RuntimeError } from "./runtime-error.mjs"; +import { table_size } from "./lib/table-size/table-size.mjs"; function optional_parameter( expected_kind: K, @@ -34,23 +35,6 @@ function optional_parameter( return value as VariableValueType; } -export function table_size(value: VariableTable): number -{ - let size: number = 0; - - for (let i: number = 1; i < value.table.size; ++i) - { - if (!value.table.has(i)) - { - return size; - } - - ++size; - } - - return size; -} - export function variable_to_string(variable: Variable, tables_done: Array = []): string { switch (variable.data_type) diff --git a/src/lib/table-size/table-size.mts b/src/lib/table-size/table-size.mts new file mode 100644 index 0000000..30dcfcc --- /dev/null +++ b/src/lib/table-size/table-size.mts @@ -0,0 +1,20 @@ +import type { VariableTable } from "../../_index.mjs"; + +function table_size(value: VariableTable): number +{ + let size: number = 0; + + for (let i: number = 1; i < value.table.size; ++i) + { + if (!value.table.has(i)) + { + return size; + } + + ++size; + } + + return size; +} + +export { table_size }; From c10f87b92159e4c6c697a682397fcf3f4862080d Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:41:11 +0000 Subject: [PATCH 48/71] fix: Moved variable_to_string to its own file. --- src/engine.mts | 9 ++-- src/lib.mts | 40 +---------------- .../variable-to-string/variable-to-string.mts | 43 +++++++++++++++++++ 3 files changed, 49 insertions(+), 43 deletions(-) create mode 100644 src/lib/variable-to-string/variable-to-string.mts diff --git a/src/engine.mts b/src/engine.mts index 38e86db..2ddb889 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -25,6 +25,7 @@ import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs import { index } from "./engine/index/index.mjs"; import type { LuaOptionsInterface } from "./engine/interface/lua-options.interface.mjs"; import { table_size } from "./lib/table-size/table-size.mjs"; +import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; function is_true(val: Variable | undefined): boolean { @@ -111,7 +112,7 @@ export class Engine for (const [i, op] of this.program.entries()) { - const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : ""; + const arg = op.arg !== undefined ? variable_to_string(op.arg) : ""; if (i === this.ip) { @@ -579,7 +580,7 @@ export class Engine const x = this.stack_pop(); const y = this.stack_pop(); - const result = std.variable_to_string(x) + std.variable_to_string(y); + const result = variable_to_string(x) + variable_to_string(y); this.stack.push(make_string(result)); break; @@ -931,7 +932,7 @@ export class Engine if (options?.trace || options?.trace_instructions) { - const arg = op.arg !== undefined ? std.variable_to_string(op.arg) : ""; + const arg = op.arg !== undefined ? variable_to_string(op.arg) : ""; console.log(this.ip - 1, op_code_name(op.code), arg); } @@ -950,7 +951,7 @@ export class Engine ...this.stack.map( (x) => { - return std.variable_to_string(x); + return variable_to_string(x); } ) ); diff --git a/src/lib.mts b/src/lib.mts index ea9efbb..2103129 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -17,6 +17,7 @@ import type { NativeFunction } from "./boundary/definition/type/native-function. import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; import { RuntimeError } from "./runtime-error.mjs"; import { table_size } from "./lib/table-size/table-size.mjs"; +import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; function optional_parameter( expected_kind: K, @@ -35,45 +36,6 @@ function optional_parameter( return value as VariableValueType; } -export function variable_to_string(variable: Variable, tables_done: Array = []): string -{ - switch (variable.data_type) - { - case VariableKind.Nil: - return "nil"; - case VariableKind.Boolean: - return variable.boolean ? "true" : "false"; - case VariableKind.Number: - return variable.number.toString(); - case VariableKind.String: - return variable.string; - case VariableKind.Function: - return ``; - case VariableKind.NativeFunction: - return ``; - case VariableKind.Table: - { - if (tables_done.includes(variable)) - { - return "..."; - } - - tables_done.push(variable); - - const items: Array = []; - - for (const [key, value] of variable.table.entries()) - { - const item: string = `${String(key)} = ${variable_to_string(value, tables_done)}`; - - items.push(item); - } - - return `{ ${items.join(", ")} }`; - } - } -} - function print(_: Engine, ...args: Array): Array { // eslint-disable-next-line no-console diff --git a/src/lib/variable-to-string/variable-to-string.mts b/src/lib/variable-to-string/variable-to-string.mts new file mode 100644 index 0000000..eb79120 --- /dev/null +++ b/src/lib/variable-to-string/variable-to-string.mts @@ -0,0 +1,43 @@ +import type { Variable } from "../../_index.mjs"; +import { VariableKind } from "../../variable/definition/enum/variable-kind.enum.mjs"; + +function variable_to_string(variable: Variable, tables_done: Array = []): string +{ + switch (variable.data_type) + { + case VariableKind.Nil: + return "nil"; + case VariableKind.Boolean: + return variable.boolean ? "true" : "false"; + case VariableKind.Number: + return variable.number.toString(); + case VariableKind.String: + return variable.string; + case VariableKind.Function: + return ``; + case VariableKind.NativeFunction: + return ``; + case VariableKind.Table: + { + if (tables_done.includes(variable)) + { + return "..."; + } + + tables_done.push(variable); + + const items: Array = []; + + for (const [key, value] of variable.table.entries()) + { + const item: string = `${String(key)} = ${variable_to_string(value, tables_done)}`; + + items.push(item); + } + + return `{ ${items.join(", ")} }`; + } + } +} + +export { variable_to_string }; From 60caf68d0150b1f737e5f99900a1974bb02c4aa5 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:42:38 +0000 Subject: [PATCH 49/71] fix: Moved print to its own file. --- src/lib.mts | 14 +------------- src/lib/print/print.mts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 src/lib/print/print.mts diff --git a/src/lib.mts b/src/lib.mts index 2103129..7fe27ce 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -18,6 +18,7 @@ import type { VariableTableMapType } from "./variable/definition/type/variable-t import { RuntimeError } from "./runtime-error.mjs"; import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; +import { print } from "./lib/print/print.mjs"; function optional_parameter( expected_kind: K, @@ -36,19 +37,6 @@ function optional_parameter( return value as VariableValueType; } -function print(_: Engine, ...args: Array): Array -{ - // eslint-disable-next-line no-console - console.log(...args.map( - (arg): string => - { - return variable_to_string(arg); - } - )); - - return [nil]; -} - function type(_: Engine, variable: Variable): Array { return [make_string(variable.data_type)]; diff --git a/src/lib/print/print.mts b/src/lib/print/print.mts new file mode 100644 index 0000000..87bd42d --- /dev/null +++ b/src/lib/print/print.mts @@ -0,0 +1,19 @@ +import { type Variable, nil } from "../../_index.mjs"; +import type { Engine } from "../../engine.mjs"; +import { variable_to_string } from "../variable-to-string/variable-to-string.mjs"; + +// @ts-expect-error - engine is unused for now. +function print(engine: Engine, ...args: Array): Array +{ + // eslint-disable-next-line no-console + console.log(...args.map( + (arg): string => + { + return variable_to_string(arg); + } + )); + + return [nil]; +} + +export { print }; From 51c98c3bc71070dd3d77d19eb6f039bc44a6b2b9 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:44:43 +0000 Subject: [PATCH 50/71] fix: Moved type to its own file. --- src/lib.mts | 6 +----- src/lib/type/type.mts | 11 +++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/lib/type/type.mts diff --git a/src/lib.mts b/src/lib.mts index 7fe27ce..b7b3b24 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -19,6 +19,7 @@ import { RuntimeError } from "./runtime-error.mjs"; import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; import { print } from "./lib/print/print.mjs"; +import { type } from "./lib/type/type.mjs"; function optional_parameter( expected_kind: K, @@ -37,11 +38,6 @@ function optional_parameter( return value as VariableValueType; } -function type(_: Engine, variable: Variable): Array -{ - return [make_string(variable.data_type)]; -} - function inext(_: Engine, table: Variable, previous_index: Variable): Array { assertVariableKind(table, VariableKind.Table); diff --git a/src/lib/type/type.mts b/src/lib/type/type.mts new file mode 100644 index 0000000..3785547 --- /dev/null +++ b/src/lib/type/type.mts @@ -0,0 +1,11 @@ +import type { Variable } from "../../_index.mjs"; +import type { Engine } from "../../engine.mjs"; +import { make_string } from "../../runtime.mjs"; + +// @ts-expect-error - engine is unused for now. +function type(engine: Engine, variable: Variable): Array +{ + return [make_string(variable.data_type)]; +} + +export { type }; From ca43a873574fe617a669db3a2a2f30e1e40c4092 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:47:15 +0000 Subject: [PATCH 51/71] fix: Moved make_boolean to its own file. --- src/compiler.mts | 3 ++- src/engine.mts | 3 ++- src/lib.mts | 3 ++- src/runtime.mts | 7 +------ src/runtime/make-boolean/make-boolean.mts | 8 ++++++++ 5 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 src/runtime/make-boolean/make-boolean.mts diff --git a/src/compiler.mts b/src/compiler.mts index 8a3f01a..abb9c87 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,4 @@ -import { make_boolean, make_number, make_string } from "./runtime.mjs"; +import { make_number, make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -20,6 +20,7 @@ import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; +import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; function compile_function(chunk: ChunkInterface, token: TokenInterface, parameters: Array, functions: Array>): number { diff --git a/src/engine.mts b/src/engine.mts index 2ddb889..1d3dd34 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,6 +1,6 @@ import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; -import { make_boolean, make_number, make_string, make_table } from "./runtime.mjs"; +import { make_number, make_string, make_table } from "./runtime.mjs"; import { TokenStream } from "./lexer.mjs"; import { parse } from "./parser.mjs"; import { compile } from "./compiler.mjs"; @@ -26,6 +26,7 @@ import { index } from "./engine/index/index.mjs"; import type { LuaOptionsInterface } from "./engine/interface/lua-options.interface.mjs"; import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; +import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; function is_true(val: Variable | undefined): boolean { diff --git a/src/lib.mts b/src/lib.mts index b7b3b24..a8f3cee 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -5,7 +5,7 @@ import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs"; import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs"; import { nil } from "./variable/nil.mjs"; import type { Engine } from "./engine.mjs"; -import { make_boolean, make_number, make_string, make_variable } from "./runtime.mjs"; +import { make_number, make_string, make_variable } from "./runtime.mjs"; import { ValidationError, assertArray, assertPopulatedArray, isCallable, isInteger, unary } from "@vitruvius-labs/ts-predicate"; import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; @@ -20,6 +20,7 @@ import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; import { print } from "./lib/print/print.mjs"; import { type } from "./lib/type/type.mjs"; +import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; function optional_parameter( expected_kind: K, diff --git a/src/runtime.mts b/src/runtime.mts index ee3166a..9b8f542 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -9,7 +9,6 @@ import { } from "@vitruvius-labs/ts-predicate"; import type { Variable } from "./variable/definition/type/variable.type.mjs"; -import type { VariableBoolean } from "./variable/definition/interface/variable-boolean.interface.mjs"; import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import type { VariableString } from "./variable/definition/interface/variable-string.interface.mjs"; import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; @@ -20,11 +19,7 @@ import { isVariable } from "./variable/predicate/is-variable.mjs"; import { nil } from "./variable/nil.mjs"; import { isTableInputType } from "./boundary/predicate/is-table-input-type.mjs"; import { isTableMapKeyType } from "./boundary/predicate/is-table-map-key-type.mjs"; - -export function make_boolean(boolean: boolean): VariableBoolean -{ - return { data_type: VariableKind.Boolean, boolean: boolean }; -} +import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; export function make_number(number: number): VariableNumber { diff --git a/src/runtime/make-boolean/make-boolean.mts b/src/runtime/make-boolean/make-boolean.mts new file mode 100644 index 0000000..aabe5d2 --- /dev/null +++ b/src/runtime/make-boolean/make-boolean.mts @@ -0,0 +1,8 @@ +import { type VariableBoolean, VariableKind } from "../../_index.mjs"; + +function make_boolean(boolean: boolean): VariableBoolean +{ + return { data_type: VariableKind.Boolean, boolean: boolean }; +} + +export { make_boolean }; From 53a1d6eb9c921706654c9643d959dc531090b37f Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:49:17 +0000 Subject: [PATCH 52/71] fix: Moved make_number to its own file. --- src/compiler.mts | 3 ++- src/engine.mts | 3 ++- src/lib.mts | 3 ++- src/runtime.mts | 7 +------ src/runtime/make-number/make-number.mts | 8 ++++++++ 5 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 src/runtime/make-number/make-number.mts diff --git a/src/compiler.mts b/src/compiler.mts index abb9c87..d89e695 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,4 @@ -import { make_number, make_string } from "./runtime.mjs"; +import { make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -21,6 +21,7 @@ import type { ReturnInterface } from "./ast/definition/interface/return.interfac import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; +import { make_number } from "./runtime/make-number/make-number.mjs"; function compile_function(chunk: ChunkInterface, token: TokenInterface, parameters: Array, functions: Array>): number { diff --git a/src/engine.mts b/src/engine.mts index 1d3dd34..fd09fa5 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,6 +1,6 @@ import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; -import { make_number, make_string, make_table } from "./runtime.mjs"; +import { make_string, make_table } from "./runtime.mjs"; import { TokenStream } from "./lexer.mjs"; import { parse } from "./parser.mjs"; import { compile } from "./compiler.mjs"; @@ -27,6 +27,7 @@ import type { LuaOptionsInterface } from "./engine/interface/lua-options.interfa import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; +import { make_number } from "./runtime/make-number/make-number.mjs"; function is_true(val: Variable | undefined): boolean { diff --git a/src/lib.mts b/src/lib.mts index a8f3cee..acc9677 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -5,7 +5,7 @@ import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs"; import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs"; import { nil } from "./variable/nil.mjs"; import type { Engine } from "./engine.mjs"; -import { make_number, make_string, make_variable } from "./runtime.mjs"; +import { make_string, make_variable } from "./runtime.mjs"; import { ValidationError, assertArray, assertPopulatedArray, isCallable, isInteger, unary } from "@vitruvius-labs/ts-predicate"; import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; @@ -21,6 +21,7 @@ import { variable_to_string } from "./lib/variable-to-string/variable-to-string. import { print } from "./lib/print/print.mjs"; import { type } from "./lib/type/type.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; +import { make_number } from "./runtime/make-number/make-number.mjs"; function optional_parameter( expected_kind: K, diff --git a/src/runtime.mts b/src/runtime.mts index 9b8f542..b4d3bc8 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -9,7 +9,6 @@ import { } from "@vitruvius-labs/ts-predicate"; import type { Variable } from "./variable/definition/type/variable.type.mjs"; -import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import type { VariableString } from "./variable/definition/interface/variable-string.interface.mjs"; import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; import type { TableInputType } from "./boundary/definition/type/table-input.type.mjs"; @@ -20,11 +19,7 @@ import { nil } from "./variable/nil.mjs"; import { isTableInputType } from "./boundary/predicate/is-table-input-type.mjs"; import { isTableMapKeyType } from "./boundary/predicate/is-table-map-key-type.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; - -export function make_number(number: number): VariableNumber -{ - return { data_type: VariableKind.Number, number: number }; -} +import { make_number } from "./runtime/make-number/make-number.mjs"; export function make_string(string: string): VariableString { diff --git a/src/runtime/make-number/make-number.mts b/src/runtime/make-number/make-number.mts new file mode 100644 index 0000000..686a434 --- /dev/null +++ b/src/runtime/make-number/make-number.mts @@ -0,0 +1,8 @@ +import { VariableKind, type VariableNumber } from "../../_index.mjs"; + +function make_number(number: number): VariableNumber +{ + return { data_type: VariableKind.Number, number: number }; +} + +export { make_number }; From a4aa20e0f538cff125ebec561826917ec5dba380 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:51:26 +0000 Subject: [PATCH 53/71] fix: Moved make_string to its own file. --- src/compiler.mts | 2 +- src/engine.mts | 3 ++- src/lib.mts | 3 ++- src/runtime.mts | 6 +----- src/runtime/make-string/make-string.mts | 8 ++++++++ 5 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 src/runtime/make-string/make-string.mts diff --git a/src/compiler.mts b/src/compiler.mts index d89e695..b1f045e 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,4 +1,3 @@ -import { make_string } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; @@ -22,6 +21,7 @@ import type { ChunkInterface } from "./ast/definition/interface/chunk.interface. import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; +import { make_string } from "./runtime/make-string/make-string.mjs"; function compile_function(chunk: ChunkInterface, token: TokenInterface, parameters: Array, functions: Array>): number { diff --git a/src/engine.mts b/src/engine.mts index fd09fa5..da23144 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -1,6 +1,6 @@ import { assertUnion, unary } from "@vitruvius-labs/ts-predicate"; -import { make_string, make_table } from "./runtime.mjs"; +import { make_table } from "./runtime.mjs"; import { TokenStream } from "./lexer.mjs"; import { parse } from "./parser.mjs"; import { compile } from "./compiler.mjs"; @@ -28,6 +28,7 @@ import { table_size } from "./lib/table-size/table-size.mjs"; import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; +import { make_string } from "./runtime/make-string/make-string.mjs"; function is_true(val: Variable | undefined): boolean { diff --git a/src/lib.mts b/src/lib.mts index acc9677..20c3491 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -5,7 +5,7 @@ import { isVariableKind } from "./variable/predicate/is-variable-kind.mjs"; import { assertVariableKind } from "./variable/predicate/assert-variable-kind.mjs"; import { nil } from "./variable/nil.mjs"; import type { Engine } from "./engine.mjs"; -import { make_string, make_variable } from "./runtime.mjs"; +import { make_variable } from "./runtime.mjs"; import { ValidationError, assertArray, assertPopulatedArray, isCallable, isInteger, unary } from "@vitruvius-labs/ts-predicate"; import type { VariableNumber } from "./variable/definition/interface/variable-number.interface.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; @@ -22,6 +22,7 @@ import { print } from "./lib/print/print.mjs"; import { type } from "./lib/type/type.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; +import { make_string } from "./runtime/make-string/make-string.mjs"; function optional_parameter( expected_kind: K, diff --git a/src/runtime.mts b/src/runtime.mts index b4d3bc8..2802c1d 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -20,11 +20,7 @@ import { isTableInputType } from "./boundary/predicate/is-table-input-type.mjs"; import { isTableMapKeyType } from "./boundary/predicate/is-table-map-key-type.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; - -export function make_string(string: string): VariableString -{ - return { data_type: VariableKind.String, string: string }; -} +import { make_string } from "./runtime/make-string/make-string.mjs"; export function make_table(input?: TableInputType): VariableTable { diff --git a/src/runtime/make-string/make-string.mts b/src/runtime/make-string/make-string.mts new file mode 100644 index 0000000..b69d97a --- /dev/null +++ b/src/runtime/make-string/make-string.mts @@ -0,0 +1,8 @@ +import { VariableKind, type VariableString } from "../../_index.mjs"; + +function make_string(string: string): VariableString +{ + return { data_type: VariableKind.String, string: string }; +} + +export { make_string }; From c67b5458489b5b9114811dcda70f7937cf1189ed Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:57:07 +0000 Subject: [PATCH 54/71] fix: Moved handle_error to its own file and slightly improve its syntax. --- src/create-binding.mts | 21 ++++--------------- .../handle-error/handle-error.mts | 19 +++++++++++++++++ src/runtime.mts | 1 - 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 src/create-binding/handle-error/handle-error.mts diff --git a/src/create-binding.mts b/src/create-binding.mts index 1b38e88..3ad7c24 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -7,6 +7,7 @@ import type { NativeFunction } from "./boundary/definition/type/native-function. import { RuntimeError } from "./runtime-error.mjs"; import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; +import { handle_error } from "./create-binding/handle-error/handle-error.mjs"; export const ParameterOptionEnum = { REQUIRED: "required", @@ -68,24 +69,10 @@ function sanitize_parameters(input: Array, descriptors: Array) => unknown, parameters_descriptor: Array): VariableNativeFunction { - if (isInstanceOf(error, RuntimeError) || isInstanceOf(error, ValidationError)) - { - throw error; - } - - if (callable.name === "") - { - throw new RuntimeError("An error occurred during native anonymous function execution.", { cause: error }); - } - - throw new RuntimeError(`An error occurred during native function ${callable.name} execution.`, { cause: error }); -} - -export function make_function(callable: Function, parameters_descriptor: Array): VariableNativeFunction -{ - const proxy_function: NativeFunction = async (_: Engine, ...args: Array): Promise> => + // @ts-expect-error: engine is unused for now. + const proxy_function: NativeFunction = async (engine: Engine, ...args: Array): Promise> => { try { diff --git a/src/create-binding/handle-error/handle-error.mts b/src/create-binding/handle-error/handle-error.mts new file mode 100644 index 0000000..9150082 --- /dev/null +++ b/src/create-binding/handle-error/handle-error.mts @@ -0,0 +1,19 @@ +import { ValidationError, isInstanceOf } from "@vitruvius-labs/ts-predicate"; +import { RuntimeError } from "../../runtime-error.mjs"; + +function handle_error(error: unknown, callable: (...args: Array) => unknown): never +{ + if (isInstanceOf(error, RuntimeError) || isInstanceOf(error, ValidationError)) + { + throw error; + } + + if (callable.name === "") + { + throw new RuntimeError("An error occurred during native anonymous function execution.", { cause: error }); + } + + throw new RuntimeError(`An error occurred during native function ${callable.name} execution.`, { cause: error }); +} + +export { handle_error }; diff --git a/src/runtime.mts b/src/runtime.mts index 2802c1d..c0079c3 100644 --- a/src/runtime.mts +++ b/src/runtime.mts @@ -9,7 +9,6 @@ import { } from "@vitruvius-labs/ts-predicate"; import type { Variable } from "./variable/definition/type/variable.type.mjs"; -import type { VariableString } from "./variable/definition/interface/variable-string.interface.mjs"; import type { VariableTable } from "./variable/definition/interface/variable-table.interface.mjs"; import type { TableInputType } from "./boundary/definition/type/table-input.type.mjs"; import type { VariableTableMapType } from "./variable/definition/type/variable-table-map.type.mjs"; From be97803ae689eeb497425ac935d3022405951a28 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 12:59:34 +0000 Subject: [PATCH 55/71] fix: Moved error to its own file and renamed it to-error to avoid shadowing. --- src/lib.mts | 3 ++- src/parser.mts | 10 ++-------- src/parser/error/to-error.mts | 10 ++++++++++ 3 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/parser/error/to-error.mts diff --git a/src/lib.mts b/src/lib.mts index 20c3491..7395235 100644 --- a/src/lib.mts +++ b/src/lib.mts @@ -23,6 +23,7 @@ import { type } from "./lib/type/type.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; import { make_string } from "./runtime/make-string/make-string.mjs"; +import { to_error } from "./parser/error/to-error.mjs"; function optional_parameter( expected_kind: K, @@ -329,7 +330,7 @@ function assert(engine: Engine, condition: Variable, message?: Variable): Array< { if (isVariableKind(condition, VariableKind.Nil) || isVariableKind(condition, VariableKind.Boolean) && !condition.boolean) { - error(engine, message ?? make_string("assertion failed!")); + to_error(engine, message ?? make_string("assertion failed!")); } return [nil]; diff --git a/src/parser.mts b/src/parser.mts index f2ae8b2..8a8f7b8 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -10,6 +10,7 @@ import type { TokenStream } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; +import { to_error } from "./parser/error/to-error.mjs"; const UNARY = [ TokenKindEnum.Not, @@ -31,20 +32,13 @@ const ORDERS = [ [TokenKindEnum.Exponent], ]; -function error(token: TokenInterface, message: string): Error -{ - return new Error( - `${token.debug.line.toFixed(0)}:${token.debug.column.toFixed(0)}: ${message}` - ); -} - function expect(stream: TokenStream, kind: TokenKindEnum): TokenInterface | Error { const token = stream.peek(); if (token.kind !== kind) { - return error( + return to_error( token, `expected '${token_kind_to_string(kind)}', got '${token_kind_to_string(token.kind)}' instead` ); diff --git a/src/parser/error/to-error.mts b/src/parser/error/to-error.mts new file mode 100644 index 0000000..e257d24 --- /dev/null +++ b/src/parser/error/to-error.mts @@ -0,0 +1,10 @@ +import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; + +function to_error(token: TokenInterface, message: string): Error +{ + return new Error( + `${token.debug.line.toFixed(0)}:${token.debug.column.toFixed(0)}: ${message}` + ); +} + +export { to_error }; From 9897cae57b036eebc5dd7feb9219cc9484ef4897 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:01:49 +0000 Subject: [PATCH 56/71] fix: Moved expect to its own file. --- src/parser.mts | 16 +--------------- src/parser/expect/expect.mts | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 src/parser/expect/expect.mts diff --git a/src/parser.mts b/src/parser.mts index 8a8f7b8..ed86604 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -11,6 +11,7 @@ import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; import { to_error } from "./parser/error/to-error.mjs"; +import { expect } from "./parser/expect/expect.mjs"; const UNARY = [ TokenKindEnum.Not, @@ -32,21 +33,6 @@ const ORDERS = [ [TokenKindEnum.Exponent], ]; -function expect(stream: TokenStream, kind: TokenKindEnum): TokenInterface | Error -{ - const token = stream.peek(); - - if (token.kind !== kind) - { - return to_error( - token, - `expected '${token_kind_to_string(kind)}', got '${token_kind_to_string(token.kind)}' instead` - ); - } - - return stream.next(); -} - function consume(stream: TokenStream, kind: TokenKindEnum): boolean { const token = stream.peek(); diff --git a/src/parser/expect/expect.mts b/src/parser/expect/expect.mts new file mode 100644 index 0000000..8356a69 --- /dev/null +++ b/src/parser/expect/expect.mts @@ -0,0 +1,22 @@ +import type { TokenStream } from "../../lexer.mjs"; +import type { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; +import { token_kind_to_string } from "../../lexer/token-kind-to-string/token-kind-to-string.mjs"; +import { to_error } from "../error/to-error.mjs"; + +function expect(stream: TokenStream, kind: TokenKindEnum): TokenInterface | Error +{ + const token = stream.peek(); + + if (token.kind !== kind) + { + return to_error( + token, + `expected '${token_kind_to_string(kind)}', got '${token_kind_to_string(token.kind)}' instead` + ); + } + + return stream.next(); +} + +export { expect }; From aacbb075ddc8fad1ccdd5792c28443a33f5bbe7a Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:03:19 +0000 Subject: [PATCH 57/71] fix: Moved consume to its own file. --- src/parser.mts | 16 +--------------- src/parser/consume/consume.mts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 src/parser/consume/consume.mts diff --git a/src/parser.mts b/src/parser.mts index ed86604..d322467 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -10,7 +10,7 @@ import type { TokenStream } from "./lexer.mjs"; import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; -import { to_error } from "./parser/error/to-error.mjs"; +import { consume } from "./parser/consume/consume.mjs"; import { expect } from "./parser/expect/expect.mjs"; const UNARY = [ @@ -33,20 +33,6 @@ const ORDERS = [ [TokenKindEnum.Exponent], ]; -function consume(stream: TokenStream, kind: TokenKindEnum): boolean -{ - const token = stream.peek(); - - if (token.kind !== kind) - { - return false; - } - - stream.next(); - - return true; -} - function parse_table_key(stream: TokenStream): ExpressionInterface | Error { if (consume(stream, TokenKindEnum.OpenSquare)) diff --git a/src/parser/consume/consume.mts b/src/parser/consume/consume.mts new file mode 100644 index 0000000..fdd7fff --- /dev/null +++ b/src/parser/consume/consume.mts @@ -0,0 +1,18 @@ +import type { TokenStream } from "../../lexer.mjs"; +import type { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; + +function consume(stream: TokenStream, kind: TokenKindEnum): boolean +{ + const token = stream.peek(); + + if (token.kind !== kind) + { + return false; + } + + stream.next(); + + return true; +} + +export { consume }; From 3ec0f1dfa6b2084f13c2bbe46a30cb526c135e97 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:04:34 +0000 Subject: [PATCH 58/71] fix: Moved unary_type_to_expression_kind to its own file. --- src/parser.mts | 16 +--------------- .../unary-type-to-expression-kind.mts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts diff --git a/src/parser.mts b/src/parser.mts index d322467..5ab4c7a 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -12,6 +12,7 @@ import type { TokenInterface } from "./lexer/definition/interface/token.interfac import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; import { consume } from "./parser/consume/consume.mjs"; import { expect } from "./parser/expect/expect.mjs"; +import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; const UNARY = [ TokenKindEnum.Not, @@ -177,21 +178,6 @@ function parse_value(stream: TokenStream): ValueInterface | Error } } -function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKind -{ - // eslint-disable-next-line @ts/switch-exhaustiveness-check - switch (kind) - { - case TokenKindEnum.Not: return ExpressionKind.Not; - case TokenKindEnum.Subtract: return ExpressionKind.Negate; - case TokenKindEnum.Hash: return ExpressionKind.Length; - case TokenKindEnum.BitXOrNot: return ExpressionKind.BitNot; - - default: - throw new Error(); - } -} - function parse_unary_operator(stream: TokenStream): ExpressionInterface | Error { const operator_token = stream.next(); diff --git a/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts b/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts new file mode 100644 index 0000000..2b620c4 --- /dev/null +++ b/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts @@ -0,0 +1,18 @@ +import { ExpressionKind } from "../../ast/definition/enum/expression-kind.enum.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; + +function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKind +{ + switch (kind) + { + case TokenKindEnum.Not: return ExpressionKind.Not; + case TokenKindEnum.Subtract: return ExpressionKind.Negate; + case TokenKindEnum.Hash: return ExpressionKind.Length; + case TokenKindEnum.BitXOrNot: return ExpressionKind.BitNot; + + default: + throw new Error(); + } +} + +export { unary_type_to_expression_kind }; From 858ee539f7f7f61473cdb049eda3d71883608f47 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:07:28 +0000 Subject: [PATCH 59/71] fix: Moved parse to its own file. --- src/parser.mts | 31 +------------------------------ src/parser/parse/parse.mts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 src/parser/parse/parse.mts diff --git a/src/parser.mts b/src/parser.mts index 5ab4c7a..3f3703d 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1121,7 +1121,7 @@ function parse_function(stream: TokenStream): StatementInterface | Error }; } -function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined +export function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined { const token = stream.peek(); @@ -1176,32 +1176,3 @@ function parse_statement(stream: TokenStream, end_tokens: Array): } } } - -export function parse(stream: TokenStream, ...end_tokens: Array): ChunkInterface | Error -{ - const chunk: ChunkInterface = { statements: [] }; - - if (end_tokens.length === 0) - { - end_tokens.push(TokenKindEnum.EOF); - } - - for (;;) - { - const statement = parse_statement(stream, end_tokens); - - if (statement === undefined) - { - break; - } - - if (statement instanceof Error) - { - return statement; - } - - chunk.statements.push(statement); - } - - return chunk; -} diff --git a/src/parser/parse/parse.mts b/src/parser/parse/parse.mts new file mode 100644 index 0000000..b4474c3 --- /dev/null +++ b/src/parser/parse/parse.mts @@ -0,0 +1,35 @@ +import type { ChunkInterface } from "../../ast/definition/interface/chunk.interface.mjs"; +import type { TokenStream } from "../../lexer.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { parse_statement } from "../../parser.mjs"; + +function parse(stream: TokenStream, ...end_tokens: Array): ChunkInterface | Error +{ + const chunk: ChunkInterface = { statements: [] }; + + if (end_tokens.length === 0) + { + end_tokens.push(TokenKindEnum.EOF); + } + + for (;;) + { + const statement = parse_statement(stream, end_tokens); + + if (statement === undefined) + { + break; + } + + if (statement instanceof Error) + { + return statement; + } + + chunk.statements.push(statement); + } + + return chunk; +} + +export { parse }; From 0a017213926e40560a5e47d05079b174859fa084 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:12:16 +0000 Subject: [PATCH 60/71] fix: Moved parse_statement to its own file. --- src/parser.mts | 66 +++--------------- .../parse-statement/parse-statement.mts | 67 +++++++++++++++++++ src/parser/parse/parse.mts | 2 +- 3 files changed, 79 insertions(+), 56 deletions(-) create mode 100644 src/parser/parse-statement/parse-statement.mts diff --git a/src/parser.mts b/src/parser.mts index 3f3703d..900993c 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -1121,58 +1121,14 @@ function parse_function(stream: TokenStream): StatementInterface | Error }; } -export function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined -{ - const token = stream.peek(); - - // eslint-disable-next-line @ts/switch-exhaustiveness-check - switch (token.kind) - { - case TokenKindEnum.Identifier: - case TokenKindEnum.NilLiteral: - case TokenKindEnum.StringLiteral: - case TokenKindEnum.NumberLiteral: - case TokenKindEnum.BooleanLiteral: - case TokenKindEnum.SquiglyOpen: - case TokenKindEnum.Local: - return parse_assign_or_expression(stream); - case TokenKindEnum.Return: - return parse_return(stream); - case TokenKindEnum.Break: - return parse_break(stream); - case TokenKindEnum.If: - return parse_if(stream); - case TokenKindEnum.While: - return parse_while(stream); - case TokenKindEnum.For: - return parse_for(stream); - case TokenKindEnum.Repeat: - return parse_repeat(stream); - case TokenKindEnum.Do: - return parse_do(stream); - case TokenKindEnum.FunctionLike: - return parse_function(stream); - case TokenKindEnum.Semicolon: - stream.next(); - - return { kind: StatementKindEnum.Empty }; - - default: - { - if (end_tokens.includes(token.kind)) - { - return undefined; - } - - const first_end_token = end_tokens[0]; - - if (first_end_token === undefined) - { - throw new Error(); - } - - return error(token, `Missing '${token_kind_to_string(first_end_token)}', ` - + `got '${token_kind_to_string(token.kind)}' instead`); - } - } -} +export { + parse_assign_or_expression, + parse_return, + parse_break, + parse_if, + parse_while, + parse_for, + parse_repeat, + parse_do, + parse_function, +}; diff --git a/src/parser/parse-statement/parse-statement.mts b/src/parser/parse-statement/parse-statement.mts new file mode 100644 index 0000000..47da4ba --- /dev/null +++ b/src/parser/parse-statement/parse-statement.mts @@ -0,0 +1,67 @@ +import { StatementKindEnum } from "../../ast/definition/enum/statement-kind.enum.mjs"; +import type { StatementInterface } from "../../ast/definition/interface/statement.interface.mjs"; +import type { TokenStream } from "../../lexer.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { token_kind_to_string } from "../../lexer/token-kind-to-string/token-kind-to-string.mjs"; +import { parse_assign_or_expression, parse_break, parse_do, parse_for, parse_function, parse_if, parse_repeat, parse_return, parse_while } from "../../parser.mjs"; +import { to_error } from "../error/to-error.mjs"; + +function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined +{ + const token = stream.peek(); + + // eslint-disable-next-line @ts/switch-exhaustiveness-check + switch (token.kind) + { + case TokenKindEnum.Identifier: + case TokenKindEnum.NilLiteral: + case TokenKindEnum.StringLiteral: + case TokenKindEnum.NumberLiteral: + case TokenKindEnum.BooleanLiteral: + case TokenKindEnum.SquiglyOpen: + case TokenKindEnum.Local: + return parse_assign_or_expression(stream); + case TokenKindEnum.Return: + return parse_return(stream); + case TokenKindEnum.Break: + return parse_break(stream); + case TokenKindEnum.If: + return parse_if(stream); + case TokenKindEnum.While: + return parse_while(stream); + case TokenKindEnum.For: + return parse_for(stream); + case TokenKindEnum.Repeat: + return parse_repeat(stream); + case TokenKindEnum.Do: + return parse_do(stream); + case TokenKindEnum.FunctionLike: + return parse_function(stream); + case TokenKindEnum.Semicolon: + stream.next(); + + return { kind: StatementKindEnum.Empty }; + + default: + { + if (end_tokens.includes(token.kind)) + { + return undefined; + } + + const first_end_token = end_tokens[0]; + + if (first_end_token === undefined) + { + throw new Error(); + } + + return to_error( + token, + `Missing '${token_kind_to_string(first_end_token)}', got '${token_kind_to_string(token.kind)}' instead` + ); + } + } +} + +export { parse_statement }; diff --git a/src/parser/parse/parse.mts b/src/parser/parse/parse.mts index b4474c3..7df97dc 100644 --- a/src/parser/parse/parse.mts +++ b/src/parser/parse/parse.mts @@ -1,7 +1,7 @@ import type { ChunkInterface } from "../../ast/definition/interface/chunk.interface.mjs"; import type { TokenStream } from "../../lexer.mjs"; import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; -import { parse_statement } from "../../parser.mjs"; +import { parse_statement } from "../parse-statement/parse-statement.mjs"; function parse(stream: TokenStream, ...end_tokens: Array): ChunkInterface | Error { From b2eef4fb9b5a9070340fa58095b0b2aa06b91715 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:25:22 +0000 Subject: [PATCH 61/71] fix: Moved parse_function_params to its own file. --- src/parser.mts | 40 +---------------- .../parse-function-params.mts | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 38 deletions(-) create mode 100644 src/parser/parse-function-params/parse-function-params.mts diff --git a/src/parser.mts b/src/parser.mts index 900993c..43cfb47 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -12,6 +12,8 @@ import type { TokenInterface } from "./lexer/definition/interface/token.interfac import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; import { consume } from "./parser/consume/consume.mjs"; import { expect } from "./parser/expect/expect.mjs"; +import { parse_function_params } from "./parser/parse-function-params/parse-function-params.mjs"; +import { parse } from "./parser/parse/parse.mjs"; import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; const UNARY = [ @@ -952,44 +954,6 @@ function parse_do(stream: TokenStream): StatementInterface | Error }; } -function parse_function_params(stream: TokenStream): Array | Error -{ - const open_brace = expect(stream, TokenKindEnum.OpenBrace); - - if (open_brace instanceof Error) - { - return open_brace; - } - - const params: Array = []; - - while (stream.peek().kind !== TokenKindEnum.CloseBrace) - { - const param = expect(stream, TokenKindEnum.Identifier); - - if (param instanceof Error) - { - break; - } - - params.push(param); - - if (!consume(stream, TokenKindEnum.Comma)) - { - break; - } - } - - const close_brace = expect(stream, TokenKindEnum.CloseBrace); - - if (close_brace instanceof Error) - { - return close_brace; - } - - return params; -} - function parse_function_value(function_token: TokenInterface, stream: TokenStream): ValueInterface | Error { const params = parse_function_params(stream); diff --git a/src/parser/parse-function-params/parse-function-params.mts b/src/parser/parse-function-params/parse-function-params.mts new file mode 100644 index 0000000..65e0677 --- /dev/null +++ b/src/parser/parse-function-params/parse-function-params.mts @@ -0,0 +1,45 @@ +import type { TokenStream } from "../../lexer.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; +import { consume } from "../consume/consume.mjs"; +import { expect } from "../expect/expect.mjs"; + +function parse_function_params(stream: TokenStream): Array | Error +{ + const open_brace = expect(stream, TokenKindEnum.OpenBrace); + + if (open_brace instanceof Error) + { + return open_brace; + } + + const params: Array = []; + + while (stream.peek().kind !== TokenKindEnum.CloseBrace) + { + const param = expect(stream, TokenKindEnum.Identifier); + + if (param instanceof Error) + { + break; + } + + params.push(param); + + if (!consume(stream, TokenKindEnum.Comma)) + { + break; + } + } + + const close_brace = expect(stream, TokenKindEnum.CloseBrace); + + if (close_brace instanceof Error) + { + return close_brace; + } + + return params; +} + +export { parse_function_params }; From 11cae4d6a50164f6cfb4a7ebcf2b961f37d07356 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 13:39:52 +0000 Subject: [PATCH 62/71] fix: Moved operation_type_to_expression_kind to its own file. --- src/parser.mts | 35 +---------------- .../operation-type-to-expression-kind.mts | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts diff --git a/src/parser.mts b/src/parser.mts index 43cfb47..94954de 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -12,6 +12,7 @@ import type { TokenInterface } from "./lexer/definition/interface/token.interfac import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; import { consume } from "./parser/consume/consume.mjs"; import { expect } from "./parser/expect/expect.mjs"; +import { operation_type_to_expression_kind } from "./parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mjs"; import { parse_function_params } from "./parser/parse-function-params/parse-function-params.mjs"; import { parse } from "./parser/parse/parse.mjs"; import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; @@ -371,40 +372,6 @@ function parse_access_expression(expression: ExpressionInterface, stream: TokenS return expression; } -function operation_type_to_expression_kind( - operation_type: TokenKindEnum -): ExpressionKind -{ - // eslint-disable-next-line @ts/switch-exhaustiveness-check - switch (operation_type) - { - case TokenKindEnum.Addition: return ExpressionKind.Addition; - case TokenKindEnum.Subtract: return ExpressionKind.Subtract; - case TokenKindEnum.Multiply: return ExpressionKind.Multiplication; - case TokenKindEnum.Division: return ExpressionKind.Division; - case TokenKindEnum.FloorDivision: return ExpressionKind.FloorDivision; - case TokenKindEnum.Modulo: return ExpressionKind.Modulo; - case TokenKindEnum.Exponent: return ExpressionKind.Exponent; - case TokenKindEnum.Concat: return ExpressionKind.Concat; - case TokenKindEnum.BitAnd: return ExpressionKind.BitAnd; - case TokenKindEnum.BitOr: return ExpressionKind.BitOr; - case TokenKindEnum.BitXOrNot: return ExpressionKind.BitXOr; - case TokenKindEnum.BitShiftLeft: return ExpressionKind.BitShiftLeft; - case TokenKindEnum.BitShiftRight: return ExpressionKind.BitShiftRight; - case TokenKindEnum.LessThan: return ExpressionKind.LessThan; - case TokenKindEnum.LessThanEquals: return ExpressionKind.LessThanEquals; - case TokenKindEnum.GreaterThan: return ExpressionKind.GreaterThan; - case TokenKindEnum.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; - case TokenKindEnum.Equals: return ExpressionKind.Equals; - case TokenKindEnum.NotEquals: return ExpressionKind.NotEquals; - case TokenKindEnum.And: return ExpressionKind.And; - case TokenKindEnum.Or: return ExpressionKind.Or; - - default: - throw new Error(); - } -} - function parse_operation( stream: TokenStream, order: number diff --git a/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts b/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts new file mode 100644 index 0000000..53d50e5 --- /dev/null +++ b/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts @@ -0,0 +1,38 @@ +import { ExpressionKind } from "../../ast/definition/enum/expression-kind.enum.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; + +function operation_type_to_expression_kind( + operation_type: TokenKindEnum +): ExpressionKind +{ + // eslint-disable-next-line @ts/switch-exhaustiveness-check + switch (operation_type) + { + case TokenKindEnum.Addition: return ExpressionKind.Addition; + case TokenKindEnum.Subtract: return ExpressionKind.Subtract; + case TokenKindEnum.Multiply: return ExpressionKind.Multiplication; + case TokenKindEnum.Division: return ExpressionKind.Division; + case TokenKindEnum.FloorDivision: return ExpressionKind.FloorDivision; + case TokenKindEnum.Modulo: return ExpressionKind.Modulo; + case TokenKindEnum.Exponent: return ExpressionKind.Exponent; + case TokenKindEnum.Concat: return ExpressionKind.Concat; + case TokenKindEnum.BitAnd: return ExpressionKind.BitAnd; + case TokenKindEnum.BitOr: return ExpressionKind.BitOr; + case TokenKindEnum.BitXOrNot: return ExpressionKind.BitXOr; + case TokenKindEnum.BitShiftLeft: return ExpressionKind.BitShiftLeft; + case TokenKindEnum.BitShiftRight: return ExpressionKind.BitShiftRight; + case TokenKindEnum.LessThan: return ExpressionKind.LessThan; + case TokenKindEnum.LessThanEquals: return ExpressionKind.LessThanEquals; + case TokenKindEnum.GreaterThan: return ExpressionKind.GreaterThan; + case TokenKindEnum.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; + case TokenKindEnum.Equals: return ExpressionKind.Equals; + case TokenKindEnum.NotEquals: return ExpressionKind.NotEquals; + case TokenKindEnum.And: return ExpressionKind.And; + case TokenKindEnum.Or: return ExpressionKind.Or; + + default: + throw new Error(); + } +} + +export { operation_type_to_expression_kind }; From 762d4a2e203c011dd4a69a9f6bb67fd8a08ae78a Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 14:08:21 +0000 Subject: [PATCH 63/71] fix: Moved parse_local_statement to its own file. --- src/parser.mts | 29 ++-------------- .../parse-local-statement.mts | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 src/parser/parse-local-statement/parse-local-statement.mts diff --git a/src/parser.mts b/src/parser.mts index 94954de..6fa01ef 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -11,9 +11,11 @@ import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { token_kind_to_string } from "./lexer/token-kind-to-string/token-kind-to-string.mjs"; import { consume } from "./parser/consume/consume.mjs"; +import { to_error } from "./parser/error/to-error.mjs"; import { expect } from "./parser/expect/expect.mjs"; import { operation_type_to_expression_kind } from "./parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mjs"; import { parse_function_params } from "./parser/parse-function-params/parse-function-params.mjs"; +import { parse_local_statement } from "./parser/parse-local-statement/parse-local-statement.mjs"; import { parse } from "./parser/parse/parse.mjs"; import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; @@ -177,7 +179,7 @@ function parse_value(stream: TokenStream): ValueInterface | Error return parse_function_value(stream.next(), stream); default: - return error(token, `Expected value, got ${token_kind_to_string(token.kind)} instead`); + return to_error(token, `Expected value, got ${token_kind_to_string(token.kind)} instead`); } } @@ -429,31 +431,6 @@ function parse_expression(stream: TokenStream): ExpressionInterface | Error return parse_operation(stream, 0); } -function parse_local_statement(local: TokenInterface, values: Array): StatementInterface | Error -{ - const names: Array = []; - - for (const expression of values) - { - const value = expression.value; - - if (value === undefined || value.kind !== ValueKindEnum.Variable) - { - return error(expression.token, "Invalid local name"); - } - - names.push(value.token); - } - - return { - kind: StatementKindEnum.Local, - local: { - token: local, - names: names, - }, - }; -} - function parse_assign_or_expression(stream: TokenStream): StatementInterface | Error { const local = expect(stream, TokenKindEnum.Local); diff --git a/src/parser/parse-local-statement/parse-local-statement.mts b/src/parser/parse-local-statement/parse-local-statement.mts new file mode 100644 index 0000000..fe2ef78 --- /dev/null +++ b/src/parser/parse-local-statement/parse-local-statement.mts @@ -0,0 +1,33 @@ +import { StatementKindEnum } from "../../ast/definition/enum/statement-kind.enum.mjs"; +import { ValueKindEnum } from "../../ast/definition/enum/value-kind.enum.mjs"; +import type { ExpressionInterface } from "../../ast/definition/interface/expression.interface.mjs"; +import type { StatementInterface } from "../../ast/definition/interface/statement.interface.mjs"; +import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; +import { to_error } from "../error/to-error.mjs"; + +function parse_local_statement(local: TokenInterface, values: Array): StatementInterface | Error +{ + const names: Array = []; + + for (const expression of values) + { + const value = expression.value; + + if (value === undefined || value.kind !== ValueKindEnum.Variable) + { + return to_error(expression.token, "Invalid local name"); + } + + names.push(value.token); + } + + return { + kind: StatementKindEnum.Local, + local: { + token: local, + names: names, + }, + }; +} + +export { parse_local_statement }; From 5a29a96e5ee69546da3a08e1f6250f3200c2d672 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 31 Jan 2026 14:11:26 +0000 Subject: [PATCH 64/71] fix: Moved parse_break to its own file. --- src/parser.mts | 15 ------------- .../parse-statement/parse-statement.mts | 3 ++- src/parser/parse_break/parse-break.mts | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 src/parser/parse_break/parse-break.mts diff --git a/src/parser.mts b/src/parser.mts index 6fa01ef..b1ac801 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -535,20 +535,6 @@ function parse_return(stream: TokenStream): StatementInterface | Error }; } -function parse_break(stream: TokenStream): StatementInterface | Error -{ - const break_token = expect(stream, TokenKindEnum.Break); - - if (break_token instanceof Error) - { - return break_token; - } - - return { - kind: StatementKindEnum.Break, - }; -} - function parse_if(stream: TokenStream): StatementInterface | Error { const if_token = expect(stream, TokenKindEnum.If); @@ -1032,7 +1018,6 @@ function parse_function(stream: TokenStream): StatementInterface | Error export { parse_assign_or_expression, parse_return, - parse_break, parse_if, parse_while, parse_for, diff --git a/src/parser/parse-statement/parse-statement.mts b/src/parser/parse-statement/parse-statement.mts index 47da4ba..14b2592 100644 --- a/src/parser/parse-statement/parse-statement.mts +++ b/src/parser/parse-statement/parse-statement.mts @@ -3,8 +3,9 @@ import type { StatementInterface } from "../../ast/definition/interface/statemen import type { TokenStream } from "../../lexer.mjs"; import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; import { token_kind_to_string } from "../../lexer/token-kind-to-string/token-kind-to-string.mjs"; -import { parse_assign_or_expression, parse_break, parse_do, parse_for, parse_function, parse_if, parse_repeat, parse_return, parse_while } from "../../parser.mjs"; +import { parse_assign_or_expression, parse_do, parse_for, parse_function, parse_if, parse_repeat, parse_return, parse_while } from "../../parser.mjs"; import { to_error } from "../error/to-error.mjs"; +import { parse_break } from "../parse_break/parse-break.mjs"; function parse_statement(stream: TokenStream, end_tokens: Array): StatementInterface | Error | undefined { diff --git a/src/parser/parse_break/parse-break.mts b/src/parser/parse_break/parse-break.mts new file mode 100644 index 0000000..250f519 --- /dev/null +++ b/src/parser/parse_break/parse-break.mts @@ -0,0 +1,21 @@ +import { StatementKindEnum } from "../../ast/definition/enum/statement-kind.enum.mjs"; +import type { StatementInterface } from "../../ast/definition/interface/statement.interface.mjs"; +import type { TokenStream } from "../../lexer.mjs"; +import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { expect } from "../expect/expect.mjs"; + +function parse_break(stream: TokenStream): StatementInterface | Error +{ + const break_token = expect(stream, TokenKindEnum.Break); + + if (break_token instanceof Error) + { + return break_token; + } + + return { + kind: StatementKindEnum.Break, + }; +} + +export { parse_break }; From 540371967b3c4e98a1fe53d738de2d878ea44f9a Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 11:56:39 +0000 Subject: [PATCH 65/71] fix: Resolved a wrong syntax call on debug options. --- src/compiler.mts | 16 ++++++++-------- src/lexer.mts | 34 +++++++++++++++++----------------- src/parser.mts | 12 ++++++++---- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/compiler.mts b/src/compiler.mts index b1f045e..e1b8909 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -126,7 +126,7 @@ function compile_operation( ops.push(...compile_expression(rhs, functions)); ops.push(...compile_expression(lhs, functions)); - ops.push({ code: operation, debug: expression.token.debug }); + ops.push({ code: operation, debug: expression.token.peek().debug }); return ops; } @@ -142,7 +142,7 @@ function compile_call( throw new Error(); } - const debug = func.token.debug; + const debug = func.token.peek().debug; const ops: Array = []; for (const arg of args) @@ -172,7 +172,7 @@ function compile_index( ops.push(...compile_expression(index, functions)); ops.push(...compile_expression(target, functions)); - ops.push({ code: OpCodeEnum.LoadIndex, debug: target.token.debug }); + ops.push({ code: OpCodeEnum.LoadIndex, debug: target.token.peek().debug }); return ops; } @@ -191,7 +191,7 @@ function compile_unary_operation( const ops: Array = []; ops.push(...compile_expression(expression.expression, functions)); - ops.push({ code: operation, debug: expression.token.debug }); + ops.push({ code: operation, debug: expression.token.peek().debug }); return ops; } @@ -289,7 +289,7 @@ function compile_assignment(assignment: AssignmentInterface | undefined, functio for (const lhs of assignment.lhs) { - const debug = lhs.token.debug; + const debug = lhs.token.peek().debug; switch (lhs.kind) { @@ -361,7 +361,7 @@ function compile_inverted_conditional_jump(condition: ExpressionInterface | unde } const ops: Array = []; - const debug = condition.token.debug; + const debug = condition.token.peek().debug; switch (condition.kind) { @@ -408,7 +408,7 @@ function compile_conditional_jump(condition: ExpressionInterface | undefined, ju } const ops: Array = []; - const debug = condition.token.debug; + const debug = condition.token.peek().debug; switch (condition.kind) { @@ -748,7 +748,7 @@ function compile_chunk(chunk: ChunkInterface, functions: Array 0) @@ -577,21 +594,4 @@ this.consume(); return this.peek_queue.shift() as TokenInterface; } - - peek(count = 1): TokenInterface - { - while (this.peek_queue.length < count) - { - this.on_char(); - } - - const token = this.peek_queue[count - 1]; - - if (token === undefined) - { - throw new Error(); - } - - return token; - } } diff --git a/src/parser.mts b/src/parser.mts index b1ac801..0dc6e05 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -93,9 +93,11 @@ function parse_table(stream: TokenStream): ValueInterface | Error const elements: Map = new Map(); let current_numeric_key = 1; - while (stream.peek().kind !== TokenKindEnum.SquiglyClose) + let tokenInterface: TokenInterface = stream.peek(); + + do { - const element = parse_table_key(stream); + const element: ExpressionInterface | Error = parse_table_key(stream); if (element instanceof Error) { @@ -118,7 +120,7 @@ function parse_table(stream: TokenStream): ValueInterface | Error const key_token = { kind: TokenKindEnum.NumberLiteral, data: current_numeric_key.toString(), - debug: element.token.debug, + debug: tokenInterface.debug, }; const key = { @@ -139,7 +141,9 @@ function parse_table(stream: TokenStream): ValueInterface | Error { break; } - } + + tokenInterface = stream.peek(); + } while (tokenInterface.kind !== TokenKindEnum.SquiglyClose); const close_squigly = expect(stream, TokenKindEnum.SquiglyClose); From a83eff6a62dcb72c7d56e09a8274e8ccc081377e Mon Sep 17 00:00:00 2001 From: Zamralik Date: Sun, 1 Feb 2026 13:44:28 +0100 Subject: [PATCH 66/71] fix: convert remaining enums + fix TS errors --- src/_index.mts | 12 +- src/ast/_index.mts | 1 + src/ast/definition/_index.mts | 2 + src/ast/definition/enum/_index.mts | 3 + .../definition/enum/expression-kind.enum.mts | 67 +++-- .../definition/enum/statement-kind.enum.mts | 37 +-- src/ast/definition/enum/value-kind.enum.mts | 23 +- src/ast/definition/interface/_index.mts | 15 + .../interface/expression.interface.mts | 9 +- .../definition/interface/local.interface.mts | 3 +- .../definition/interface/value.interface.mts | 3 +- src/compiler.mts | 282 +++++++++--------- src/create-binding.mts | 3 +- src/engine.mts | 102 +++---- src/lexer.mts | 222 +++++++------- src/lexer/definition/enum/state.enum.mts | 30 +- src/lexer/definition/enum/token-kind.enum.mts | 119 ++++---- .../definition/interface/token.interface.mts | 3 +- .../token-kind-to-string.mts | 166 +++++++---- src/lexer/utility/get-debug.mts | 15 + src/lib/type/type.mts | 2 +- src/opcode/_index.mts | 0 src/opcode/definition/enum/op-code.enum.mts | 128 ++++---- src/opcode/op-code-name/op-code-name.mts | 154 ++++++---- src/optimizer.mts | 57 ++-- src/parser.mts | 264 ++++++++-------- src/parser/consume/consume.mts | 3 +- src/parser/error/to-error.mts | 3 +- .../operation-type-to-expression-kind.mts | 73 +++-- .../parse-function-params.mts | 12 +- .../parse-local-statement.mts | 11 +- .../parse-statement/parse-statement.mts | 38 +-- src/parser/parse/parse.mts | 4 +- src/parser/parse_break/parse-break.mts | 8 +- .../unary-type-to-expression-kind.mts | 19 +- 35 files changed, 1057 insertions(+), 836 deletions(-) create mode 100644 src/ast/_index.mts create mode 100644 src/ast/definition/_index.mts create mode 100644 src/ast/definition/enum/_index.mts create mode 100644 src/ast/definition/interface/_index.mts create mode 100644 src/lexer/utility/get-debug.mts create mode 100644 src/opcode/_index.mts diff --git a/src/_index.mts b/src/_index.mts index 3a0c520..9537994 100644 --- a/src/_index.mts +++ b/src/_index.mts @@ -1,12 +1,16 @@ import { Engine } from "./engine.mjs"; -import { make_boolean, make_number, make_string, make_table, make_variable } from "./runtime.mjs"; +import { make_table, make_variable } from "./runtime.mjs"; +import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; +import { make_number } from "./runtime/make-number/make-number.mjs"; +import { make_string } from "./runtime/make-string/make-string.mjs"; import { compile } from "./compiler.mjs"; -import { std_lib, variable_to_string } from "./lib.mjs"; +import { std_lib } from "./lib.mjs"; +import { variable_to_string } from "./lib/variable-to-string/variable-to-string.mjs"; export * as lexer from "./lexer.mjs"; export * as parser from "./parser.mjs"; -export * as ast from "./ast.mjs"; -export * as opcode from "./opcode.mjs"; +export * as ast from "./ast/_index.mjs"; +export * as opcode from "./opcode/_index.mjs"; export * as runtime from "./runtime.mjs"; export type * from "./boundary/definition/_index.mjs"; diff --git a/src/ast/_index.mts b/src/ast/_index.mts new file mode 100644 index 0000000..9b306eb --- /dev/null +++ b/src/ast/_index.mts @@ -0,0 +1 @@ +export * from "./definition/_index.mjs"; diff --git a/src/ast/definition/_index.mts b/src/ast/definition/_index.mts new file mode 100644 index 0000000..833204c --- /dev/null +++ b/src/ast/definition/_index.mts @@ -0,0 +1,2 @@ +export type * from "./interface/_index.mjs"; +export * from "./enum/_index.mjs"; diff --git a/src/ast/definition/enum/_index.mts b/src/ast/definition/enum/_index.mts new file mode 100644 index 0000000..4e797b4 --- /dev/null +++ b/src/ast/definition/enum/_index.mts @@ -0,0 +1,3 @@ +export * from "./expression-kind.enum.mjs"; +export * from "./statement-kind.enum.mjs"; +export * from "./value-kind.enum.mjs"; diff --git a/src/ast/definition/enum/expression-kind.enum.mts b/src/ast/definition/enum/expression-kind.enum.mts index 9159892..dea3e49 100644 --- a/src/ast/definition/enum/expression-kind.enum.mts +++ b/src/ast/definition/enum/expression-kind.enum.mts @@ -1,37 +1,40 @@ -enum ExpressionKind -{ - Value = 0, - Call = 1, - Index = 2, +const ExpressionKind = { + Value: "value", + Call: "call", + Index: "index", - Addition = 3, - Subtract = 4, - Multiplication = 5, - Division = 6, - FloorDivision = 7, - Modulo = 8, - Exponent = 9, - Concat = 10, + Negate: "negate", + Addition: "addition", + Subtract: "subtract", + Multiplication: "multiplication", + Division: "division", + FloorDivision: "floor-division", + Modulo: "modulo", + Exponent: "exponent", + Concat: "concat", - BitAnd = 11, - BitOr = 12, - BitXOr = 13, - BitNot = 14, - BitShiftLeft = 15, - BitShiftRight = 16, + BitAnd: "bit-and", + BitOr: "bit-or", + BitXOr: "bit-xor", + BitNot: "bit-not", + BitShiftLeft: "bit-shift-left", + BitShiftRight: "bit-shift-right", - Equals = 17, - NotEquals = 18, - LessThan = 19, - LessThanEquals = 20, - GreaterThan = 21, - GreaterThanEquals = 22, - And = 23, - Or = 24, + Equals: "equals", + NotEquals: "not-equals", + LessThan: "less-than", + LessThanEquals: "less-than-equals", + GreaterThan: "greater-than", + GreaterThanEquals: "greater-than-equals", - Not = 25, - Negate = 26, - Length = 27, -} + And: "and", + // eslint-disable-next-line id-length + Or: "or", + Not: "not", -export { ExpressionKind }; + Length: "length", +} as const satisfies Record; + +type ExpressionKindEnum = typeof ExpressionKind[keyof typeof ExpressionKind]; + +export { ExpressionKind, type ExpressionKindEnum }; diff --git a/src/ast/definition/enum/statement-kind.enum.mts b/src/ast/definition/enum/statement-kind.enum.mts index 2aa3f95..977a6e8 100644 --- a/src/ast/definition/enum/statement-kind.enum.mts +++ b/src/ast/definition/enum/statement-kind.enum.mts @@ -1,18 +1,21 @@ -const enum StatementKindEnum -{ - Invalid = 0, - Empty = 1, - Expression = 2, - Assignment = 3, - Local = 4, - If = 5, - While = 6, - For = 7, - NumericFor = 8, - Repeat = 9, - Do = 10, - Return = 11, - Break = 12, -} +const StatementKind = { + Invalid: "invalid", + Empty: "empty", + Expression: "expression", + Assignment: "assignment", + Local: "local", + // eslint-disable-next-line id-length + If: "if", + While: "while", + For: "for", + NumericFor: "numeric-for", + Repeat: "repeat", + // eslint-disable-next-line id-length + Do: "do", + Return: "return", + Break: "break", +} as const satisfies Record; -export { StatementKindEnum }; +type StatementKindEnum = typeof StatementKind[keyof typeof StatementKind]; + +export { StatementKind, type StatementKindEnum }; diff --git a/src/ast/definition/enum/value-kind.enum.mts b/src/ast/definition/enum/value-kind.enum.mts index 48db867..2f571d9 100644 --- a/src/ast/definition/enum/value-kind.enum.mts +++ b/src/ast/definition/enum/value-kind.enum.mts @@ -1,12 +1,13 @@ -const enum ValueKindEnum -{ - NilLiteral = 0, - NumberLiteral = 1, - BooleanLiteral = 2, - StringLiteral = 3, - TableLiteral = 4, - FunctionLike = 5, - Variable = 6, -} +const ValueKind = { + NilLiteral: "nil-literal", + NumberLiteral: "number-literal", + BooleanLiteral: "boolean-literal", + StringLiteral: "string-literal", + TableLiteral: "table-literal", + FunctionLike: "function-like", + Variable: "variable", +} as const satisfies Record; -export { ValueKindEnum }; +type ValueKindEnum = typeof ValueKind[keyof typeof ValueKind]; + +export { ValueKind, type ValueKindEnum }; diff --git a/src/ast/definition/interface/_index.mts b/src/ast/definition/interface/_index.mts new file mode 100644 index 0000000..428b91b --- /dev/null +++ b/src/ast/definition/interface/_index.mts @@ -0,0 +1,15 @@ +export type * from "./assignment.interface.mjs"; +export type * from "./chunk.interface.mjs"; +export type * from "./do.interface.mjs"; +export type * from "./else-if-block.interface.mjs"; +export type * from "./expression.interface.mjs"; +export type * from "./for.interface.mjs"; +export type * from "./if-block.interface.mjs"; +export type * from "./local.interface.mjs"; +export type * from "./lua-function.interface.mjs"; +export type * from "./numeric-for.interface.mjs"; +export type * from "./repeat.interface.mjs"; +export type * from "./return.interface.mjs"; +export type * from "./statement.interface.mjs"; +export type * from "./value.interface.mjs"; +export type * from "./while.interface.mjs"; diff --git a/src/ast/definition/interface/expression.interface.mts b/src/ast/definition/interface/expression.interface.mts index 6ab992f..0243a5c 100644 --- a/src/ast/definition/interface/expression.interface.mts +++ b/src/ast/definition/interface/expression.interface.mts @@ -1,11 +1,14 @@ import type { TokenStream } from "../../../lexer.mjs"; -import type { ExpressionKind } from "../enum/expression-kind.enum.mjs"; +import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; +import type { ExpressionKindEnum } from "../enum/expression-kind.enum.mjs"; import type { ValueInterface } from "./value.interface.mjs"; interface ExpressionInterface { - kind: ExpressionKind; - token: TokenStream; + // @TODO: Remove 'data' field + data?: never; + kind: ExpressionKindEnum; + token: TokenInterface | TokenStream; lhs?: ExpressionInterface; rhs?: ExpressionInterface; value?: ValueInterface; diff --git a/src/ast/definition/interface/local.interface.mts b/src/ast/definition/interface/local.interface.mts index 785a001..80ac9f2 100644 --- a/src/ast/definition/interface/local.interface.mts +++ b/src/ast/definition/interface/local.interface.mts @@ -1,8 +1,9 @@ +import type { TokenStream } from "../../../lexer.mjs"; import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; interface LocalInterface { - names: Array; + names: Array; token: TokenInterface; } diff --git a/src/ast/definition/interface/value.interface.mts b/src/ast/definition/interface/value.interface.mts index 7617408..a23211a 100644 --- a/src/ast/definition/interface/value.interface.mts +++ b/src/ast/definition/interface/value.interface.mts @@ -1,3 +1,4 @@ +import type { TokenStream } from "../../../lexer.mjs"; import type { TokenInterface } from "../../../lexer/definition/interface/token.interface.mjs"; import type { ValueKindEnum } from "../enum/value-kind.enum.mjs"; import type { ExpressionInterface } from "./expression.interface.mjs"; @@ -6,7 +7,7 @@ import type { LuaFunctionInterface } from "./lua-function.interface.mjs"; interface ValueInterface { kind: ValueKindEnum; - token: TokenInterface; + token: TokenInterface | TokenStream; number?: number | undefined; boolean?: boolean | undefined; string?: string | undefined; diff --git a/src/compiler.mts b/src/compiler.mts index e1b8909..3170cdb 100644 --- a/src/compiler.mts +++ b/src/compiler.mts @@ -1,9 +1,9 @@ import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import { nil } from "./variable/nil.mjs"; -import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import { ValueKind } from "./ast/definition/enum/value-kind.enum.mjs"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; -import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; -import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; +import { StatementKind } from "./ast/definition/enum/statement-kind.enum.mjs"; +import { OpCode, type OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import type { ProgramInterface } from "./opcode/definition/interface/program.interface.mjs"; import type { ValueInterface } from "./ast/definition/interface/value.interface.mjs"; @@ -19,25 +19,28 @@ import type { DoInterface } from "./ast/definition/interface/do.interface.mjs"; import type { ReturnInterface } from "./ast/definition/interface/return.interface.mjs"; import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; +import type { TokenStream } from "./lexer.mjs"; +import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; import { make_boolean } from "./runtime/make-boolean/make-boolean.mjs"; import { make_number } from "./runtime/make-number/make-number.mjs"; import { make_string } from "./runtime/make-string/make-string.mjs"; +import { getDebug } from "./lexer/utility/get-debug.mjs"; -function compile_function(chunk: ChunkInterface, token: TokenInterface, parameters: Array, functions: Array>): number +function compile_function(chunk: ChunkInterface, token: TokenInterface | TokenStream, parameters: Array, functions: Array>): number { const ops: Array = []; - ops.push({ code: OpCodeEnum.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); + ops.push({ code: OpCode.ArgumentCount, arg: make_number(parameters.length), debug: token.debug }); for (const parameter of parameters.reverse()) { - ops.push({ code: OpCodeEnum.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }); - ops.push({ code: OpCodeEnum.Store, arg: make_string(parameter.data), debug: parameter.debug }); + ops.push({ code: OpCode.MakeLocal, arg: make_string(parameter.data), debug: parameter.debug }); + ops.push({ code: OpCode.Store, arg: make_string(parameter.data), debug: parameter.debug }); } ops.push(...compile_block(chunk, functions)); - ops.push({ code: OpCodeEnum.Push, arg: nil, debug: token.debug }); - ops.push({ code: OpCodeEnum.Return, arg: make_number(0), debug: token.debug }); + ops.push({ code: OpCode.Push, arg: nil, debug: token.debug }); + ops.push({ code: OpCode.Return, arg: make_number(0), debug: token.debug }); functions.push(ops); @@ -55,19 +58,19 @@ function compile_value(value: ValueInterface | undefined, functions: Array = []; - output.push({ code: OpCodeEnum.NewTable, debug: debug }); + output.push({ code: OpCode.NewTable, debug: debug }); for (const [key, expression] of [...value.table?.entries() ?? []].reverse()) { @@ -93,15 +96,15 @@ function compile_value(value: ValueInterface | undefined, functions: Array | undefined, functions: Array> ): Array { - if (func === undefined || args === undefined) + if (callable === undefined || args === undefined) { throw new Error(); } - const debug = func.token.peek().debug; + const debug: DebugInterface = getDebug(callable.token); const ops: Array = []; for (const arg of args) @@ -150,9 +157,9 @@ function compile_call( ops.push(...compile_expression(arg, functions)); } - ops.push(...compile_expression(func, functions)); - ops.push({ code: OpCodeEnum.Push, arg: make_number(args.length), debug: debug }); - ops.push({ code: OpCodeEnum.Call, debug: debug }); + ops.push(...compile_expression(callable, functions)); + ops.push({ code: OpCode.Push, arg: make_number(args.length), debug: debug }); + ops.push({ code: OpCode.Call, debug: debug }); return ops; } @@ -172,7 +179,7 @@ function compile_index( ops.push(...compile_expression(index, functions)); ops.push(...compile_expression(target, functions)); - ops.push({ code: OpCodeEnum.LoadIndex, debug: target.token.peek().debug }); + ops.push({ code: OpCode.LoadIndex, debug: getDebug(target.token) }); return ops; } @@ -191,7 +198,7 @@ function compile_unary_operation( const ops: Array = []; ops.push(...compile_expression(expression.expression, functions)); - ops.push({ code: operation, debug: expression.token.peek().debug }); + ops.push({ code: operation, debug: getDebug(expression.token) }); return ops; } @@ -213,58 +220,58 @@ function compile_expression(expression: ExpressionInterface | undefined, functio return compile_index(expression.expression, expression.index, functions); case ExpressionKind.Addition: - return compile_operation(expression, OpCodeEnum.Add, functions); + return compile_operation(expression, OpCode.Add, functions); case ExpressionKind.Subtract: - return compile_operation(expression, OpCodeEnum.Subtract, functions); + return compile_operation(expression, OpCode.Subtract, functions); case ExpressionKind.Multiplication: - return compile_operation(expression, OpCodeEnum.Multiply, functions); + return compile_operation(expression, OpCode.Multiply, functions); case ExpressionKind.Division: - return compile_operation(expression, OpCodeEnum.Divide, functions); + return compile_operation(expression, OpCode.Divide, functions); case ExpressionKind.FloorDivision: - return compile_operation(expression, OpCodeEnum.FloorDivide, functions); + return compile_operation(expression, OpCode.FloorDivide, functions); case ExpressionKind.Modulo: - return compile_operation(expression, OpCodeEnum.Modulo, functions); + return compile_operation(expression, OpCode.Modulo, functions); case ExpressionKind.Exponent: - return compile_operation(expression, OpCodeEnum.Exponent, functions); + return compile_operation(expression, OpCode.Exponent, functions); case ExpressionKind.Concat: - return compile_operation(expression, OpCodeEnum.Concat, functions); + return compile_operation(expression, OpCode.Concat, functions); case ExpressionKind.BitAnd: - return compile_operation(expression, OpCodeEnum.BitAnd, functions); + return compile_operation(expression, OpCode.BitAnd, functions); case ExpressionKind.BitOr: - return compile_operation(expression, OpCodeEnum.BitOr, functions); + return compile_operation(expression, OpCode.BitOr, functions); case ExpressionKind.BitXOr: - return compile_operation(expression, OpCodeEnum.BitXOr, functions); + return compile_operation(expression, OpCode.BitXOr, functions); case ExpressionKind.BitShiftLeft: - return compile_operation(expression, OpCodeEnum.BitShiftLeft, functions); + return compile_operation(expression, OpCode.BitShiftLeft, functions); case ExpressionKind.BitShiftRight: - return compile_operation(expression, OpCodeEnum.BitShiftRight, functions); + return compile_operation(expression, OpCode.BitShiftRight, functions); case ExpressionKind.Equals: - return compile_operation(expression, OpCodeEnum.Equals, functions); + return compile_operation(expression, OpCode.Equals, functions); case ExpressionKind.NotEquals: - return compile_operation(expression, OpCodeEnum.NotEquals, functions); + return compile_operation(expression, OpCode.NotEquals, functions); case ExpressionKind.LessThan: - return compile_operation(expression, OpCodeEnum.LessThan, functions); + return compile_operation(expression, OpCode.LessThan, functions); case ExpressionKind.LessThanEquals: - return compile_operation(expression, OpCodeEnum.LessThanEquals, functions); + return compile_operation(expression, OpCode.LessThanEquals, functions); case ExpressionKind.GreaterThan: - return compile_operation(expression, OpCodeEnum.GreaterThan, functions); + return compile_operation(expression, OpCode.GreaterThan, functions); case ExpressionKind.GreaterThanEquals: - return compile_operation(expression, OpCodeEnum.GreaterThanEquals, functions); + return compile_operation(expression, OpCode.GreaterThanEquals, functions); case ExpressionKind.And: - return compile_operation(expression, OpCodeEnum.And, functions); + return compile_operation(expression, OpCode.And, functions); case ExpressionKind.Or: - return compile_operation(expression, OpCodeEnum.Or, functions); + return compile_operation(expression, OpCode.Or, functions); case ExpressionKind.Not: - return compile_unary_operation(expression, OpCodeEnum.Not, functions); + return compile_unary_operation(expression, OpCode.Not, functions); case ExpressionKind.Negate: - return compile_unary_operation(expression, OpCodeEnum.Negate, functions); + return compile_unary_operation(expression, OpCode.Negate, functions); case ExpressionKind.Length: - return compile_unary_operation(expression, OpCodeEnum.Length, functions); + return compile_unary_operation(expression, OpCode.Length, functions); case ExpressionKind.BitNot: - return compile_unary_operation(expression, OpCodeEnum.BitNot, functions); + return compile_unary_operation(expression, OpCode.BitNot, functions); } } @@ -278,24 +285,24 @@ function compile_assignment(assignment: AssignmentInterface | undefined, functio const ops: Array = []; const debug = assignment.token.debug; - ops.push({ code: OpCodeEnum.StartStackChange, debug: debug }); + ops.push({ code: OpCode.StartStackChange, debug: debug }); for (const rhs of assignment.rhs) { ops.push(...compile_expression(rhs, functions)); } - ops.push({ code: OpCodeEnum.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }); + ops.push({ code: OpCode.EndStackChange, arg: make_number(assignment.lhs.length), debug: debug }); for (const lhs of assignment.lhs) { - const debug = lhs.token.peek().debug; + const debug: DebugInterface = getDebug(lhs.token); switch (lhs.kind) { case ExpressionKind.Value: { - if (lhs.value?.kind !== ValueKindEnum.Variable) + if (lhs.value?.kind !== ValueKind.Variable) { throw new Error(); } @@ -304,10 +311,10 @@ function compile_assignment(assignment: AssignmentInterface | undefined, functio if (assignment.local) { - ops.push({ code: OpCodeEnum.MakeLocal, arg: identifier, debug: debug }); + ops.push({ code: OpCode.MakeLocal, arg: identifier, debug: debug }); } - ops.push({ code: OpCodeEnum.Store, arg: identifier, debug: debug }); + ops.push({ code: OpCode.Store, arg: identifier, debug: debug }); break; } @@ -316,10 +323,10 @@ function compile_assignment(assignment: AssignmentInterface | undefined, functio // FIXME: Throw error here if `assignment.local` is true. I think? ops.push(...compile_expression(lhs.expression, functions)); - ops.push({ code: OpCodeEnum.Swap, debug: debug }); + ops.push({ code: OpCode.Swap, debug: debug }); ops.push(...compile_expression(lhs.index, functions)); - ops.push({ code: OpCodeEnum.StoreIndex, debug: debug }); - ops.push({ code: OpCodeEnum.Pop, debug: debug }); + ops.push({ code: OpCode.StoreIndex, debug: debug }); + ops.push({ code: OpCode.Pop, debug: debug }); break; } @@ -342,7 +349,7 @@ function compile_local(local: LocalInterface | undefined): Array (name): OpInterface => { return { - code: OpCodeEnum.MakeLocal, + code: OpCode.MakeLocal, arg: { data_type: VariableKind.String, string: name.data, @@ -361,7 +368,7 @@ function compile_inverted_conditional_jump(condition: ExpressionInterface | unde } const ops: Array = []; - const debug = condition.token.peek().debug; + const debug: DebugInterface = getDebug(condition.token); switch (condition.kind) { @@ -392,7 +399,7 @@ function compile_inverted_conditional_jump(condition: ExpressionInterface | unde default: { ops.push(...compile_expression(condition, functions)); - ops.push({ code: OpCodeEnum.JumpIf, arg: make_number(jump_by), debug: debug }); + ops.push({ code: OpCode.JumpIf, arg: make_number(jump_by), debug: debug }); break; } } @@ -408,7 +415,7 @@ function compile_conditional_jump(condition: ExpressionInterface | undefined, ju } const ops: Array = []; - const debug = condition.token.peek().debug; + const debug: DebugInterface = getDebug(condition.token); switch (condition.kind) { @@ -439,7 +446,7 @@ function compile_conditional_jump(condition: ExpressionInterface | undefined, ju default: { ops.push(...compile_expression(condition, functions)); - ops.push({ code: OpCodeEnum.JumpIfNot, arg: make_number(jump_by), debug: debug }); + ops.push({ code: OpCode.JumpIfNot, arg: make_number(jump_by), debug: debug }); break; } } @@ -479,7 +486,7 @@ function compile_if(if_block: IfBlockInterface | undefined, functions: Array = []; const body = compile_block(if_block.body, functions); - ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); + ops.push({ code: OpCode.StartBlock, debug: debug }); ops.push(...compile_conditional_jump(if_block.condition, body.length + 1, functions)); ops.push(...body); @@ -499,7 +506,7 @@ function compile_if(if_block: IfBlockInterface | undefined, functions: Array, offset_from_end: number): void { for (const [i, op] of code.entries()) { - if (op.code === OpCodeEnum.Break) + if (op.code === OpCode.Break) { const offset = code.length - i - 1 + offset_from_end; - op.code = OpCodeEnum.Jump; + op.code = OpCode.Jump; op.arg = make_number(offset); } } @@ -539,11 +546,11 @@ function compile_while(while_block: WhileInterface | undefined, functions: Array replace_breaks(body, 1); - ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); + ops.push({ code: OpCode.StartBlock, debug: debug }); ops.push(...compile_conditional_jump(while_block.condition, body.length + 1, functions)); ops.push(...body); - ops.push({ code: OpCodeEnum.Jump, arg: make_number(-ops.length - 1), debug: debug }); - ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length - 1), debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); return ops; } @@ -562,35 +569,35 @@ function compile_for(for_block: ForInterface | undefined, functions: Array = []; const debug = repeat.token.debug; - ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); + ops.push({ code: OpCode.StartBlock, debug: debug }); ops.push(...compile_block(repeat.body, functions)); ops.push(...compile_inverted_conditional_jump(repeat.condition, 1, functions)); - ops.push({ code: OpCodeEnum.Jump, arg: make_number(-ops.length), debug: debug }); + ops.push({ code: OpCode.Jump, arg: make_number(-ops.length), debug: debug }); - ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); return ops; } @@ -674,9 +681,9 @@ function compile_do(do_block: DoInterface | undefined, functions: Array = []; const debug = do_block.token.debug; - ops.push({ code: OpCodeEnum.StartBlock, debug: debug }); + ops.push({ code: OpCode.StartBlock, debug: debug }); ops.push(...compile_block(do_block.body, functions)); - ops.push({ code: OpCodeEnum.EndBlock, debug: debug }); + ops.push({ code: OpCode.EndBlock, debug: debug }); return ops; } @@ -698,7 +705,7 @@ function compile_return(return_block: ReturnInterface | undefined, functions: Ar const debug = return_block.token.debug; const return_count = return_block.values.length; - ops.push({ code: OpCodeEnum.Return, arg: make_number(return_count), debug: debug }); + ops.push({ code: OpCode.Return, arg: make_number(return_count), debug: debug }); return ops; } @@ -715,7 +722,7 @@ function compile_block(chunk: ChunkInterface, functions: Array): Pro if (extend?.length ?? 0 > 0) { - ops.push({ code: OpCodeEnum.Pop, debug: { line: 0, column: 0 } }); + ops.push({ code: OpCode.Pop, debug: { line: 0, column: 0 } }); } ops.push(...code); if (!has_last_expression) { - ops.push({ code: OpCodeEnum.Push, arg: nil, debug: { line: 0, column: 0 } }); + ops.push({ code: OpCode.Push, arg: nil, debug: { line: 0, column: 0 } }); } return { diff --git a/src/create-binding.mts b/src/create-binding.mts index 3ad7c24..076cbb4 100644 --- a/src/create-binding.mts +++ b/src/create-binding.mts @@ -1,10 +1,9 @@ -import { ValidationError, assertArray, assertDefined, isInstanceOf, isNullish } from "@vitruvius-labs/ts-predicate"; +import { assertArray, assertDefined, isNullish } from "@vitruvius-labs/ts-predicate"; import type { Engine } from "./engine.mjs"; import { make_variable } from "./runtime.mjs"; import { VariableKind } from "./variable/definition/enum/variable-kind.enum.mjs"; import type { Variable } from "./variable/definition/type/variable.type.mjs"; import type { NativeFunction } from "./boundary/definition/type/native-function.type.mjs"; -import { RuntimeError } from "./runtime-error.mjs"; import { VariableUnwrapUtility } from "./variable/unwrap-variable.mjs"; import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; import { handle_error } from "./create-binding/handle-error/handle-error.mjs"; diff --git a/src/engine.mts b/src/engine.mts index da23144..de63a50 100644 --- a/src/engine.mts +++ b/src/engine.mts @@ -19,7 +19,7 @@ import type { VariableFunction } from "./variable/definition/interface/variable- import type { VariableNativeFunction } from "./variable/definition/interface/variable-native-function.interface.mjs"; import { equals } from "./variable/equals.mjs"; import { assertVariable } from "./variable/predicate/assert-variable.mjs"; -import { OpCodeEnum } from "./opcode/definition/enum/op-code.enum.mjs"; +import { OpCode } from "./opcode/definition/enum/op-code.enum.mjs"; import { op_code_name } from "./opcode/op-code-name/op-code-name.mjs"; import type { OpInterface } from "./opcode/definition/interface/op.interface.mjs"; import { index } from "./engine/index/index.mjs"; @@ -83,7 +83,7 @@ export class Engine load(chunk: string): undefined | Error { - const stream = new TokenStream(); + const stream: TokenStream = new TokenStream(); stream.feed(chunk); @@ -358,7 +358,7 @@ export class Engine switch (code) { - case OpCodeEnum.Pop: + case OpCode.Pop: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; @@ -366,7 +366,7 @@ export class Engine break; } - case OpCodeEnum.Dup: + case OpCode.Dup: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; const items = this.stack.splice(this.stack.length - count, count); @@ -375,7 +375,7 @@ export class Engine break; } - case OpCodeEnum.Swap: + case OpCode.Swap: { const x = this.stack.splice(this.stack.length - 2, 1); @@ -383,13 +383,13 @@ export class Engine break; } - case OpCodeEnum.IterUpdateState: + case OpCode.IterUpdateState: { this.stack[this.stack.length - 2] = this.stack_get(-1); break; } - case OpCodeEnum.IterNext: + case OpCode.IterNext: { const state = this.stack_get(-1); const control = this.stack_get(-2); @@ -421,7 +421,7 @@ export class Engine break; } - case OpCodeEnum.IterJumpIfDone: + case OpCode.IterJumpIfDone: { if (isVariableKind(arg, VariableKind.Number) && isNil(this.stack_get(-1))) { @@ -431,7 +431,7 @@ export class Engine break; } - case OpCodeEnum.Add: + case OpCode.Add: this.operation( (x, y) => { @@ -440,7 +440,7 @@ export class Engine ); break; - case OpCodeEnum.Subtract: + case OpCode.Subtract: this.operation( (x, y) => { @@ -449,7 +449,7 @@ export class Engine ); break; - case OpCodeEnum.Multiply: + case OpCode.Multiply: this.operation( (x, y) => { @@ -458,7 +458,7 @@ export class Engine ); break; - case OpCodeEnum.Divide: + case OpCode.Divide: this.operation( (x, y) => { @@ -467,7 +467,7 @@ export class Engine ); break; - case OpCodeEnum.FloorDivide: + case OpCode.FloorDivide: this.operation( (x, y) => { @@ -476,7 +476,7 @@ export class Engine ); break; - case OpCodeEnum.Modulo: + case OpCode.Modulo: this.operation( (x, y) => { @@ -485,7 +485,7 @@ export class Engine ); break; - case OpCodeEnum.Exponent: + case OpCode.Exponent: this.operation( (x, y) => { @@ -495,7 +495,7 @@ export class Engine break; - case OpCodeEnum.LessThan: + case OpCode.LessThan: this.compare( (x, y) => { @@ -504,7 +504,7 @@ export class Engine ); break; - case OpCodeEnum.LessThanEquals: + case OpCode.LessThanEquals: this.compare( (x, y) => { @@ -513,7 +513,7 @@ export class Engine ); break; - case OpCodeEnum.GreaterThan: + case OpCode.GreaterThan: this.compare( (x, y) => { @@ -522,7 +522,7 @@ export class Engine ); break; - case OpCodeEnum.GreaterThanEquals: + case OpCode.GreaterThanEquals: this.compare( (x, y) => { @@ -532,7 +532,7 @@ export class Engine break; - case OpCodeEnum.BitAnd: + case OpCode.BitAnd: this.operation( (x, y) => { @@ -541,7 +541,7 @@ export class Engine ); break; - case OpCodeEnum.BitOr: + case OpCode.BitOr: this.operation( (x, y) => { @@ -550,7 +550,7 @@ export class Engine ); break; - case OpCodeEnum.BitXOr: + case OpCode.BitXOr: this.operation( (x, y) => { @@ -559,7 +559,7 @@ export class Engine ); break; - case OpCodeEnum.BitShiftLeft: + case OpCode.BitShiftLeft: this.operation( (x, y) => { @@ -568,7 +568,7 @@ export class Engine ); break; - case OpCodeEnum.BitShiftRight: + case OpCode.BitShiftRight: this.operation( (x, y) => { @@ -578,7 +578,7 @@ export class Engine break; - case OpCodeEnum.Concat: + case OpCode.Concat: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -589,7 +589,7 @@ export class Engine break; } - case OpCodeEnum.Equals: + case OpCode.Equals: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -598,7 +598,7 @@ export class Engine break; } - case OpCodeEnum.NotEquals: + case OpCode.NotEquals: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -607,7 +607,7 @@ export class Engine break; } - case OpCodeEnum.And: + case OpCode.And: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -618,7 +618,7 @@ export class Engine break; } - case OpCodeEnum.Or: + case OpCode.Or: { const x = this.stack_pop(); const y = this.stack_pop(); @@ -629,23 +629,23 @@ export class Engine break; } - case OpCodeEnum.Not: + case OpCode.Not: this.stack.push(make_boolean(!is_true(this.stack_pop()))); break; - case OpCodeEnum.BitNot: + case OpCode.BitNot: this.stack.push(make_number(~this.stack_pop_kind(VariableKind.Number).number)); break; - case OpCodeEnum.Negate: + case OpCode.Negate: this.stack.push(make_number(-this.stack_pop_kind(VariableKind.Number).number)); break; - case OpCodeEnum.IsNotNil: + case OpCode.IsNotNil: this.stack.push(make_boolean(!isNil(this.stack_pop()))); break; - case OpCodeEnum.Jump: + case OpCode.Jump: if (isVariableKind(arg, VariableKind.Number)) { this.ip = this.ip + arg.number; @@ -653,7 +653,7 @@ export class Engine break; - case OpCodeEnum.JumpIfNot: + case OpCode.JumpIfNot: if (isVariableKind(arg, VariableKind.Number) && !is_true(this.stack_pop())) { this.ip = this.ip + arg.number; @@ -661,7 +661,7 @@ export class Engine break; - case OpCodeEnum.JumpIf: + case OpCode.JumpIf: if (isVariableKind(arg, VariableKind.Number) && is_true(this.stack_pop())) { this.ip = this.ip + arg.number; @@ -669,7 +669,7 @@ export class Engine break; - case OpCodeEnum.MakeLocal: + case OpCode.MakeLocal: const last_locals = this.locals_stack.at(-1); if (last_locals) @@ -679,19 +679,19 @@ export class Engine break; - case OpCodeEnum.NewTable: + case OpCode.NewTable: this.stack.push(make_table()); break; - case OpCodeEnum.StartBlock: + case OpCode.StartBlock: this.locals_stack.push(new Map()); break; - case OpCodeEnum.EndBlock: + case OpCode.EndBlock: this.locals_stack.pop(); break; - case OpCodeEnum.Length: + case OpCode.Length: { const variable = this.stack_pop_maybe(); @@ -711,7 +711,7 @@ export class Engine break; } - case OpCodeEnum.Return: + case OpCode.Return: { this.ip = this.call_stack.pop() ?? this.program.length; this.locals_stack = this.locals_stack.slice(0, this.call_stack.pop()); @@ -719,7 +719,7 @@ export class Engine break; } - case OpCodeEnum.LoadIndex: + case OpCode.LoadIndex: { const table = this.stack_pop_maybe(); @@ -748,7 +748,7 @@ export class Engine break; } - case OpCodeEnum.StoreIndex: + case OpCode.StoreIndex: { const count = isVariableKind(arg, VariableKind.Number) ? arg.number : 1; const table = this.stack_get(-1 - count * 2); @@ -771,7 +771,7 @@ export class Engine break; } - case OpCodeEnum.Store: + case OpCode.Store: { const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; const value = this.stack_pop_maybe(); @@ -794,7 +794,7 @@ export class Engine break; } - case OpCodeEnum.Push: + case OpCode.Push: { if (this.locals_stack.length > 0 && isVariableKind(arg, VariableKind.Function)) { @@ -805,7 +805,7 @@ export class Engine break; } - case OpCodeEnum.Load: + case OpCode.Load: { const name = isVariableKind(arg, VariableKind.String) ? arg.string : ""; const local = [...this.locals_capture, ...this.locals_stack] @@ -828,7 +828,7 @@ export class Engine break; } - case OpCodeEnum.Call: + case OpCode.Call: { const x = this.stack_pop_maybe(); const count = isVariableKind(x, VariableKind.Number) ? x.number : 0; @@ -883,7 +883,7 @@ export class Engine break; } - case OpCodeEnum.ArgumentCount: + case OpCode.ArgumentCount: { const x = this.stack_pop_maybe(); const got = isVariableKind(x, VariableKind.Number) ? x.number : 0; @@ -893,13 +893,13 @@ export class Engine break; } - case OpCodeEnum.StartStackChange: + case OpCode.StartStackChange: { this.assign_height_stack.push(this.stack.length); break; } - case OpCodeEnum.EndStackChange: + case OpCode.EndStackChange: { const got = this.stack.length - (this.assign_height_stack.pop() ?? 0); const expected = isVariableKind(arg, VariableKind.Number) ? arg.number : 0; diff --git a/src/lexer.mts b/src/lexer.mts index a9924eb..ec99d8a 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,75 +1,77 @@ -import { StateEnum } from "./lexer/definition/enum/state.enum.mjs"; -import { TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; +import { State, type StateEnum } from "./lexer/definition/enum/state.enum.mjs"; +import { TokenKind, type TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; const single_token_map: Map = new Map([ - ["(", TokenKindEnum.OpenBrace], - [")", TokenKindEnum.CloseBrace], - ["[", TokenKindEnum.OpenSquare], - ["]", TokenKindEnum.CloseSquare], - ["{", TokenKindEnum.SquiglyOpen], - ["}", TokenKindEnum.SquiglyClose], - - ["+", TokenKindEnum.Addition], - ["-", TokenKindEnum.Subtract], - ["*", TokenKindEnum.Multiply], - ["/", TokenKindEnum.Division], - ["%", TokenKindEnum.Modulo], - ["^", TokenKindEnum.Exponent], - ["&", TokenKindEnum.BitAnd], - ["|", TokenKindEnum.BitOr], - ["~", TokenKindEnum.BitXOrNot], - - ["<", TokenKindEnum.LessThan], - [">", TokenKindEnum.GreaterThan], - - ["=", TokenKindEnum.Assign], - [";", TokenKindEnum.Semicolon], - [",", TokenKindEnum.Comma], - [".", TokenKindEnum.Dot], - ["#", TokenKindEnum.Hash], + ["(", TokenKind.OpenBrace], + [")", TokenKind.CloseBrace], + ["[", TokenKind.OpenSquare], + ["]", TokenKind.CloseSquare], + ["{", TokenKind.SquiglyOpen], + ["}", TokenKind.SquiglyClose], + + ["+", TokenKind.Addition], + ["-", TokenKind.Subtract], + ["*", TokenKind.Multiply], + ["/", TokenKind.Division], + ["%", TokenKind.Modulo], + ["^", TokenKind.Exponent], + ["&", TokenKind.BitAnd], + ["|", TokenKind.BitOr], + ["~", TokenKind.BitXOrNot], + + ["<", TokenKind.LessThan], + [">", TokenKind.GreaterThan], + + ["=", TokenKind.Assign], + [";", TokenKind.Semicolon], + [",", TokenKind.Comma], + [".", TokenKind.Dot], + ["#", TokenKind.Hash], ]); const double_token_map: Map = new Map([ - ["==", TokenKindEnum.Equals], - ["<=", TokenKindEnum.LessThanEquals], - [">=", TokenKindEnum.GreaterThanEquals], - ["~=", TokenKindEnum.NotEquals], - ["..", TokenKindEnum.Concat], - ["//", TokenKindEnum.FloorDivision], - ["<<", TokenKindEnum.BitShiftLeft], - [">>", TokenKindEnum.BitShiftRight], + ["==", TokenKind.Equals], + ["<=", TokenKind.LessThanEquals], + [">=", TokenKind.GreaterThanEquals], + ["~=", TokenKind.NotEquals], + ["..", TokenKind.Concat], + ["//", TokenKind.FloorDivision], + ["<<", TokenKind.BitShiftLeft], + [">>", TokenKind.BitShiftRight], ]); const keyword_map: Map = new Map([ - ["function", TokenKindEnum.FunctionLike], - ["if", TokenKindEnum.If], - ["while", TokenKindEnum.While], - ["for", TokenKindEnum.For], - ["repeat", TokenKindEnum.Repeat], - ["in", TokenKindEnum.In], - ["do", TokenKindEnum.Do], - ["then", TokenKindEnum.Then], - ["elseif", TokenKindEnum.ElseIf], - ["else", TokenKindEnum.Else], - ["until", TokenKindEnum.Until], - ["end", TokenKindEnum.End], - ["return", TokenKindEnum.Return], - ["break", TokenKindEnum.Break], - - ["and", TokenKindEnum.And], - ["or", TokenKindEnum.Or], - ["not", TokenKindEnum.Not], - - ["true", TokenKindEnum.BooleanLiteral], - ["false", TokenKindEnum.BooleanLiteral], - ["nil", TokenKindEnum.NilLiteral], - ["local", TokenKindEnum.Local], + ["function", TokenKind.FunctionLike], + ["if", TokenKind.If], + ["while", TokenKind.While], + ["for", TokenKind.For], + ["repeat", TokenKind.Repeat], + ["in", TokenKind.In], + ["do", TokenKind.Do], + ["then", TokenKind.Then], + ["elseif", TokenKind.ElseIf], + ["else", TokenKind.Else], + ["until", TokenKind.Until], + ["end", TokenKind.End], + ["return", TokenKind.Return], + ["break", TokenKind.Break], + + ["and", TokenKind.And], + ["or", TokenKind.Or], + ["not", TokenKind.Not], + + ["true", TokenKind.BooleanLiteral], + ["false", TokenKind.BooleanLiteral], + ["nil", TokenKind.NilLiteral], + ["local", TokenKind.Local], ]); export class TokenStream { + public debug: DebugInterface = { line: -1, column: -1 }; + private readonly processing_stream: Array; private readonly peek_queue: Array; @@ -83,7 +85,7 @@ export class TokenStream public constructor() { - this.state = StateEnum.Initial; + this.state = State.Initial; this.processing_stream = []; this.buffer = ""; this.token_start_debug = { line: 0, column: 0 }; @@ -93,7 +95,7 @@ export class TokenStream this.peek_queue = []; } - public peek(count = 1): TokenInterface + public peek(count: number = 1): TokenInterface { while (this.peek_queue.length < count) { @@ -159,7 +161,7 @@ export class TokenStream { this.peek_queue.push({ data: "", - kind: TokenKindEnum.EOF, + kind: TokenKind.EOF, debug: { line: this.line, column: this.column, @@ -172,11 +174,11 @@ export class TokenStream const c = this.current() ?? "\0"; if (/\s/.test(c)) - { -this.consume(); + { + this.consume(); - return; -} + return; + } if (this.processing_stream.length > 1) { @@ -184,14 +186,14 @@ this.consume(); if (double === "--") { - this.state = StateEnum.Comment; + this.state = State.Comment; return; } if (double === "[[") { - this.state = StateEnum.MultiLineString; + this.state = State.MultiLineString; this.start_token(); this.consume(); this.consume(); @@ -241,7 +243,7 @@ this.consume(); { this.start_token(); this.consume(); - this.state = StateEnum.StringLiteral; + this.state = State.StringLiteral; return; } @@ -249,7 +251,7 @@ this.consume(); if (/[a-zA-Z_]/.test(c)) { this.start_token(); - this.state = StateEnum.Identifier; + this.state = State.Identifier; return; } @@ -257,7 +259,7 @@ this.consume(); if (/[0-9]/.test(c)) { this.start_token(); - this.state = StateEnum.NumberLiteral; + this.state = State.NumberLiteral; } } @@ -271,18 +273,18 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.StringLiteral, + kind: TokenKind.StringLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; return; } if (c === "\\") { - this.state = StateEnum.StringLiteralEscape; + this.state = State.StringLiteralEscape; return; } @@ -295,7 +297,7 @@ this.consume(); const c = this.current(); this.consume(); - this.state = StateEnum.StringLiteral; + this.state = State.StringLiteral; switch (c) { @@ -320,12 +322,12 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.StringLiteral, + kind: TokenKind.StringLiteral, debug: this.token_start_debug, }); this.consume(); - this.state = StateEnum.Initial; + this.state = State.Initial; return; } @@ -343,11 +345,11 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: kind ?? TokenKindEnum.Identifier, + kind: kind ?? TokenKind.Identifier, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; return; } @@ -372,7 +374,7 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = StateEnum.NumberLiteralDot; + this.state = State.NumberLiteralDot; return; } @@ -381,7 +383,7 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = StateEnum.NumberLiteralExp; + this.state = State.NumberLiteralExp; return; } @@ -392,17 +394,17 @@ this.consume(); { this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; return; } this.buffer = this.buffer + c; - this.state = StateEnum.NumberHex; + this.state = State.NumberHex; this.consume(); return; @@ -410,11 +412,11 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; } private number_dot() @@ -432,7 +434,7 @@ this.consume(); if (c === "e" || c === "E") { this.buffer = this.buffer + c; - this.state = StateEnum.NumberLiteralExpSign; + this.state = State.NumberLiteralExpSign; this.consume(); return; @@ -440,11 +442,11 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; } private number_exp_sign() @@ -455,18 +457,18 @@ this.consume(); { this.buffer = this.buffer + c; this.consume(); - this.state = StateEnum.NumberLiteralExp; + this.state = State.NumberLiteralExp; return; } this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; } private number_exp() @@ -483,11 +485,11 @@ this.consume(); this.peek_queue.push({ data: this.buffer, - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; } private number_hex() @@ -504,11 +506,11 @@ this.consume(); this.peek_queue.push({ data: parseInt(this.buffer.slice(2), 16).toString(), - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, debug: this.token_start_debug, }); - this.state = StateEnum.Initial; + this.state = State.Initial; } private comment() @@ -519,7 +521,7 @@ this.consume(); if (c === "\n") { - this.state = StateEnum.Initial; + this.state = State.Initial; } } @@ -529,9 +531,9 @@ this.consume(); { this.peek_queue.push({ data: "", - kind: this.state === StateEnum.Initial - ? TokenKindEnum.EOF - : TokenKindEnum.NotFinished, + kind: this.state === State.Initial + ? TokenKind.EOF + : TokenKind.NotFinished, debug: { line: this.line, column: this.column, @@ -543,37 +545,37 @@ this.consume(); switch (this.state) { - case StateEnum.Initial: + case State.Initial: this.initial(); break; - case StateEnum.Identifier: + case State.Identifier: this.read_identifier(); break; - case StateEnum.StringLiteral: + case State.StringLiteral: this.read_string(); break; - case StateEnum.StringLiteralEscape: + case State.StringLiteralEscape: this.read_string_escape(); break; - case StateEnum.MultiLineString: + case State.MultiLineString: this.read_multi_line_string(); break; - case StateEnum.NumberLiteral: + case State.NumberLiteral: this.number(); break; - case StateEnum.NumberLiteralDot: + case State.NumberLiteralDot: this.number_dot(); break; - case StateEnum.NumberLiteralExpSign: + case State.NumberLiteralExpSign: this.number_exp_sign(); break; - case StateEnum.NumberLiteralExp: + case State.NumberLiteralExp: this.number_exp(); break; - case StateEnum.NumberHex: + case State.NumberHex: this.number_hex(); break; - case StateEnum.Comment: + case State.Comment: this.comment(); break; } diff --git a/src/lexer/definition/enum/state.enum.mts b/src/lexer/definition/enum/state.enum.mts index a2f9cef..bfef1fb 100644 --- a/src/lexer/definition/enum/state.enum.mts +++ b/src/lexer/definition/enum/state.enum.mts @@ -1,15 +1,17 @@ -const enum StateEnum { - Initial = "initial", - Identifier = "identifier", - StringLiteral = "string-literal", - StringLiteralEscape = "string-literal-escape", - MultiLineString = "multi-line-string", - NumberLiteral = "number-literal", - NumberLiteralDot = "number-literal-dot", - NumberLiteralExpSign = "number-literal-exp-sign", - NumberLiteralExp = "number-literal-exp", - NumberHex = "number-hex", - Comment = "comment", -} +const State = { + Initial: "initial", + Identifier: "identifier", + StringLiteral: "string-literal", + StringLiteralEscape: "string-literal-escape", + MultiLineString: "multi-line-string", + NumberLiteral: "number-literal", + NumberLiteralDot: "number-literal-dot", + NumberLiteralExpSign: "number-literal-exp-sign", + NumberLiteralExp: "number-literal-exp", + NumberHex: "number-hex", + Comment: "comment", +} as const satisfies Record; -export { StateEnum }; +type StateEnum = typeof State[keyof typeof State]; + +export { State, type StateEnum }; diff --git a/src/lexer/definition/enum/token-kind.enum.mts b/src/lexer/definition/enum/token-kind.enum.mts index 587680c..2742964 100644 --- a/src/lexer/definition/enum/token-kind.enum.mts +++ b/src/lexer/definition/enum/token-kind.enum.mts @@ -1,66 +1,69 @@ -const enum TokenKindEnum { - EOF = "EOF", - NotFinished = "not-finished", +/* eslint-disable id-length */ +const TokenKind = { + EOF: "EOF", + NotFinished: "not-finished", - Identifier = "identifier", - StringLiteral = "string-literal", - BooleanLiteral = "boolean-literal", - NumberLiteral = "number-literal", - NilLiteral = "nil-literal", + Identifier: "identifier", + StringLiteral: "string-literal", + BooleanLiteral: "boolean-literal", + NumberLiteral: "number-literal", + NilLiteral: "nil-literal", - OpenBrace = "open-brace", - CloseBrace = "close-brace", - OpenSquare = "open-square", - CloseSquare = "close-square", - SquiglyOpen = "squigly-open", - SquiglyClose = "squigly-close", + OpenBrace: "open-brace", + CloseBrace: "close-brace", + OpenSquare: "open-square", + CloseSquare: "close-square", + SquiglyOpen: "squigly-open", + SquiglyClose: "squigly-close", - Addition = "addition", - Subtract = "subtract", - Multiply = "multiply", - Division = "division", - FloorDivision = "floor-division", - Modulo = "modulo", - Exponent = "exponent", - Concat = "concat", - Hash = "hash", + Addition: "addition", + Subtract: "subtract", + Multiply: "multiply", + Division: "division", + FloorDivision: "floor-division", + Modulo: "modulo", + Exponent: "exponent", + Concat: "concat", + Hash: "hash", - BitAnd = "bit-and", - BitOr = "bit-or", - BitXOrNot = "bit-xor-not", - BitShiftLeft = "bit-shift-left", - BitShiftRight = "bit-shift-right", + BitAnd: "bit-and", + BitOr: "bit-or", + BitXOrNot: "bit-xor-not", + BitShiftLeft: "bit-shift-left", + BitShiftRight: "bit-shift-right", - Equals = "equals", - NotEquals = "not-equals", - LessThan = "less-than", - LessThanEquals = "less-than-equals", - GreaterThan = "greater-than", - GreaterThanEquals = "greater-than-equals", - And = "and", - Or = "or", - Not = "not", + Equals: "equals", + NotEquals: "not-equals", + LessThan: "less-than", + LessThanEquals: "less-than-equals", + GreaterThan: "greater-than", + GreaterThanEquals: "greater-than-equals", + And: "and", + Or: "or", + Not: "not", - Assign = "assign", - Semicolon = "semicolon", - Comma = "comma", - Dot = "dot", + Assign: "assign", + Semicolon: "semicolon", + Comma: "comma", + Dot: "dot", - FunctionLike = "function", - If = "if", - While = "while", - For = "for", - Repeat = "repeat", - In = "in", - Do = "do", - Then = "then", - ElseIf = "elseif", - Else = "else", - Until = "until", - End = "end", - Return = "return", - Break = "break", - Local = "local", -} + FunctionLike: "function", + If: "if", + While: "while", + For: "for", + Repeat: "repeat", + In: "in", + Do: "do", + Then: "then", + ElseIf: "elseif", + Else: "else", + Until: "until", + End: "end", + Return: "return", + Break: "break", + Local: "local", +} as const satisfies Record; -export { TokenKindEnum }; +type TokenKindEnum = typeof TokenKind[keyof typeof TokenKind]; + +export { TokenKind, type TokenKindEnum }; diff --git a/src/lexer/definition/interface/token.interface.mts b/src/lexer/definition/interface/token.interface.mts index b49c97f..e96517c 100644 --- a/src/lexer/definition/interface/token.interface.mts +++ b/src/lexer/definition/interface/token.interface.mts @@ -1,7 +1,8 @@ import type { TokenKindEnum } from "../enum/token-kind.enum.mjs"; import type { DebugInterface } from "./debug.interface.mjs"; -interface TokenInterface { +interface TokenInterface +{ data: string; kind: TokenKindEnum; debug: DebugInterface; diff --git a/src/lexer/token-kind-to-string/token-kind-to-string.mts b/src/lexer/token-kind-to-string/token-kind-to-string.mts index 792ff3b..7e8c209 100644 --- a/src/lexer/token-kind-to-string/token-kind-to-string.mts +++ b/src/lexer/token-kind-to-string/token-kind-to-string.mts @@ -1,62 +1,120 @@ -import { TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; +import { TokenKind, type TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; -// eslint-disable-next-line complexity +// eslint-disable-next-line complexity, max-lines-per-function function token_kind_to_string(kind: TokenKindEnum): string { switch (kind) { - case TokenKindEnum.EOF: return "EOF"; - case TokenKindEnum.NotFinished: return "NotFinished"; - case TokenKindEnum.Identifier: return "Identifier"; - case TokenKindEnum.StringLiteral: return "StringLiteral"; - case TokenKindEnum.BooleanLiteral: return "BooleanLiteral"; - case TokenKindEnum.NumberLiteral: return "NumberLiteral"; - case TokenKindEnum.NilLiteral: return "nil"; - case TokenKindEnum.OpenBrace: return "("; - case TokenKindEnum.CloseBrace: return ")"; - case TokenKindEnum.OpenSquare: return "["; - case TokenKindEnum.CloseSquare: return "]"; - case TokenKindEnum.SquiglyOpen: return "{"; - case TokenKindEnum.SquiglyClose: return "}"; - case TokenKindEnum.Addition: return "+"; - case TokenKindEnum.Subtract: return "-"; - case TokenKindEnum.Multiply: return "*"; - case TokenKindEnum.Division: return "/"; - case TokenKindEnum.FloorDivision: return "//"; - case TokenKindEnum.Modulo: return "%"; - case TokenKindEnum.Exponent: return "^"; - case TokenKindEnum.BitAnd: return "&"; - case TokenKindEnum.BitOr: return "|"; - case TokenKindEnum.BitXOrNot: return "~"; - case TokenKindEnum.BitShiftLeft: return "<<"; - case TokenKindEnum.BitShiftRight: return ">>"; - case TokenKindEnum.LessThan: return "<"; - case TokenKindEnum.GreaterThan: return ">"; - case TokenKindEnum.And: return "and"; - case TokenKindEnum.Or: return "or"; - case TokenKindEnum.Not: return "not"; - case TokenKindEnum.Assign: return "="; - case TokenKindEnum.Semicolon: return ";"; - case TokenKindEnum.Comma: return ","; - case TokenKindEnum.Dot: return "."; - case TokenKindEnum.FunctionLike: return "function"; - case TokenKindEnum.If: return "if"; - case TokenKindEnum.While: return "while"; - case TokenKindEnum.For: return "for"; - case TokenKindEnum.Repeat: return "repeat"; - case TokenKindEnum.In: return "in"; - case TokenKindEnum.Do: return "do"; - case TokenKindEnum.Then: return "then"; - case TokenKindEnum.ElseIf: return "elseif"; - case TokenKindEnum.Else: return "else"; - case TokenKindEnum.Until: return "until"; - case TokenKindEnum.End: return "end"; - case TokenKindEnum.Return: return "return"; - case TokenKindEnum.Break: return "break"; - case TokenKindEnum.Local: return "local"; - - default: - return "unknown token"; + case TokenKind.EOF: + return "EOF"; + case TokenKind.NotFinished: + return "NotFinished"; + case TokenKind.Identifier: + return "Identifier"; + case TokenKind.StringLiteral: + return "StringLiteral"; + case TokenKind.BooleanLiteral: + return "BooleanLiteral"; + case TokenKind.NumberLiteral: + return "NumberLiteral"; + case TokenKind.NilLiteral: + return "nil"; + case TokenKind.OpenBrace: + return "("; + case TokenKind.CloseBrace: + return ")"; + case TokenKind.OpenSquare: + return "["; + case TokenKind.CloseSquare: + return "]"; + case TokenKind.SquiglyOpen: + return "{"; + case TokenKind.SquiglyClose: + return "}"; + case TokenKind.Addition: + return "+"; + case TokenKind.Subtract: + return "-"; + case TokenKind.Multiply: + return "*"; + case TokenKind.Division: + return "/"; + case TokenKind.FloorDivision: + return "//"; + case TokenKind.Modulo: + return "%"; + case TokenKind.Exponent: + return "^"; + case TokenKind.BitAnd: + return "&"; + case TokenKind.BitOr: + return "|"; + case TokenKind.BitXOrNot: + return "~"; + case TokenKind.BitShiftLeft: + return "<<"; + case TokenKind.BitShiftRight: + return ">>"; + case TokenKind.Equals: + return "=="; + case TokenKind.NotEquals: + return "~="; + case TokenKind.LessThan: + return "<"; + case TokenKind.LessThanEquals: + return "<="; + case TokenKind.GreaterThan: + return ">"; + case TokenKind.GreaterThanEquals: + return ">="; + case TokenKind.And: + return "and"; + case TokenKind.Or: + return "or"; + case TokenKind.Not: + return "not"; + case TokenKind.Assign: + return "="; + case TokenKind.Semicolon: + return ";"; + case TokenKind.Comma: + return ","; + case TokenKind.Dot: + return "."; + case TokenKind.FunctionLike: + return "function"; + case TokenKind.If: + return "if"; + case TokenKind.While: + return "while"; + case TokenKind.For: + return "for"; + case TokenKind.Repeat: + return "repeat"; + case TokenKind.In: + return "in"; + case TokenKind.Do: + return "do"; + case TokenKind.Then: + return "then"; + case TokenKind.ElseIf: + return "elseif"; + case TokenKind.Else: + return "else"; + case TokenKind.Until: + return "until"; + case TokenKind.End: + return "end"; + case TokenKind.Return: + return "return"; + case TokenKind.Break: + return "break"; + case TokenKind.Local: + return "local"; + case TokenKind.Concat: + return ".."; + case TokenKind.Hash: + return "#"; } } diff --git a/src/lexer/utility/get-debug.mts b/src/lexer/utility/get-debug.mts new file mode 100644 index 0000000..4b452ea --- /dev/null +++ b/src/lexer/utility/get-debug.mts @@ -0,0 +1,15 @@ +import type { DebugInterface } from "../definition/interface/debug.interface.mjs"; +import type { TokenInterface } from "../definition/interface/token.interface.mjs"; +import { TokenStream } from "../../lexer.mjs"; + +function getDebug(token: TokenInterface | TokenStream): DebugInterface +{ + if (token instanceof TokenStream) + { + return token.peek().debug; + } + + return token.debug; +} + +export { getDebug }; diff --git a/src/lib/type/type.mts b/src/lib/type/type.mts index 3785547..a830aad 100644 --- a/src/lib/type/type.mts +++ b/src/lib/type/type.mts @@ -1,6 +1,6 @@ import type { Variable } from "../../_index.mjs"; import type { Engine } from "../../engine.mjs"; -import { make_string } from "../../runtime.mjs"; +import { make_string } from "../../runtime/make-string/make-string.mjs"; // @ts-expect-error - engine is unused for now. function type(engine: Engine, variable: Variable): Array diff --git a/src/opcode/_index.mts b/src/opcode/_index.mts new file mode 100644 index 0000000..e69de29 diff --git a/src/opcode/definition/enum/op-code.enum.mts b/src/opcode/definition/enum/op-code.enum.mts index 5589165..3606929 100644 --- a/src/opcode/definition/enum/op-code.enum.mts +++ b/src/opcode/definition/enum/op-code.enum.mts @@ -1,63 +1,65 @@ -const enum OpCodeEnum { - Load = "load", - Store = "store", - Push = "push", - Pop = "pop", - Dup = "dup", - Swap = "swap", - - IterUpdateState = "iter_update_state", - IterNext = "iter_next", - IterJumpIfDone = "iter_jump_if_done", - - NewTable = "new_table", - LoadIndex = "load_index", - StoreIndex = "store_index", - - Add = "add", - Subtract = "subtract", - Multiply = "multiply", - Divide = "divide", - FloorDivide = "floor_divide", - Modulo = "modulo", - Exponent = "exponent", - Concat = "concat", - - BitAnd = "bit_and", - BitOr = "bit_or", - BitXOr = "bit_xor", - BitNot = "bit_not", - BitShiftLeft = "bit_shift_left", - BitShiftRight = "bit_shift_right", - - Equals = "equals", - NotEquals = "not_equals", - LessThan = "less_than", - LessThanEquals = "less_than_equals", - GreaterThan = "greater_than", - GreaterThanEquals = "greater_than_equals", - - And = "and", - Or = "or", - Not = "not", - - Negate = "negate", - Length = "length", - IsNotNil = "is_not_nil", - - StartBlock = "start_block", - EndBlock = "end_block", - MakeLocal = "make_local", - Call = "call", - Return = "return", - Jump = "jump", - JumpIfNot = "jump_if_not", - JumpIf = "jump_if", - - StartStackChange = "start_stack_change", - EndStackChange = "end_stack_change", - ArgumentCount = "argument_count", - Break = "break", -} - -export { OpCodeEnum }; +const OpCode = { + Load: "load", + Store: "store", + Push: "push", + Pop: "pop", + Dup: "dup", + Swap: "swap", + + IterUpdateState: "iter-update-state", + IterNext: "iter-next", + IterJumpIfDone: "iter-jump-if-done", + + NewTable: "new-table", + LoadIndex: "load-index", + StoreIndex: "store-index", + + Add: "add", + Subtract: "subtract", + Multiply: "multiply", + Divide: "divide", + FloorDivide: "floor-divide", + Modulo: "modulo", + Exponent: "exponent", + Concat: "concat", + + BitAnd: "bit-and", + BitOr: "bit-or", + BitXOr: "bit-xor", + BitNot: "bit-not", + BitShiftLeft: "bit-shift-left", + BitShiftRight: "bit-shift-right", + + Equals: "equals", + NotEquals: "not-equals", + LessThan: "less-than", + LessThanEquals: "less-than-equals", + GreaterThan: "greater-than", + GreaterThanEquals: "greater-than-equals", + + And: "and", + Or: "or", + Not: "not", + + Negate: "negate", + Length: "length", + IsNotNil: "is-not-nil", + + StartBlock: "start-block", + EndBlock: "end-block", + MakeLocal: "make-local", + Call: "call", + Return: "return", + Jump: "jump", + JumpIfNot: "jump-if-not", + JumpIf: "jump-if", + + StartStackChange: "start-stack-change", + EndStackChange: "end-stack-change", + ArgumentCount: "argument-count", + Break: "break", +} as const satisfies Record; + +type OpCodeEnum = typeof OpCode[keyof typeof OpCode]; + +export { OpCode, type OpCodeEnum }; diff --git a/src/opcode/op-code-name/op-code-name.mts b/src/opcode/op-code-name/op-code-name.mts index 7868e04..5eb9098 100644 --- a/src/opcode/op-code-name/op-code-name.mts +++ b/src/opcode/op-code-name/op-code-name.mts @@ -1,60 +1,110 @@ -import { OpCodeEnum } from "../definition/enum/op-code.enum.mjs"; +import { OpCode, type OpCodeEnum } from "../definition/enum/op-code.enum.mjs"; -// eslint-disable-next-line complexity +// eslint-disable-next-line complexity, max-lines-per-function function op_code_name(op_code: OpCodeEnum): string { switch (op_code) { - case OpCodeEnum.Load: return "Load"; - case OpCodeEnum.Store: return "Store"; - case OpCodeEnum.Push: return "Push"; - case OpCodeEnum.Pop: return "Pop"; - case OpCodeEnum.Dup: return "Dup"; - case OpCodeEnum.Swap: return "Swap"; - case OpCodeEnum.IterUpdateState: return "IterUpdateState"; - case OpCodeEnum.IterNext: return "IterNext"; - case OpCodeEnum.IterJumpIfDone: return "IterJumpIfDone"; - case OpCodeEnum.NewTable: return "NewTable"; - case OpCodeEnum.LoadIndex: return "LoadIndex"; - case OpCodeEnum.StoreIndex: return "StoreIndex"; - case OpCodeEnum.Add: return "Add"; - case OpCodeEnum.Subtract: return "Subtract"; - case OpCodeEnum.Multiply: return "Multiply"; - case OpCodeEnum.Divide: return "Divide"; - case OpCodeEnum.FloorDivide: return "FloorDivide"; - case OpCodeEnum.Modulo: return "Modulo"; - case OpCodeEnum.Exponent: return "Exponent"; - case OpCodeEnum.Concat: return "Concat"; - case OpCodeEnum.BitAnd: return "BitAnd"; - case OpCodeEnum.BitOr: return "BitOr"; - case OpCodeEnum.BitXOr: return "BitXOr"; - case OpCodeEnum.BitNot: return "BitNot"; - case OpCodeEnum.BitShiftLeft: return "BitShiftLeft"; - case OpCodeEnum.BitShiftRight: return "BitShiftRight"; - case OpCodeEnum.Equals: return "Equals"; - case OpCodeEnum.NotEquals: return "NotEquals"; - case OpCodeEnum.LessThan: return "LessThan"; - case OpCodeEnum.LessThanEquals: return "LessThanEquals"; - case OpCodeEnum.GreaterThan: return "GreaterThan"; - case OpCodeEnum.GreaterThanEquals: return "GreaterThanEquals"; - case OpCodeEnum.And: return "And"; - case OpCodeEnum.Or: return "Or"; - case OpCodeEnum.Not: return "Not"; - case OpCodeEnum.Negate: return "Negate"; - case OpCodeEnum.Length: return "Length"; - case OpCodeEnum.IsNotNil: return "IsNotNil"; - case OpCodeEnum.StartBlock: return "StartBlock"; - case OpCodeEnum.EndBlock: return "EndBlock"; - case OpCodeEnum.MakeLocal: return "MakeLocal"; - case OpCodeEnum.Call: return "Call"; - case OpCodeEnum.Return: return "Return"; - case OpCodeEnum.Jump: return "Jump"; - case OpCodeEnum.JumpIfNot: return "JumpIfNot"; - case OpCodeEnum.JumpIf: return "JumpIf"; - case OpCodeEnum.StartStackChange: return "StartStackChange"; - case OpCodeEnum.EndStackChange: return "EndStackChange"; - case OpCodeEnum.ArgumentCount: return "ArgumentCount"; - case OpCodeEnum.Break: return "Break[Debug]"; + case OpCode.Load: + return "Load"; + case OpCode.Store: + return "Store"; + case OpCode.Push: + return "Push"; + case OpCode.Pop: + return "Pop"; + case OpCode.Dup: + return "Dup"; + case OpCode.Swap: + return "Swap"; + case OpCode.IterUpdateState: + return "IterUpdateState"; + case OpCode.IterNext: + return "IterNext"; + case OpCode.IterJumpIfDone: + return "IterJumpIfDone"; + case OpCode.NewTable: + return "NewTable"; + case OpCode.LoadIndex: + return "LoadIndex"; + case OpCode.StoreIndex: + return "StoreIndex"; + case OpCode.Add: + return "Add"; + case OpCode.Subtract: + return "Subtract"; + case OpCode.Multiply: + return "Multiply"; + case OpCode.Divide: + return "Divide"; + case OpCode.FloorDivide: + return "FloorDivide"; + case OpCode.Modulo: + return "Modulo"; + case OpCode.Exponent: + return "Exponent"; + case OpCode.Concat: + return "Concat"; + case OpCode.BitAnd: + return "BitAnd"; + case OpCode.BitOr: + return "BitOr"; + case OpCode.BitXOr: + return "BitXOr"; + case OpCode.BitNot: + return "BitNot"; + case OpCode.BitShiftLeft: + return "BitShiftLeft"; + case OpCode.BitShiftRight: + return "BitShiftRight"; + case OpCode.Equals: + return "Equals"; + case OpCode.NotEquals: + return "NotEquals"; + case OpCode.LessThan: + return "LessThan"; + case OpCode.LessThanEquals: + return "LessThanEquals"; + case OpCode.GreaterThan: + return "GreaterThan"; + case OpCode.GreaterThanEquals: + return "GreaterThanEquals"; + case OpCode.And: + return "And"; + case OpCode.Or: + return "Or"; + case OpCode.Not: + return "Not"; + case OpCode.Negate: + return "Negate"; + case OpCode.Length: + return "Length"; + case OpCode.IsNotNil: + return "IsNotNil"; + case OpCode.StartBlock: + return "StartBlock"; + case OpCode.EndBlock: + return "EndBlock"; + case OpCode.MakeLocal: + return "MakeLocal"; + case OpCode.Call: + return "Call"; + case OpCode.Return: + return "Return"; + case OpCode.Jump: + return "Jump"; + case OpCode.JumpIfNot: + return "JumpIfNot"; + case OpCode.JumpIf: + return "JumpIf"; + case OpCode.StartStackChange: + return "StartStackChange"; + case OpCode.EndStackChange: + return "EndStackChange"; + case OpCode.ArgumentCount: + return "ArgumentCount"; + case OpCode.Break: + return "Break[Debug]"; } } diff --git a/src/optimizer.mts b/src/optimizer.mts index e711a7a..94ebf1d 100644 --- a/src/optimizer.mts +++ b/src/optimizer.mts @@ -1,6 +1,7 @@ +import { isEnumValue } from "@vitruvius-labs/ts-predicate"; import { ExpressionKind } from "./ast/definition/enum/expression-kind.enum.mjs"; -import { StatementKindEnum } from "./ast/definition/enum/statement-kind.enum.mjs"; -import { ValueKindEnum } from "./ast/definition/enum/value-kind.enum.mjs"; +import { StatementKind } from "./ast/definition/enum/statement-kind.enum.mjs"; +import { ValueKind } from "./ast/definition/enum/value-kind.enum.mjs"; import type { AssignmentInterface } from "./ast/definition/interface/assignment.interface.mjs"; import type { ChunkInterface } from "./ast/definition/interface/chunk.interface.mjs"; import type { ExpressionInterface } from "./ast/definition/interface/expression.interface.mjs"; @@ -13,10 +14,10 @@ import type { ValueInterface } from "./ast/definition/interface/value.interface. import type { WhileInterface } from "./ast/definition/interface/while.interface.mjs"; const CONSTANT_VALUES = [ - ValueKindEnum.NilLiteral, - ValueKindEnum.NumberLiteral, - ValueKindEnum.BooleanLiteral, - ValueKindEnum.StringLiteral, + ValueKind.NilLiteral, + ValueKind.NumberLiteral, + ValueKind.BooleanLiteral, + ValueKind.StringLiteral, ]; function compute_arithmetic_operation( @@ -34,7 +35,7 @@ function compute_arithmetic_operation( } return { - kind: ValueKindEnum.NumberLiteral, + kind: ValueKind.NumberLiteral, number: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, }; @@ -55,7 +56,7 @@ function compute_comparison_operation( } return { - kind: ValueKindEnum.BooleanLiteral, + kind: ValueKind.BooleanLiteral, boolean: operation(lhs.number ?? 0, rhs.number ?? 0), token: expression.token, }; @@ -63,7 +64,7 @@ function compute_comparison_operation( function compute_logical_operation( expression: ExpressionInterface, - operation: ExpressionKind.And | ExpressionKind.Or, + operation: typeof ExpressionKind.And | typeof ExpressionKind.Or, constants: Map ): ValueInterface | undefined { @@ -75,7 +76,7 @@ function compute_logical_operation( return undefined; } - const lhs_falsy: boolean = lhs.kind === ValueKindEnum.NilLiteral || lhs.kind === ValueKindEnum.BooleanLiteral && !(lhs.boolean ?? true); + const lhs_falsy: boolean = lhs.kind === ValueKind.NilLiteral || lhs.kind === ValueKind.BooleanLiteral && !(lhs.boolean ?? true); if (operation === ExpressionKind.And) { @@ -86,6 +87,7 @@ function compute_logical_operation( } // @TODO: Fix complexity warning +// eslint-disable-next-line max-lines-per-function, complexity function compute_constant_expression( expression: ExpressionInterface | undefined, constants: Map @@ -107,12 +109,12 @@ function compute_constant_expression( const value = expression.value; - if (CONSTANT_VALUES.includes(value.kind)) + if (isEnumValue(value.kind, CONSTANT_VALUES)) { return value; } - if (value.kind === ValueKindEnum.Variable && constants.has(value.identifier ?? "")) + if (value.kind === ValueKind.Variable && constants.has(value.identifier ?? "")) { return constants.get(value.identifier ?? ""); } @@ -311,7 +313,7 @@ function compute_constant_expression( } return { - kind: ValueKindEnum.BooleanLiteral, + kind: ValueKind.BooleanLiteral, boolean: !(lhs.boolean ?? false), token: expression.token, }; @@ -327,7 +329,7 @@ function compute_constant_expression( } return { - kind: ValueKindEnum.NumberLiteral, + kind: ValueKind.NumberLiteral, number: -(lhs.number ?? 0), token: expression.token, }; @@ -336,7 +338,10 @@ function compute_constant_expression( case ExpressionKind.Length: return undefined; - default: + case ExpressionKind.Call: + return undefined; + + case ExpressionKind.Index: return undefined; } } @@ -568,35 +573,35 @@ export function optimize_chunk(chunk: ChunkInterface, parent_constants?: Map = new Map(); let current_numeric_key = 1; - let tokenInterface: TokenInterface = stream.peek(); - - do + while (stream.peek().kind !== TokenKind.SquiglyClose) { const element: ExpressionInterface | Error = parse_table_key(stream); @@ -104,7 +104,9 @@ function parse_table(stream: TokenStream): ValueInterface | Error return element; } - if (consume(stream, TokenKindEnum.Assign)) + const is_assign: boolean = consume(stream, TokenKind.Assign); + + if (is_assign) { const value = parse_expression(stream); @@ -115,19 +117,20 @@ function parse_table(stream: TokenStream): ValueInterface | Error elements.set(element, value); } - else + + if (!is_assign) { const key_token = { - kind: TokenKindEnum.NumberLiteral, + kind: TokenKind.NumberLiteral, data: current_numeric_key.toString(), - debug: tokenInterface.debug, + debug: getDebug(stream.peek()), }; const key = { kind: ExpressionKind.Value, token: key_token, value: { - kind: ValueKindEnum.NumberLiteral, + kind: ValueKind.NumberLiteral, token: key_token, number: current_numeric_key, }, @@ -137,15 +140,13 @@ function parse_table(stream: TokenStream): ValueInterface | Error elements.set(key, element); } - if (!consume(stream, TokenKindEnum.Comma)) + if (!consume(stream, TokenKind.Comma)) { break; } + } - tokenInterface = stream.peek(); - } while (tokenInterface.kind !== TokenKindEnum.SquiglyClose); - - const close_squigly = expect(stream, TokenKindEnum.SquiglyClose); + const close_squigly = expect(stream, TokenKind.SquiglyClose); if (close_squigly instanceof Error) { @@ -153,7 +154,7 @@ function parse_table(stream: TokenStream): ValueInterface | Error } return { - kind: ValueKindEnum.TableLiteral, + kind: ValueKind.TableLiteral, token: squigly_open, table: elements, }; @@ -166,20 +167,20 @@ function parse_value(stream: TokenStream): ValueInterface | Error // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { - case TokenKindEnum.NumberLiteral: - return { kind: ValueKindEnum.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; - case TokenKindEnum.BooleanLiteral: - return { kind: ValueKindEnum.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; - case TokenKindEnum.StringLiteral: - return { kind: ValueKindEnum.StringLiteral, token: stream.next(), string: token.data }; - case TokenKindEnum.NilLiteral: - return { kind: ValueKindEnum.NilLiteral, token: stream.next() }; - case TokenKindEnum.Identifier: - return { kind: ValueKindEnum.Variable, token: stream.next(), identifier: token.data }; - - case TokenKindEnum.SquiglyOpen: + case TokenKind.NumberLiteral: + return { kind: ValueKind.NumberLiteral, token: stream.next(), number: parseFloat(token.data) }; + case TokenKind.BooleanLiteral: + return { kind: ValueKind.BooleanLiteral, token: stream.next(), boolean: token.data === "true" }; + case TokenKind.StringLiteral: + return { kind: ValueKind.StringLiteral, token: stream.next(), string: token.data }; + case TokenKind.NilLiteral: + return { kind: ValueKind.NilLiteral, token: stream.next() }; + case TokenKind.Identifier: + return { kind: ValueKind.Variable, token: stream.next(), identifier: token.data }; + + case TokenKind.SquiglyOpen: return parse_table(stream); - case TokenKindEnum.FunctionLike: + case TokenKind.FunctionLike: return parse_function_value(stream.next(), stream); default: @@ -208,7 +209,7 @@ function parse_unary_operator(stream: TokenStream): ExpressionInterface | Error function parse_value_expression(stream: TokenStream): ExpressionInterface | Error { - if (consume(stream, TokenKindEnum.OpenBrace)) + if (consume(stream, TokenKind.OpenBrace)) { const sub_expression = parse_expression(stream); @@ -217,7 +218,7 @@ function parse_value_expression(stream: TokenStream): ExpressionInterface | Erro return sub_expression; } - const close_brace = expect(stream, TokenKindEnum.CloseBrace); + const close_brace = expect(stream, TokenKind.CloseBrace); if (close_brace instanceof Error) { @@ -227,7 +228,7 @@ function parse_value_expression(stream: TokenStream): ExpressionInterface | Erro return sub_expression; } - if (UNARY.includes(stream.peek().kind)) + if (isEnumValue(stream.peek().kind, UNARY)) { return parse_unary_operator(stream); } @@ -248,12 +249,12 @@ function parse_value_expression(stream: TokenStream): ExpressionInterface | Erro return parse_access_expression(expression_value, stream); } -function parse_call(func: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error +function parse_call(callable: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { - const open_brace = stream.next(); + const open_brace: TokenInterface = stream.next(); const args: Array = []; - while (stream.peek().kind !== TokenKindEnum.CloseBrace) + while (stream.peek().kind !== TokenKind.CloseBrace) { const argument = parse_expression(stream); @@ -264,13 +265,13 @@ function parse_call(func: ExpressionInterface, stream: TokenStream): ExpressionI args.push(argument); - if (!consume(stream, TokenKindEnum.Comma)) + if (!consume(stream, TokenKind.Comma)) { break; } } - const close_brace = expect(stream, TokenKindEnum.CloseBrace); + const close_brace = expect(stream, TokenKind.CloseBrace); if (close_brace instanceof Error) { @@ -280,7 +281,7 @@ function parse_call(func: ExpressionInterface, stream: TokenStream): ExpressionI return parse_access_expression({ kind: ExpressionKind.Call, token: open_brace, - expression: func, + expression: callable, arguments: args, }, stream); } @@ -295,7 +296,7 @@ function parse_index(table: ExpressionInterface, stream: TokenStream): Expressio return index; } - const close_square = expect(stream, TokenKindEnum.CloseSquare); + const close_square = expect(stream, TokenKind.CloseSquare); if (close_square instanceof Error) { @@ -316,7 +317,7 @@ function parse_index(table: ExpressionInterface, stream: TokenStream): Expressio function parse_dot(table: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const dot = stream.next(); - const index = expect(stream, TokenKindEnum.Identifier); + const index = expect(stream, TokenKind.Identifier); if (index instanceof Error) { @@ -331,7 +332,7 @@ function parse_dot(table: ExpressionInterface, stream: TokenStream): ExpressionI kind: ExpressionKind.Value, token: index, value: { - kind: ValueKindEnum.StringLiteral, + kind: ValueKind.StringLiteral, token: index, string: index.data, }, @@ -339,7 +340,7 @@ function parse_dot(table: ExpressionInterface, stream: TokenStream): ExpressionI }, stream); } -function parse_single_argument_call(func: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error +function parse_single_argument_call(callable: ExpressionInterface, stream: TokenStream): ExpressionInterface | Error { const argument = parse_expression(stream); @@ -350,8 +351,8 @@ function parse_single_argument_call(func: ExpressionInterface, stream: TokenStre return { kind: ExpressionKind.Call, - token: func.token, - expression: func, + token: callable.token, + expression: callable, arguments: [argument], }; } @@ -361,17 +362,17 @@ function parse_access_expression(expression: ExpressionInterface, stream: TokenS // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (stream.peek().kind) { - case TokenKindEnum.OpenBrace: + case TokenKind.OpenBrace: return parse_call(expression, stream); - case TokenKindEnum.OpenSquare: + case TokenKind.OpenSquare: return parse_index(expression, stream); - case TokenKindEnum.Dot: + case TokenKind.Dot: return parse_dot(expression, stream); - case TokenKindEnum.SquiglyOpen: - case TokenKindEnum.StringLiteral: + case TokenKind.SquiglyOpen: + case TokenKind.StringLiteral: return parse_single_argument_call(expression, stream); } @@ -402,7 +403,7 @@ function parse_operation( throw new Error(); } - while (orders_order.includes(stream.peek().kind)) + while (isEnumValue(stream.peek().kind, orders_order)) { const operation_type = stream.next(); const rhs = parse_operation(stream, order + 1); @@ -427,7 +428,7 @@ function parse_operation( function parse_expression(stream: TokenStream): ExpressionInterface | Error { - if (stream.peek().kind === TokenKindEnum.BitXOrNot) + if (stream.peek().kind === TokenKind.BitXOrNot) { return parse_unary_operator(stream); } @@ -437,10 +438,10 @@ function parse_expression(stream: TokenStream): ExpressionInterface | Error function parse_assign_or_expression(stream: TokenStream): StatementInterface | Error { - const local = expect(stream, TokenKindEnum.Local); + const local = expect(stream, TokenKind.Local); const lhs: Array = []; - while (lhs.length === 0 || consume(stream, TokenKindEnum.Comma)) + while (lhs.length === 0 || consume(stream, TokenKind.Comma)) { const lvalue = parse_expression(stream); @@ -452,14 +453,14 @@ function parse_assign_or_expression(stream: TokenStream): StatementInterface | E lhs.push(lvalue); } - const assign = expect(stream, TokenKindEnum.Assign); + const assign = expect(stream, TokenKind.Assign); if (assign instanceof Error) { if (local instanceof Error) { // @TODO: Investigate as lhs[0] seems to always be undefined - return { kind: StatementKindEnum.Expression, expression: lhs[0] }; + return { kind: StatementKind.Expression, expression: lhs[0] }; } return parse_local_statement(local, lhs); @@ -467,7 +468,7 @@ function parse_assign_or_expression(stream: TokenStream): StatementInterface | E const rhs: Array = []; - while (rhs.length === 0 || consume(stream, TokenKindEnum.Comma)) + while (rhs.length === 0 || consume(stream, TokenKind.Comma)) { const rvalue = parse_expression(stream); @@ -480,7 +481,7 @@ function parse_assign_or_expression(stream: TokenStream): StatementInterface | E } return { - kind: StatementKindEnum.Assignment, + kind: StatementKind.Assignment, assignment: { local: !(local instanceof Error), lhs: lhs.reverse(), @@ -492,7 +493,7 @@ function parse_assign_or_expression(stream: TokenStream): StatementInterface | E function parse_return(stream: TokenStream): StatementInterface | Error { - const ret = expect(stream, TokenKindEnum.Return); + const ret = expect(stream, TokenKind.Return); if (ret instanceof Error) { @@ -501,7 +502,7 @@ function parse_return(stream: TokenStream): StatementInterface | Error const values: Array = []; - while (values.length === 0 || consume(stream, TokenKindEnum.Comma)) + while (values.length === 0 || consume(stream, TokenKind.Comma)) { const value = parse_expression(stream); @@ -524,14 +525,14 @@ function parse_return(stream: TokenStream): StatementInterface | Error kind: ExpressionKind.Value, token: ret, value: { - kind: ValueKindEnum.NilLiteral, + kind: ValueKind.NilLiteral, token: ret, }, }); } return { - kind: StatementKindEnum.Return, + kind: StatementKind.Return, return: { values: values, token: ret, @@ -541,7 +542,7 @@ function parse_return(stream: TokenStream): StatementInterface | Error function parse_if(stream: TokenStream): StatementInterface | Error { - const if_token = expect(stream, TokenKindEnum.If); + const if_token = expect(stream, TokenKind.If); if (if_token instanceof Error) { @@ -555,14 +556,14 @@ function parse_if(stream: TokenStream): StatementInterface | Error return condition; } - const then = expect(stream, TokenKindEnum.Then); + const then = expect(stream, TokenKind.Then); if (then instanceof Error) { return then; } - const body = parse(stream, TokenKindEnum.Else, TokenKindEnum.ElseIf, TokenKindEnum.End); + const body = parse(stream, TokenKind.Else, TokenKind.ElseIf, TokenKind.End); if (body instanceof Error) { @@ -572,7 +573,7 @@ function parse_if(stream: TokenStream): StatementInterface | Error const else_if_bodies: Array = []; let else_body: ChunkInterface | undefined = undefined; - while (consume(stream, TokenKindEnum.ElseIf)) + while (consume(stream, TokenKind.ElseIf)) { const condition = parse_expression(stream); @@ -581,14 +582,14 @@ function parse_if(stream: TokenStream): StatementInterface | Error return condition; } - const then = expect(stream, TokenKindEnum.Then); + const then = expect(stream, TokenKind.Then); if (then instanceof Error) { return then; } - const chunk = parse(stream, TokenKindEnum.End, TokenKindEnum.ElseIf, TokenKindEnum.Else); + const chunk = parse(stream, TokenKind.End, TokenKind.ElseIf, TokenKind.Else); if (chunk instanceof Error) { @@ -602,9 +603,9 @@ function parse_if(stream: TokenStream): StatementInterface | Error }); } - if (consume(stream, TokenKindEnum.Else)) + if (consume(stream, TokenKind.Else)) { - const chunk = parse(stream, TokenKindEnum.End); + const chunk = parse(stream, TokenKind.End); if (chunk instanceof Error) { @@ -614,7 +615,7 @@ function parse_if(stream: TokenStream): StatementInterface | Error else_body = chunk; } - const end = expect(stream, TokenKindEnum.End); + const end = expect(stream, TokenKind.End); if (end instanceof Error) { @@ -622,7 +623,7 @@ function parse_if(stream: TokenStream): StatementInterface | Error } return { - kind: StatementKindEnum.If, + kind: StatementKind.If, if: { condition: condition, body: body, @@ -635,7 +636,7 @@ function parse_if(stream: TokenStream): StatementInterface | Error function parse_while(stream: TokenStream): StatementInterface | Error { - const while_token = expect(stream, TokenKindEnum.While); + const while_token = expect(stream, TokenKind.While); if (while_token instanceof Error) { @@ -649,24 +650,24 @@ function parse_while(stream: TokenStream): StatementInterface | Error return condition; } - const do_token = expect(stream, TokenKindEnum.Do); + const do_token = expect(stream, TokenKind.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKindEnum.End); + const body = parse(stream, TokenKind.End); if (body instanceof Error) { return body; } - consume(stream, TokenKindEnum.End); + consume(stream, TokenKind.End); return { - kind: StatementKindEnum.While, + kind: StatementKind.While, while: { condition: condition, body: body, @@ -684,7 +685,7 @@ function parse_numeric_for(index: TokenInterface, stream: TokenStream): Statemen return start; } - const comma = expect(stream, TokenKindEnum.Comma); + const comma = expect(stream, TokenKind.Comma); if (comma instanceof Error) { @@ -700,7 +701,7 @@ function parse_numeric_for(index: TokenInterface, stream: TokenStream): Statemen let step: ExpressionInterface | undefined = undefined; - if (consume(stream, TokenKindEnum.Comma)) + if (consume(stream, TokenKind.Comma)) { const expression = parse_expression(stream); @@ -712,24 +713,24 @@ function parse_numeric_for(index: TokenInterface, stream: TokenStream): Statemen step = expression; } - const do_token = expect(stream, TokenKindEnum.Do); + const do_token = expect(stream, TokenKind.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKindEnum.End); + const body = parse(stream, TokenKind.End); if (body instanceof Error) { return body; } - consume(stream, TokenKindEnum.End); + consume(stream, TokenKind.End); return { - kind: StatementKindEnum.NumericFor, + kind: StatementKind.NumericFor, numeric_for: { index: index, start: start, @@ -742,7 +743,7 @@ function parse_numeric_for(index: TokenInterface, stream: TokenStream): Statemen function parse_for(stream: TokenStream): StatementInterface | Error { - const for_token = expect(stream, TokenKindEnum.For); + const for_token = expect(stream, TokenKind.For); if (for_token instanceof Error) { @@ -751,9 +752,9 @@ function parse_for(stream: TokenStream): StatementInterface | Error const items: Array = []; - while (items.length === 0 || consume(stream, TokenKindEnum.Comma)) + while (items.length === 0 || consume(stream, TokenKind.Comma)) { - const item = expect(stream, TokenKindEnum.Identifier); + const item = expect(stream, TokenKind.Identifier); if (item instanceof Error) { @@ -763,7 +764,7 @@ function parse_for(stream: TokenStream): StatementInterface | Error items.push(item); } - if (consume(stream, TokenKindEnum.Assign)) + if (consume(stream, TokenKind.Assign)) { const token = items[0]; @@ -775,7 +776,7 @@ function parse_for(stream: TokenStream): StatementInterface | Error return parse_numeric_for(token, stream); } - const in_token = expect(stream, TokenKindEnum.In); + const in_token = expect(stream, TokenKind.In); if (in_token instanceof Error) { @@ -789,24 +790,24 @@ function parse_for(stream: TokenStream): StatementInterface | Error return iterator; } - const do_token = expect(stream, TokenKindEnum.Do); + const do_token = expect(stream, TokenKind.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKindEnum.End); + const body = parse(stream, TokenKind.End); if (body instanceof Error) { return body; } - consume(stream, TokenKindEnum.End); + consume(stream, TokenKind.End); return { - kind: StatementKindEnum.For, + kind: StatementKind.For, for: { items: items, iterator: iterator, @@ -818,21 +819,21 @@ function parse_for(stream: TokenStream): StatementInterface | Error function parse_repeat(stream: TokenStream): StatementInterface | Error { - const repeat_token = expect(stream, TokenKindEnum.Repeat); + const repeat_token = expect(stream, TokenKind.Repeat); if (repeat_token instanceof Error) { return repeat_token; } - const body = parse(stream, TokenKindEnum.Until); + const body = parse(stream, TokenKind.Until); if (body instanceof Error) { return body; } - const until_token = expect(stream, TokenKindEnum.Until); + const until_token = expect(stream, TokenKind.Until); if (until_token instanceof Error) { @@ -847,7 +848,7 @@ function parse_repeat(stream: TokenStream): StatementInterface | Error } return { - kind: StatementKindEnum.Repeat, + kind: StatementKind.Repeat, repeat: { body: body, condition: condition, @@ -858,21 +859,21 @@ function parse_repeat(stream: TokenStream): StatementInterface | Error function parse_do(stream: TokenStream): StatementInterface | Error { - const do_token = expect(stream, TokenKindEnum.Do); + const do_token = expect(stream, TokenKind.Do); if (do_token instanceof Error) { return do_token; } - const body = parse(stream, TokenKindEnum.End); + const body = parse(stream, TokenKind.End); if (body instanceof Error) { return body; } - const end_token = expect(stream, TokenKindEnum.End); + const end_token = expect(stream, TokenKind.End); if (end_token instanceof Error) { @@ -880,7 +881,7 @@ function parse_do(stream: TokenStream): StatementInterface | Error } return { - kind: StatementKindEnum.Do, + kind: StatementKind.Do, do: { body: body, token: do_token, @@ -897,17 +898,17 @@ function parse_function_value(function_token: TokenInterface, stream: TokenStrea return params; } - const body = parse(stream, TokenKindEnum.End); + const body = parse(stream, TokenKind.End); if (body instanceof Error) { return body; } - consume(stream, TokenKindEnum.End); + consume(stream, TokenKind.End); return { - kind: ValueKindEnum.FunctionLike, + kind: ValueKind.FunctionLike, token: function_token, function: { parameters: params, @@ -918,7 +919,7 @@ function parse_function_value(function_token: TokenInterface, stream: TokenStrea function parse_local_function(table_name: TokenInterface, stream: TokenStream): StatementInterface | Error { - const local_name = expect(stream, TokenKindEnum.Identifier); + const local_name = expect(stream, TokenKind.Identifier); if (local_name instanceof Error) { @@ -933,7 +934,7 @@ function parse_local_function(table_name: TokenInterface, stream: TokenStream): } return { - kind: StatementKindEnum.Assignment, + kind: StatementKind.Assignment, assignment: { token: table_name, local: false, @@ -944,7 +945,7 @@ function parse_local_function(table_name: TokenInterface, stream: TokenStream): kind: ExpressionKind.Value, token: table_name, value: { - kind: ValueKindEnum.Variable, + kind: ValueKind.Variable, token: table_name, identifier: table_name.data, }, @@ -953,7 +954,7 @@ function parse_local_function(table_name: TokenInterface, stream: TokenStream): kind: ExpressionKind.Value, token: local_name, value: { - kind: ValueKindEnum.StringLiteral, + kind: ValueKind.StringLiteral, token: local_name, string: local_name.data, }, @@ -970,21 +971,21 @@ function parse_local_function(table_name: TokenInterface, stream: TokenStream): function parse_function(stream: TokenStream): StatementInterface | Error { - const function_token = expect(stream, TokenKindEnum.FunctionLike); + const function_token = expect(stream, TokenKind.FunctionLike); if (function_token instanceof Error) { return function_token; } - const name = expect(stream, TokenKindEnum.Identifier); + const name = expect(stream, TokenKind.Identifier); if (name instanceof Error) { return name; } - if (consume(stream, TokenKindEnum.Dot)) + if (consume(stream, TokenKind.Dot)) { return parse_local_function(name, stream); } @@ -997,7 +998,7 @@ function parse_function(stream: TokenStream): StatementInterface | Error } return { - kind: StatementKindEnum.Assignment, + kind: StatementKind.Assignment, assignment: { token: name, local: false, @@ -1005,7 +1006,7 @@ function parse_function(stream: TokenStream): StatementInterface | Error kind: ExpressionKind.Value, token: name, value: { - kind: ValueKindEnum.Variable, + kind: ValueKind.Variable, token: name, identifier: name.data, }, @@ -1020,6 +1021,7 @@ function parse_function(stream: TokenStream): StatementInterface | Error } export { + parse, parse_assign_or_expression, parse_return, parse_if, diff --git a/src/parser/consume/consume.mts b/src/parser/consume/consume.mts index fdd7fff..ebb9f23 100644 --- a/src/parser/consume/consume.mts +++ b/src/parser/consume/consume.mts @@ -1,9 +1,10 @@ import type { TokenStream } from "../../lexer.mjs"; import type { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; function consume(stream: TokenStream, kind: TokenKindEnum): boolean { - const token = stream.peek(); + const token: TokenInterface = stream.peek(); if (token.kind !== kind) { diff --git a/src/parser/error/to-error.mts b/src/parser/error/to-error.mts index e257d24..009ca96 100644 --- a/src/parser/error/to-error.mts +++ b/src/parser/error/to-error.mts @@ -1,6 +1,7 @@ +import type { TokenStream } from "../../lexer.mjs"; import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; -function to_error(token: TokenInterface, message: string): Error +function to_error(token: TokenInterface | TokenStream, message: string): Error { return new Error( `${token.debug.line.toFixed(0)}:${token.debug.column.toFixed(0)}: ${message}` diff --git a/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts b/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts index 53d50e5..d840b99 100644 --- a/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts +++ b/src/parser/operation-type-to-expression-kind/operation-type-to-expression-kind.mts @@ -1,37 +1,58 @@ -import { ExpressionKind } from "../../ast/definition/enum/expression-kind.enum.mjs"; -import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { ExpressionKind, type ExpressionKindEnum } from "../../ast/definition/enum/expression-kind.enum.mjs"; +import { TokenKind, type TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; function operation_type_to_expression_kind( operation_type: TokenKindEnum -): ExpressionKind +): ExpressionKindEnum { - // eslint-disable-next-line @ts/switch-exhaustiveness-check + // eslint-disable-next-line @ts/switch-exhaustiveness-check -- Not all operations are expressions switch (operation_type) { - case TokenKindEnum.Addition: return ExpressionKind.Addition; - case TokenKindEnum.Subtract: return ExpressionKind.Subtract; - case TokenKindEnum.Multiply: return ExpressionKind.Multiplication; - case TokenKindEnum.Division: return ExpressionKind.Division; - case TokenKindEnum.FloorDivision: return ExpressionKind.FloorDivision; - case TokenKindEnum.Modulo: return ExpressionKind.Modulo; - case TokenKindEnum.Exponent: return ExpressionKind.Exponent; - case TokenKindEnum.Concat: return ExpressionKind.Concat; - case TokenKindEnum.BitAnd: return ExpressionKind.BitAnd; - case TokenKindEnum.BitOr: return ExpressionKind.BitOr; - case TokenKindEnum.BitXOrNot: return ExpressionKind.BitXOr; - case TokenKindEnum.BitShiftLeft: return ExpressionKind.BitShiftLeft; - case TokenKindEnum.BitShiftRight: return ExpressionKind.BitShiftRight; - case TokenKindEnum.LessThan: return ExpressionKind.LessThan; - case TokenKindEnum.LessThanEquals: return ExpressionKind.LessThanEquals; - case TokenKindEnum.GreaterThan: return ExpressionKind.GreaterThan; - case TokenKindEnum.GreaterThanEquals: return ExpressionKind.GreaterThanEquals; - case TokenKindEnum.Equals: return ExpressionKind.Equals; - case TokenKindEnum.NotEquals: return ExpressionKind.NotEquals; - case TokenKindEnum.And: return ExpressionKind.And; - case TokenKindEnum.Or: return ExpressionKind.Or; + case TokenKind.Addition: + return ExpressionKind.Addition; + case TokenKind.Subtract: + return ExpressionKind.Subtract; + case TokenKind.Multiply: + return ExpressionKind.Multiplication; + case TokenKind.Division: + return ExpressionKind.Division; + case TokenKind.FloorDivision: + return ExpressionKind.FloorDivision; + case TokenKind.Modulo: + return ExpressionKind.Modulo; + case TokenKind.Exponent: + return ExpressionKind.Exponent; + case TokenKind.Concat: + return ExpressionKind.Concat; + case TokenKind.BitAnd: + return ExpressionKind.BitAnd; + case TokenKind.BitOr: + return ExpressionKind.BitOr; + case TokenKind.BitXOrNot: + return ExpressionKind.BitXOr; + case TokenKind.BitShiftLeft: + return ExpressionKind.BitShiftLeft; + case TokenKind.BitShiftRight: + return ExpressionKind.BitShiftRight; + case TokenKind.LessThan: + return ExpressionKind.LessThan; + case TokenKind.LessThanEquals: + return ExpressionKind.LessThanEquals; + case TokenKind.GreaterThan: + return ExpressionKind.GreaterThan; + case TokenKind.GreaterThanEquals: + return ExpressionKind.GreaterThanEquals; + case TokenKind.Equals: + return ExpressionKind.Equals; + case TokenKind.NotEquals: + return ExpressionKind.NotEquals; + case TokenKind.And: + return ExpressionKind.And; + case TokenKind.Or: + return ExpressionKind.Or; default: - throw new Error(); + throw new Error(`"${operation_type}" is not an expression operation.`); } } diff --git a/src/parser/parse-function-params/parse-function-params.mts b/src/parser/parse-function-params/parse-function-params.mts index 65e0677..00ed941 100644 --- a/src/parser/parse-function-params/parse-function-params.mts +++ b/src/parser/parse-function-params/parse-function-params.mts @@ -1,12 +1,12 @@ import type { TokenStream } from "../../lexer.mjs"; -import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { TokenKind } from "../../lexer/definition/enum/token-kind.enum.mjs"; import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; import { consume } from "../consume/consume.mjs"; import { expect } from "../expect/expect.mjs"; function parse_function_params(stream: TokenStream): Array | Error { - const open_brace = expect(stream, TokenKindEnum.OpenBrace); + const open_brace = expect(stream, TokenKind.OpenBrace); if (open_brace instanceof Error) { @@ -15,9 +15,9 @@ function parse_function_params(stream: TokenStream): Array | Err const params: Array = []; - while (stream.peek().kind !== TokenKindEnum.CloseBrace) + while (stream.peek().kind !== TokenKind.CloseBrace) { - const param = expect(stream, TokenKindEnum.Identifier); + const param = expect(stream, TokenKind.Identifier); if (param instanceof Error) { @@ -26,13 +26,13 @@ function parse_function_params(stream: TokenStream): Array | Err params.push(param); - if (!consume(stream, TokenKindEnum.Comma)) + if (!consume(stream, TokenKind.Comma)) { break; } } - const close_brace = expect(stream, TokenKindEnum.CloseBrace); + const close_brace = expect(stream, TokenKind.CloseBrace); if (close_brace instanceof Error) { diff --git a/src/parser/parse-local-statement/parse-local-statement.mts b/src/parser/parse-local-statement/parse-local-statement.mts index fe2ef78..1dd5acf 100644 --- a/src/parser/parse-local-statement/parse-local-statement.mts +++ b/src/parser/parse-local-statement/parse-local-statement.mts @@ -1,19 +1,20 @@ -import { StatementKindEnum } from "../../ast/definition/enum/statement-kind.enum.mjs"; -import { ValueKindEnum } from "../../ast/definition/enum/value-kind.enum.mjs"; +import { StatementKind } from "../../ast/definition/enum/statement-kind.enum.mjs"; +import { ValueKind } from "../../ast/definition/enum/value-kind.enum.mjs"; import type { ExpressionInterface } from "../../ast/definition/interface/expression.interface.mjs"; import type { StatementInterface } from "../../ast/definition/interface/statement.interface.mjs"; +import type { TokenStream } from "../../lexer.mjs"; import type { TokenInterface } from "../../lexer/definition/interface/token.interface.mjs"; import { to_error } from "../error/to-error.mjs"; function parse_local_statement(local: TokenInterface, values: Array): StatementInterface | Error { - const names: Array = []; + const names: Array = []; for (const expression of values) { const value = expression.value; - if (value === undefined || value.kind !== ValueKindEnum.Variable) + if (value === undefined || value.kind !== ValueKind.Variable) { return to_error(expression.token, "Invalid local name"); } @@ -22,7 +23,7 @@ function parse_local_statement(local: TokenInterface, values: Array): // eslint-disable-next-line @ts/switch-exhaustiveness-check switch (token.kind) { - case TokenKindEnum.Identifier: - case TokenKindEnum.NilLiteral: - case TokenKindEnum.StringLiteral: - case TokenKindEnum.NumberLiteral: - case TokenKindEnum.BooleanLiteral: - case TokenKindEnum.SquiglyOpen: - case TokenKindEnum.Local: + case TokenKind.Identifier: + case TokenKind.NilLiteral: + case TokenKind.StringLiteral: + case TokenKind.NumberLiteral: + case TokenKind.BooleanLiteral: + case TokenKind.SquiglyOpen: + case TokenKind.Local: return parse_assign_or_expression(stream); - case TokenKindEnum.Return: + case TokenKind.Return: return parse_return(stream); - case TokenKindEnum.Break: + case TokenKind.Break: return parse_break(stream); - case TokenKindEnum.If: + case TokenKind.If: return parse_if(stream); - case TokenKindEnum.While: + case TokenKind.While: return parse_while(stream); - case TokenKindEnum.For: + case TokenKind.For: return parse_for(stream); - case TokenKindEnum.Repeat: + case TokenKind.Repeat: return parse_repeat(stream); - case TokenKindEnum.Do: + case TokenKind.Do: return parse_do(stream); - case TokenKindEnum.FunctionLike: + case TokenKind.FunctionLike: return parse_function(stream); - case TokenKindEnum.Semicolon: + case TokenKind.Semicolon: stream.next(); - return { kind: StatementKindEnum.Empty }; + return { kind: StatementKind.Empty }; default: { diff --git a/src/parser/parse/parse.mts b/src/parser/parse/parse.mts index 7df97dc..2db8d81 100644 --- a/src/parser/parse/parse.mts +++ b/src/parser/parse/parse.mts @@ -1,6 +1,6 @@ import type { ChunkInterface } from "../../ast/definition/interface/chunk.interface.mjs"; import type { TokenStream } from "../../lexer.mjs"; -import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { TokenKind, type TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; import { parse_statement } from "../parse-statement/parse-statement.mjs"; function parse(stream: TokenStream, ...end_tokens: Array): ChunkInterface | Error @@ -9,7 +9,7 @@ function parse(stream: TokenStream, ...end_tokens: Array): ChunkI if (end_tokens.length === 0) { - end_tokens.push(TokenKindEnum.EOF); + end_tokens.push(TokenKind.EOF); } for (;;) diff --git a/src/parser/parse_break/parse-break.mts b/src/parser/parse_break/parse-break.mts index 250f519..e6977de 100644 --- a/src/parser/parse_break/parse-break.mts +++ b/src/parser/parse_break/parse-break.mts @@ -1,12 +1,12 @@ -import { StatementKindEnum } from "../../ast/definition/enum/statement-kind.enum.mjs"; +import { StatementKind } from "../../ast/definition/enum/statement-kind.enum.mjs"; import type { StatementInterface } from "../../ast/definition/interface/statement.interface.mjs"; import type { TokenStream } from "../../lexer.mjs"; -import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { TokenKind } from "../../lexer/definition/enum/token-kind.enum.mjs"; import { expect } from "../expect/expect.mjs"; function parse_break(stream: TokenStream): StatementInterface | Error { - const break_token = expect(stream, TokenKindEnum.Break); + const break_token = expect(stream, TokenKind.Break); if (break_token instanceof Error) { @@ -14,7 +14,7 @@ function parse_break(stream: TokenStream): StatementInterface | Error } return { - kind: StatementKindEnum.Break, + kind: StatementKind.Break, }; } diff --git a/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts b/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts index 2b620c4..3e63d00 100644 --- a/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts +++ b/src/parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mts @@ -1,14 +1,19 @@ -import { ExpressionKind } from "../../ast/definition/enum/expression-kind.enum.mjs"; -import { TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import { ExpressionKind, type ExpressionKindEnum } from "../../ast/definition/enum/expression-kind.enum.mjs"; +import { TokenKind, type TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; -function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKind +function unary_type_to_expression_kind(kind: TokenKindEnum): ExpressionKindEnum { + // eslint-disable-next-line @ts/switch-exhaustiveness-check -- Only unary expressions switch (kind) { - case TokenKindEnum.Not: return ExpressionKind.Not; - case TokenKindEnum.Subtract: return ExpressionKind.Negate; - case TokenKindEnum.Hash: return ExpressionKind.Length; - case TokenKindEnum.BitXOrNot: return ExpressionKind.BitNot; + case TokenKind.Not: + return ExpressionKind.Not; + case TokenKind.Subtract: + return ExpressionKind.Negate; + case TokenKind.Hash: + return ExpressionKind.Length; + case TokenKind.BitXOrNot: + return ExpressionKind.BitNot; default: throw new Error(); From 3904b7acb852632d22f7940cfddea807ad868fa1 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 14:57:37 +0000 Subject: [PATCH 67/71] fix: Moved single_token_map to its own file and made it a function. --- src/lexer.mts | 31 +--------- .../get-single-token/get-single-token.mts | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 29 deletions(-) create mode 100644 src/lexer/get-single-token/get-single-token.mts diff --git a/src/lexer.mts b/src/lexer.mts index ec99d8a..8bd950a 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -2,34 +2,7 @@ import { State, type StateEnum } from "./lexer/definition/enum/state.enum.mjs"; import { TokenKind, type TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; - -const single_token_map: Map = new Map([ - ["(", TokenKind.OpenBrace], - [")", TokenKind.CloseBrace], - ["[", TokenKind.OpenSquare], - ["]", TokenKind.CloseSquare], - ["{", TokenKind.SquiglyOpen], - ["}", TokenKind.SquiglyClose], - - ["+", TokenKind.Addition], - ["-", TokenKind.Subtract], - ["*", TokenKind.Multiply], - ["/", TokenKind.Division], - ["%", TokenKind.Modulo], - ["^", TokenKind.Exponent], - ["&", TokenKind.BitAnd], - ["|", TokenKind.BitOr], - ["~", TokenKind.BitXOrNot], - - ["<", TokenKind.LessThan], - [">", TokenKind.GreaterThan], - - ["=", TokenKind.Assign], - [";", TokenKind.Semicolon], - [",", TokenKind.Comma], - [".", TokenKind.Dot], - ["#", TokenKind.Hash], -]); +import { get_single_token } from "./lexer/get-single-token/get-single-token.mjs"; const double_token_map: Map = new Map([ ["==", TokenKind.Equals], @@ -221,7 +194,7 @@ export class TokenStream } } - const single_token_type = single_token_map.get(c); + const single_token_type = get_single_token(c); if (single_token_type !== undefined) { diff --git a/src/lexer/get-single-token/get-single-token.mts b/src/lexer/get-single-token/get-single-token.mts new file mode 100644 index 0000000..218959b --- /dev/null +++ b/src/lexer/get-single-token/get-single-token.mts @@ -0,0 +1,56 @@ +import { TokenKind, type TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; + +function get_single_token(token: string): TokenKindEnum | undefined +{ + switch (token) + { + case "(": + return TokenKind.OpenBrace; + case ")": + return TokenKind.CloseBrace; + case "[": + return TokenKind.OpenSquare; + case "]": + return TokenKind.CloseSquare; + case "{": + return TokenKind.SquiglyOpen; + case "}": + return TokenKind.SquiglyClose; + case "+": + return TokenKind.Addition; + case "-": + return TokenKind.Subtract; + case "*": + return TokenKind.Multiply; + case "/": + return TokenKind.Division; + case "%": + return TokenKind.Modulo; + case "^": + return TokenKind.Exponent; + case "&": + return TokenKind.BitAnd; + case "|": + return TokenKind.BitOr; + case "~": + return TokenKind.BitXOrNot; + case "<": + return TokenKind.LessThan; + case ">": + return TokenKind.GreaterThan; + case "=": + return TokenKind.Assign; + case ";": + return TokenKind.Semicolon; + case ",": + return TokenKind.Comma; + case ".": + return TokenKind.Dot; + case "#": + return TokenKind.Hash; + } + + return undefined; +} + +export { get_single_token }; From 16c38a9144a5662b4f10e0bb02bb23cf2651cf05 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 14:59:38 +0000 Subject: [PATCH 68/71] fix: Moved double_token_map to its own file and made it a function. --- src/lexer.mts | 14 ++-------- .../get-double-token/get-double-token.mts | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/lexer/get-double-token/get-double-token.mts diff --git a/src/lexer.mts b/src/lexer.mts index 8bd950a..3821d72 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -2,19 +2,9 @@ import { State, type StateEnum } from "./lexer/definition/enum/state.enum.mjs"; import { TokenKind, type TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; +import { get_double_token } from "./lexer/get-double-token/get-double-token.mjs"; import { get_single_token } from "./lexer/get-single-token/get-single-token.mjs"; -const double_token_map: Map = new Map([ - ["==", TokenKind.Equals], - ["<=", TokenKind.LessThanEquals], - [">=", TokenKind.GreaterThanEquals], - ["~=", TokenKind.NotEquals], - ["..", TokenKind.Concat], - ["//", TokenKind.FloorDivision], - ["<<", TokenKind.BitShiftLeft], - [">>", TokenKind.BitShiftRight], -]); - const keyword_map: Map = new Map([ ["function", TokenKind.FunctionLike], ["if", TokenKind.If], @@ -174,7 +164,7 @@ export class TokenStream return; } - const dobule_token_type = double_token_map.get(double); + const dobule_token_type = get_double_token(double); if (dobule_token_type !== undefined) { diff --git a/src/lexer/get-double-token/get-double-token.mts b/src/lexer/get-double-token/get-double-token.mts new file mode 100644 index 0000000..74b20b3 --- /dev/null +++ b/src/lexer/get-double-token/get-double-token.mts @@ -0,0 +1,28 @@ +import { TokenKind, type TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; + +function get_double_token(double: string): TokenKindEnum | undefined +{ + switch (double) + { + case "==": + return TokenKind.Equals; + case "<=": + return TokenKind.LessThanEquals; + case ">=": + return TokenKind.GreaterThanEquals; + case "~=": + return TokenKind.NotEquals; + case "..": + return TokenKind.Concat; + case "//": + return TokenKind.FloorDivision; + case "<<": + return TokenKind.BitShiftLeft; + case ">>": + return TokenKind.BitShiftRight; + } + + return undefined; +} + +export { get_double_token }; From 257064d36dd1b4d80b9ddfd2e636d1a773763a12 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 15:06:15 +0000 Subject: [PATCH 69/71] fix: Moved keyword_map to its own file and made it a function. --- src/lexer.mts | 31 ++------------- src/lexer/get-keyword/get-keyword.mts | 54 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 28 deletions(-) create mode 100644 src/lexer/get-keyword/get-keyword.mts diff --git a/src/lexer.mts b/src/lexer.mts index 3821d72..944a6c8 100644 --- a/src/lexer.mts +++ b/src/lexer.mts @@ -1,36 +1,11 @@ import { State, type StateEnum } from "./lexer/definition/enum/state.enum.mjs"; -import { TokenKind, type TokenKindEnum } from "./lexer/definition/enum/token-kind.enum.mjs"; +import { TokenKind } from "./lexer/definition/enum/token-kind.enum.mjs"; import type { DebugInterface } from "./lexer/definition/interface/debug.interface.mjs"; import type { TokenInterface } from "./lexer/definition/interface/token.interface.mjs"; import { get_double_token } from "./lexer/get-double-token/get-double-token.mjs"; +import { get_keyword } from "./lexer/get-keyword/get-keyword.mjs"; import { get_single_token } from "./lexer/get-single-token/get-single-token.mjs"; -const keyword_map: Map = new Map([ - ["function", TokenKind.FunctionLike], - ["if", TokenKind.If], - ["while", TokenKind.While], - ["for", TokenKind.For], - ["repeat", TokenKind.Repeat], - ["in", TokenKind.In], - ["do", TokenKind.Do], - ["then", TokenKind.Then], - ["elseif", TokenKind.ElseIf], - ["else", TokenKind.Else], - ["until", TokenKind.Until], - ["end", TokenKind.End], - ["return", TokenKind.Return], - ["break", TokenKind.Break], - - ["and", TokenKind.And], - ["or", TokenKind.Or], - ["not", TokenKind.Not], - - ["true", TokenKind.BooleanLiteral], - ["false", TokenKind.BooleanLiteral], - ["nil", TokenKind.NilLiteral], - ["local", TokenKind.Local], -]); - export class TokenStream { public debug: DebugInterface = { line: -1, column: -1 }; @@ -304,7 +279,7 @@ export class TokenStream if (!/[a-zA-Z0-9_]/.test(c)) { - const kind = keyword_map.get(this.buffer); + const kind = get_keyword(this.buffer); this.peek_queue.push({ data: this.buffer, diff --git a/src/lexer/get-keyword/get-keyword.mts b/src/lexer/get-keyword/get-keyword.mts new file mode 100644 index 0000000..7670386 --- /dev/null +++ b/src/lexer/get-keyword/get-keyword.mts @@ -0,0 +1,54 @@ +import { TokenKind, type TokenKindEnum } from "../definition/enum/token-kind.enum.mjs"; + +function get_keyword(keyword: string): TokenKindEnum | undefined +{ + switch (keyword) + { + case "function": + return TokenKind.FunctionLike; + case "if": + return TokenKind.If; + case "while": + return TokenKind.While; + case "for": + return TokenKind.For; + case "repeat": + return TokenKind.Repeat; + case "in": + return TokenKind.In; + case "do": + return TokenKind.Do; + case "then": + return TokenKind.Then; + case "elseif": + return TokenKind.ElseIf; + case "else": + return TokenKind.Else; + case "until": + return TokenKind.Until; + case "end": + return TokenKind.End; + case "return": + return TokenKind.Return; + case "break": + return TokenKind.Break; + case "and": + return TokenKind.And; + case "or": + return TokenKind.Or; + case "not": + return TokenKind.Not; + case "true": + return TokenKind.BooleanLiteral; + case "false": + return TokenKind.BooleanLiteral; + case "nil": + return TokenKind.NilLiteral; + case "local": + return TokenKind.Local; + } + + return undefined; +} + +export { get_keyword }; From bad75b8316a6e7cf3dec78d42c9b6d1808e31c63 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 15:32:40 +0000 Subject: [PATCH 70/71] fix: Moved UNARY to its own file and made it a guard. --- src/parser.mts | 9 ++------- .../is-unary-operator/is-unary-operator.mts | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 src/parser/is-unary-operator/is-unary-operator.mts diff --git a/src/parser.mts b/src/parser.mts index 07e9a7b..b1e176e 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -20,12 +20,7 @@ import { parse_local_statement } from "./parser/parse-local-statement/parse-loca import { parse } from "./parser/parse/parse.mjs"; import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; import { getDebug } from "./lexer/utility/get-debug.mjs"; - -const UNARY = [ - TokenKind.Not, - TokenKind.Subtract, - TokenKind.Hash, -]; +import { isUnaryOperatorToken } from "./parser/is-unary-operator/is-unary-operator.mjs"; const ORDERS = [ [TokenKind.Or], @@ -228,7 +223,7 @@ function parse_value_expression(stream: TokenStream): ExpressionInterface | Erro return sub_expression; } - if (isEnumValue(stream.peek().kind, UNARY)) + if (isUnaryOperatorToken(stream.peek().kind)) { return parse_unary_operator(stream); } diff --git a/src/parser/is-unary-operator/is-unary-operator.mts b/src/parser/is-unary-operator/is-unary-operator.mts new file mode 100644 index 0000000..1b29861 --- /dev/null +++ b/src/parser/is-unary-operator/is-unary-operator.mts @@ -0,0 +1,20 @@ +import { isEnumValue } from "@vitruvius-labs/ts-predicate"; +import { TokenKind, type TokenKindEnum } from "../../lexer/definition/enum/token-kind.enum.mjs"; + +type UnaryOperatorTokenKind = ( + | typeof TokenKind.Not + | typeof TokenKind.Subtract + | typeof TokenKind.Hash +); + +function isUnaryOperatorToken(token: TokenKindEnum): token is UnaryOperatorTokenKind +{ + return isEnumValue(token, [ + TokenKind.Not, + TokenKind.Subtract, + TokenKind.Hash, + ]); +} + +export { isUnaryOperatorToken }; + From a1635f257800514e371afe9cb5ce1bef96acc9bc Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 1 Feb 2026 15:38:16 +0000 Subject: [PATCH 71/71] fix: Moved ORDERS to its own file and made it a function. --- src/parser.mts | 19 +++-------------- .../definition/type/order-tuple.type.mts | 17 +++++++++++++++ src/parser/get-orders/get-orders.mts | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 src/parser/get-orders/definition/type/order-tuple.type.mts create mode 100644 src/parser/get-orders/get-orders.mts diff --git a/src/parser.mts b/src/parser.mts index b1e176e..efd935b 100644 --- a/src/parser.mts +++ b/src/parser.mts @@ -21,20 +21,7 @@ import { parse } from "./parser/parse/parse.mjs"; import { unary_type_to_expression_kind } from "./parser/unary-type-to-expression-kind/unary-type-to-expression-kind.mjs"; import { getDebug } from "./lexer/utility/get-debug.mjs"; import { isUnaryOperatorToken } from "./parser/is-unary-operator/is-unary-operator.mjs"; - -const ORDERS = [ - [TokenKind.Or], - [TokenKind.And], - [TokenKind.LessThan, TokenKind.LessThanEquals, TokenKind.GreaterThan, TokenKind.GreaterThanEquals, TokenKind.Equals, TokenKind.NotEquals], - [TokenKind.BitOr], - [TokenKind.BitAnd], - [TokenKind.BitXOrNot], - [TokenKind.BitShiftLeft, TokenKind.BitShiftRight], - [TokenKind.Concat], - [TokenKind.Addition, TokenKind.Subtract], - [TokenKind.Multiply, TokenKind.Division, TokenKind.FloorDivision, TokenKind.Modulo], - [TokenKind.Exponent], -]; +import { get_orders } from "./parser/get-orders/get-orders.mjs"; function parse_table_key(stream: TokenStream): ExpressionInterface | Error { @@ -379,7 +366,7 @@ function parse_operation( order: number ): ExpressionInterface | Error { - if (order >= ORDERS.length) + if (order >= get_orders().length) { return parse_value_expression(stream); } @@ -391,7 +378,7 @@ function parse_operation( return lhs; } - const orders_order = ORDERS[order]; + const orders_order = get_orders()[order]; if (orders_order === undefined) { diff --git a/src/parser/get-orders/definition/type/order-tuple.type.mts b/src/parser/get-orders/definition/type/order-tuple.type.mts new file mode 100644 index 0000000..eede8a4 --- /dev/null +++ b/src/parser/get-orders/definition/type/order-tuple.type.mts @@ -0,0 +1,17 @@ +import type { TokenKind } from "../../../../lexer/definition/enum/token-kind.enum.mjs"; + +type OrderTupleType = [ + [typeof TokenKind["Or"]], + [typeof TokenKind["And"]], + [typeof TokenKind["LessThan"], typeof TokenKind["LessThanEquals"], typeof TokenKind["GreaterThan"], typeof TokenKind["GreaterThanEquals"], typeof TokenKind["Equals"], typeof TokenKind["NotEquals"]], + [typeof TokenKind["BitOr"]], + [typeof TokenKind["BitAnd"]], + [typeof TokenKind["BitXOrNot"]], + [typeof TokenKind["BitShiftLeft"], typeof TokenKind["BitShiftRight"]], + [typeof TokenKind["Concat"]], + [typeof TokenKind["Addition"], typeof TokenKind["Subtract"]], + [typeof TokenKind["Multiply"], typeof TokenKind["Division"], typeof TokenKind["FloorDivision"], typeof TokenKind["Modulo"]], + [typeof TokenKind["Exponent"]], +]; + +export { type OrderTupleType }; diff --git a/src/parser/get-orders/get-orders.mts b/src/parser/get-orders/get-orders.mts new file mode 100644 index 0000000..96a64eb --- /dev/null +++ b/src/parser/get-orders/get-orders.mts @@ -0,0 +1,21 @@ +import { TokenKind } from "../../lexer/definition/enum/token-kind.enum.mjs"; +import type { OrderTupleType } from "./definition/type/order-tuple.type.mjs"; + +function get_orders(): OrderTupleType +{ + return [ + [TokenKind.Or], + [TokenKind.And], + [TokenKind.LessThan, TokenKind.LessThanEquals, TokenKind.GreaterThan, TokenKind.GreaterThanEquals, TokenKind.Equals, TokenKind.NotEquals], + [TokenKind.BitOr], + [TokenKind.BitAnd], + [TokenKind.BitXOrNot], + [TokenKind.BitShiftLeft, TokenKind.BitShiftRight], + [TokenKind.Concat], + [TokenKind.Addition, TokenKind.Subtract], + [TokenKind.Multiply, TokenKind.Division, TokenKind.FloorDivision, TokenKind.Modulo], + [TokenKind.Exponent], + ]; +} + +export { get_orders };