v2
This commit is contained in:
parent
3a668ccad4
commit
b880de0b08
49 changed files with 998 additions and 3527 deletions
0
dist/index.d.ts
vendored
0
dist/index.d.ts
vendored
1712
dist/index.js
vendored
1712
dist/index.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "es2020",
|
||||
"target": "es6",
|
||||
"lib": ["es2022", "dom"],
|
||||
"checkJs": false,
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
496
package-lock.json
generated
496
package-lock.json
generated
|
@ -1,496 +0,0 @@
|
|||
{
|
||||
"name": "kuebiko",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kuebiko",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"izuna": "git+https://git.kitsu.cafe/rowan/izuna.git",
|
||||
"kojima": "git+https://git.kitsu.cafe/rowan/kojima.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
|
||||
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
|
||||
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
|
||||
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
|
||||
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
|
||||
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.2",
|
||||
"@esbuild/android-arm": "0.25.2",
|
||||
"@esbuild/android-arm64": "0.25.2",
|
||||
"@esbuild/android-x64": "0.25.2",
|
||||
"@esbuild/darwin-arm64": "0.25.2",
|
||||
"@esbuild/darwin-x64": "0.25.2",
|
||||
"@esbuild/freebsd-arm64": "0.25.2",
|
||||
"@esbuild/freebsd-x64": "0.25.2",
|
||||
"@esbuild/linux-arm": "0.25.2",
|
||||
"@esbuild/linux-arm64": "0.25.2",
|
||||
"@esbuild/linux-ia32": "0.25.2",
|
||||
"@esbuild/linux-loong64": "0.25.2",
|
||||
"@esbuild/linux-mips64el": "0.25.2",
|
||||
"@esbuild/linux-ppc64": "0.25.2",
|
||||
"@esbuild/linux-riscv64": "0.25.2",
|
||||
"@esbuild/linux-s390x": "0.25.2",
|
||||
"@esbuild/linux-x64": "0.25.2",
|
||||
"@esbuild/netbsd-arm64": "0.25.2",
|
||||
"@esbuild/netbsd-x64": "0.25.2",
|
||||
"@esbuild/openbsd-arm64": "0.25.2",
|
||||
"@esbuild/openbsd-x64": "0.25.2",
|
||||
"@esbuild/sunos-x64": "0.25.2",
|
||||
"@esbuild/win32-arm64": "0.25.2",
|
||||
"@esbuild/win32-ia32": "0.25.2",
|
||||
"@esbuild/win32-x64": "0.25.2"
|
||||
}
|
||||
},
|
||||
"node_modules/folktest": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+https://git.kitsu.cafe/rowan/folktest.git#cbf48ff3b1334eb883f202a77a5bc89d24534520",
|
||||
"dev": true,
|
||||
"license": "GPL-3.0-or-later"
|
||||
},
|
||||
"node_modules/izuna": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+https://git.kitsu.cafe/rowan/izuna.git#4ab7c265d83856f2dc527780a3ac87b3d54676f1",
|
||||
"license": "GPL-3.0-or-later"
|
||||
},
|
||||
"node_modules/kojima": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+https://git.kitsu.cafe/rowan/kojima.git#b997bca5e31323bb81b38fafe5f823cae6e574de",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"izuna": "git+https://git.kitsu.cafe/rowan/izuna.git",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
package.json
25
package.json
|
@ -1,24 +1,13 @@
|
|||
{
|
||||
"name": "kuebiko",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./src/index.js",
|
||||
"types": "./typings/index.d.ts",
|
||||
"version": "2.0.0",
|
||||
"author": "Rowan <rowan@kitsu.cafe> (https://kitsu.cafe)",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "./tests/index.js",
|
||||
"build": "esbuild ./src/index.js --bundle --outfile=dist/index.js"
|
||||
"test": "node --test"
|
||||
},
|
||||
"keywords": [],
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"izuna": "git+https://git.kitsu.cafe/rowan/izuna.git",
|
||||
"kojima": "git+https://git.kitsu.cafe/rowan/kojima.git"
|
||||
}
|
||||
"keywords": ["parser", "functional", "combinator"],
|
||||
"license": "MIT",
|
||||
"description": ""
|
||||
}
|
||||
|
|
83
src/byte.js
83
src/byte.js
|
@ -1,83 +0,0 @@
|
|||
import { chain, curry } from 'izuna'
|
||||
import { any, map, seq } from './combinator.js'
|
||||
import { not } from './cond.js'
|
||||
import { Digits, LowerAlpha, UpperAlpha } from './const.js'
|
||||
import { clone, fail, join, mapStr, next, succeed } from './fn.js'
|
||||
import { ok } from 'kojima'
|
||||
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
||||
export const char = curry(
|
||||
/**
|
||||
* @param {string} ch
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
(ch, state) => {
|
||||
return next(state) === ch ? succeed(ch, state) : fail(`could not parse ${ch} `, state)
|
||||
})
|
||||
|
||||
export const tag = curry(
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
(str, state) => (
|
||||
map(
|
||||
join(''),
|
||||
seq(...mapStr(char, str))
|
||||
)(state)
|
||||
))
|
||||
|
||||
export const charNoCase = curry((ch, state) => {
|
||||
return any(
|
||||
char(ch.toLowerCase()),
|
||||
char(ch.toUpperCase())
|
||||
)(state)
|
||||
})
|
||||
|
||||
export const tagNoCase = curry(
|
||||
(str, state) => (
|
||||
map(
|
||||
join(''),
|
||||
seq(...mapStr(charNoCase, str))
|
||||
)(state)
|
||||
))
|
||||
|
||||
export const anyChar = state => {
|
||||
const ch = next(state)
|
||||
return !!ch ? succeed(ch, state) : fail('end of input', state)
|
||||
}
|
||||
|
||||
export const take = curry(
|
||||
(limit, state) => {
|
||||
let res = ok(state)
|
||||
|
||||
while (res.isOk() && limit > 0) {
|
||||
res = chain(anyChar, res)
|
||||
limit -= 1
|
||||
}
|
||||
return res
|
||||
}
|
||||
)
|
||||
|
||||
export const oneOf = curry(
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
(str, state) => (
|
||||
any(...mapStr(char, str))(state)
|
||||
))
|
||||
|
||||
export const noneOf = curry((str, state) => seq(not(any(oneOf(str), eof)), anyChar)(state))
|
||||
|
||||
export const digit = oneOf(Digits)
|
||||
export const lowerAlpha = oneOf(LowerAlpha)
|
||||
export const upperAlpha = oneOf(UpperAlpha)
|
||||
export const alpha = any(lowerAlpha, upperAlpha)
|
||||
export const alphanumeric = any(alpha, digit)
|
||||
|
||||
export const eof = state => {
|
||||
return clone(state).next().done ? succeed([], state) : fail('not end of stream', state)
|
||||
}
|
||||
|
11
src/clone.js
11
src/clone.js
|
@ -1,11 +0,0 @@
|
|||
export const Clone = Symbol('clone')
|
||||
|
||||
export const clone = target => {
|
||||
if (target[Clone]) {
|
||||
return target[Clone]()
|
||||
} else {
|
||||
return structuredClone(target)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import { chain, curry } from 'izuna'
|
||||
import { ok } from 'kojima'
|
||||
import { clone, diff, fail, succeed } from './fn.js'
|
||||
import { state as State } from './state.js'
|
||||
import { skip } from './cond.js'
|
||||
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
||||
/**
|
||||
* @param {...Parser} parsers
|
||||
*/
|
||||
export const seq = (...parsers) =>
|
||||
/** @param {ParserState} state */
|
||||
state => {
|
||||
let acc = ok(state)
|
||||
for (const parser of parsers) {
|
||||
if (acc.isOk()) {
|
||||
acc = chain(parser, acc)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {...any} parsers
|
||||
*/
|
||||
export const any = (...parsers) =>
|
||||
/**
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
state => {
|
||||
for (const parser of parsers) {
|
||||
//console.log('combinator.js::35/any:', state)
|
||||
const result = parser(clone(state))
|
||||
if (result.isOk()) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return fail('no matching parsers', state)
|
||||
}
|
||||
|
||||
export const map = curry(
|
||||
/**
|
||||
* @param {(...args: any[]) => any} fn
|
||||
* @param {Parser} parser
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
(fn, parser, state) => {
|
||||
return parser(state).chain(result => {
|
||||
try {
|
||||
/** @type {Result<ParserState, ParseError>} */
|
||||
const parsed = fn(diff(state[0], result[0]))
|
||||
const backtrack = State(result[1], state[0])
|
||||
|
||||
return succeed(parsed, backtrack)
|
||||
} catch (e) {
|
||||
return fail('failed to map', state, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export const delimited = curry((first, second, third, state) => seq(skip(first), second, skip(third))(state))
|
||||
export const preceded = curry((first, second, state) => seq(skip(first), second)(state))
|
||||
export const terminated = curry((first, second, state) => seq(first, skip(second))(state))
|
||||
|
||||
export const separatedPair = curry((first, delimiter, second, state) => seq(first, skip(delimiter), second)(state))
|
||||
|
55
src/cond.js
55
src/cond.js
|
@ -1,55 +0,0 @@
|
|||
import { ParseError } from './state.js'
|
||||
import { clone, fail, succeed } from './fn.js'
|
||||
import { anyChar } from './byte.js'
|
||||
import { ok } from 'kojima'
|
||||
import { chain, curry, pipe } from 'izuna'
|
||||
|
||||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
||||
export const maybe = curry(
|
||||
/**
|
||||
* @param {(...args: any[]) => Result<ParserState, ParseError>} parser
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
(parser, state) => {
|
||||
const result = parser(clone(state))
|
||||
return result.isOk() ? result : succeed([], state)
|
||||
})
|
||||
|
||||
export const not = curry((parser, state) => {
|
||||
const result = parser(clone(state))
|
||||
|
||||
if (result.isOk()) {
|
||||
return fail(`'not' parser failed`, state)
|
||||
} else {
|
||||
return succeed([], state)
|
||||
}
|
||||
})
|
||||
|
||||
export const until = curry((parser, state) => {
|
||||
let result = ok(state)
|
||||
|
||||
while (result.isOk()) {
|
||||
result = result.chain(pipe(clone, parser))
|
||||
if (result.isOk()) {
|
||||
break
|
||||
} else {
|
||||
result = anyChar(state)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
export const verify = curry((parser, predicate, state) => {
|
||||
const result = chain(parser, clone(state))
|
||||
return chain(x => (
|
||||
predicate(x) ? result : fail('verification failed', state)
|
||||
), result)
|
||||
})
|
||||
|
||||
export const skip = curry((parser, state) => {
|
||||
return chain(_ => succeed([], state), parser(state))
|
||||
})
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export const LowerAlpha = 'abcdefghijklmnopqrstuvwxyz'
|
||||
export const UpperAlpha = LowerAlpha.toUpperCase()
|
||||
export const Alpha = LowerAlpha + UpperAlpha
|
||||
export const Digits = '1234567890'
|
||||
export const Alphanumeric = Alpha + Digits
|
||||
|
69
src/fn.js
69
src/fn.js
|
@ -1,69 +0,0 @@
|
|||
import { err, ok } from 'kojima'
|
||||
import { concat, curry } from 'izuna'
|
||||
import { ParseError, state } from './state.js'
|
||||
import { IndexableIterator } from './iter.js'
|
||||
|
||||
/** @import { ParserState } from './state.js' */
|
||||
|
||||
|
||||
export const iter = value => new IndexableIterator(value)
|
||||
|
||||
/**
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
export const clone = (state) => state.clone()
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T | T[]} v
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
export const succeed = (v, [x, y]) => {
|
||||
return ok(state(y, concat(v, x)))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
* @param {ParserState} state
|
||||
* @param {Error} [e]
|
||||
*/
|
||||
export const fail = (msg, state, e = undefined) => err(new ParseError(msg, state, e))
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {number & keyof T} n
|
||||
* @param {T[]} iter
|
||||
*/
|
||||
export const nth = (n, iter) => iter[n]
|
||||
|
||||
/**
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
export const next = state => state.next().value
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T[]} a
|
||||
* @param {T[]} b
|
||||
*/
|
||||
export const diff = (a, b) => b.slice(-Math.max(0, b.length - a.length))
|
||||
|
||||
export const join = curry(
|
||||
/**
|
||||
* @param {string} delim
|
||||
* @param {string[]} val
|
||||
*/
|
||||
(delim, val) => val.join(delim)
|
||||
)
|
||||
|
||||
export const mapStr = curry(
|
||||
/**
|
||||
* @param {(...args: any[]) => any} fn
|
||||
* @param {string} str
|
||||
*/
|
||||
(fn, str) => Array.from(str).flatMap(x => {
|
||||
return fn(x)
|
||||
})
|
||||
)
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export * from './byte.js'
|
||||
export * from './combinator.js'
|
||||
export * from './cond.js'
|
||||
export * from './multi.js'
|
||||
export * from './state.js'
|
||||
|
418
src/iter.js
418
src/iter.js
|
@ -1,418 +0,0 @@
|
|||
import { Clone } from './clone.js'
|
||||
|
||||
export class IndexableIterator extends Iterator {
|
||||
_value
|
||||
_index
|
||||
|
||||
constructor(value, index = 0) {
|
||||
super()
|
||||
|
||||
this._value = value
|
||||
this._index = index
|
||||
}
|
||||
|
||||
clone() {
|
||||
return this[Clone]()
|
||||
}
|
||||
|
||||
done() {
|
||||
return this._index >= this._value.length
|
||||
}
|
||||
|
||||
next() {
|
||||
if (this.done()) {
|
||||
return { value: undefined, done: true }
|
||||
} else {
|
||||
return { value: this._value[this._index++], done: false }
|
||||
}
|
||||
}
|
||||
|
||||
[Clone]() {
|
||||
return new IndexableIterator(
|
||||
this._value,
|
||||
this._index
|
||||
)
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @implements Iterator<T>
|
||||
* @implements Iterable<T>
|
||||
*/
|
||||
class Iter {
|
||||
/**
|
||||
* @template T
|
||||
* @param {any} value
|
||||
* @returns {value is Iterable<T>}
|
||||
*/
|
||||
static _isIterable(value) {
|
||||
return typeof value[Symbol.iterator] === 'function'
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static from(value) {
|
||||
if (value instanceof Iter) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (Array.isArray(value) || typeof value === 'string') {
|
||||
return new IndexableIterator(value)
|
||||
}
|
||||
|
||||
if (Iter._isIterable(value)) {
|
||||
const iterator = value[Symbol.iterator]()
|
||||
|
||||
if (iterator instanceof Iter) {
|
||||
return iterator
|
||||
}
|
||||
|
||||
return new this(iterator, value)
|
||||
}
|
||||
|
||||
throw new TypeError('object is not an iterator')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} limit
|
||||
*/
|
||||
drop(limit) {
|
||||
return new DropIter(this, limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: T, index: number) => boolean} callbackFn
|
||||
*/
|
||||
every(callbackFn) {
|
||||
let next = this.next()
|
||||
let index = 0
|
||||
let result = true
|
||||
|
||||
while (!next.done) {
|
||||
if (!callbackFn(next.value, index)) {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
|
||||
next = this.next()
|
||||
index += 1
|
||||
}
|
||||
|
||||
this.return()
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: T, index: number) => boolean} callbackFn
|
||||
*/
|
||||
filter(callbackFn) {
|
||||
return new FilterIter(this, callbackFn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: T, index: number) => boolean} callbackFn
|
||||
*/
|
||||
find(callbackFn) {
|
||||
let next = this.next()
|
||||
let index = 0
|
||||
|
||||
while (!next.done) {
|
||||
if (callbackFn(next.value, index)) {
|
||||
this.return()
|
||||
return next.value
|
||||
}
|
||||
|
||||
next = this.next()
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {<U>(value: T, index: number) => U} callbackFn
|
||||
*/
|
||||
flatMap(callbackFn) {
|
||||
return new FlatMapIter(this, callbackFn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: T, index: number) => void} callbackFn
|
||||
*/
|
||||
forEach(callbackFn) {
|
||||
let next = this.next()
|
||||
let index = 0
|
||||
|
||||
while (!next.done) {
|
||||
callbackFn(next.value, index)
|
||||
next = this.next()
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {<U>(value: T, index: number) => U} callbackFn
|
||||
*/
|
||||
map(callbackFn) {
|
||||
return new MapIter(this, callbackFn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {(accumulator: U, value: T, index: number) => U} callbackFn
|
||||
* @param {U} init
|
||||
*/
|
||||
reduce(callbackFn, init) {
|
||||
let next = this.next()
|
||||
let index = 0
|
||||
let acc = init
|
||||
|
||||
while (!next.done) {
|
||||
acc = callbackFn(acc, next.value, index)
|
||||
|
||||
next = this.next()
|
||||
index += 1
|
||||
}
|
||||
|
||||
this.return()
|
||||
return acc
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: T, index: number) => boolean} callbackFn
|
||||
*/
|
||||
some(callbackFn) {
|
||||
let next = this.next()
|
||||
let index = 0
|
||||
let result = false
|
||||
|
||||
while (!next.done) {
|
||||
if (callbackFn(next.value, index)) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
|
||||
next = this.next()
|
||||
index += 1
|
||||
}
|
||||
|
||||
this.return()
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} limit
|
||||
*/
|
||||
take(limit) {
|
||||
return new TakeIter(this, limit)
|
||||
}
|
||||
|
||||
/*
|
||||
* @returns {T[]}
|
||||
*/
|
||||
toArray() {
|
||||
/** @type {T[]} */
|
||||
const result = []
|
||||
|
||||
for (const item of this) {
|
||||
result.push(item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends Iter<T>
|
||||
*/
|
||||
class DropIter extends Iter {
|
||||
_limit
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {number} limit
|
||||
*/
|
||||
constructor(iterator, limit) {
|
||||
super(iterator)
|
||||
this._limit = limit
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} value
|
||||
*/
|
||||
next(value) {
|
||||
for (let i = this._limit; i > 0; i--) {
|
||||
const next = super.next(value)
|
||||
|
||||
if (next.done) {
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
return super.next(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends Iter<T>
|
||||
*/
|
||||
class FilterIter extends Iter {
|
||||
_filter
|
||||
_index = 0
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {(value: T, index: number) => boolean} callbackFn
|
||||
*/
|
||||
constructor(iterator, callbackFn) {
|
||||
super(iterator)
|
||||
this._filter = callbackFn
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} [value]
|
||||
* @returns {IteratorResult<T>}
|
||||
*/
|
||||
next(value) {
|
||||
let next = super.next(value)
|
||||
|
||||
while (!next.done && !this._filter(next.value, this._index)) {
|
||||
next = super.next(value)
|
||||
this._index += 1
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends Iter<T>
|
||||
*/
|
||||
class FlatMapIter extends Iter {
|
||||
_flatMap
|
||||
_index = 0
|
||||
|
||||
/** @type {Iterator<T> | undefined} */
|
||||
_inner = undefined
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {<U>(value: T, index: number) => Iterator<U> | Iterable<U>} callbackFn
|
||||
*/
|
||||
constructor(iterator, callbackFn) {
|
||||
super(iterator)
|
||||
this._flatMap = callbackFn
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} value
|
||||
* @returns {IteratorResult<T>}
|
||||
*/
|
||||
next(value) {
|
||||
if (this._inner) {
|
||||
const innerResult = this._inner.next(value)
|
||||
|
||||
if (!innerResult.done) {
|
||||
this._index += 1
|
||||
return { value: innerResult.value, done: false }
|
||||
}
|
||||
|
||||
this._inner = undefined
|
||||
}
|
||||
|
||||
const outerResult = super.next(value)
|
||||
if (outerResult.done) {
|
||||
return { value: undefined, done: true }
|
||||
}
|
||||
|
||||
const nextIterable = this._flatMap(outerResult.value, this._index || 0)
|
||||
if (Iter._isIterable(nextIterable)) {
|
||||
this._inner = Iter.from(nextIterable)
|
||||
return this.next(value)
|
||||
} else {
|
||||
throw new TypeError('value is not an iterator')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends Iter<T>
|
||||
*/
|
||||
class MapIter extends Iter {
|
||||
_map
|
||||
_index = 0
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {<U>(value: T, index: number) => U} callbackFn
|
||||
*/
|
||||
constructor(iterator, callbackFn) {
|
||||
super(iterator)
|
||||
this._map = callbackFn
|
||||
}
|
||||
|
||||
/** @param {any} value */
|
||||
next(value) {
|
||||
let next = super.next(value)
|
||||
|
||||
if (next.done) {
|
||||
return next
|
||||
}
|
||||
|
||||
const result = {
|
||||
done: false,
|
||||
value: this._map(next.value, this._index)
|
||||
}
|
||||
|
||||
this._index += 1
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends Iter<T>
|
||||
*/
|
||||
class TakeIter extends Iter {
|
||||
_limit
|
||||
|
||||
/**
|
||||
* @param {Iterator<T>} iterator
|
||||
* @param {number} limit
|
||||
*/
|
||||
constructor(iterator, limit) {
|
||||
super(iterator)
|
||||
this._limit = limit
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} value
|
||||
* @returns {IteratorResult<T>}
|
||||
*/
|
||||
next(value) {
|
||||
if (this._limit > 0) {
|
||||
const next = super.next(value)
|
||||
if (!next.done) {
|
||||
this._limit -= 1
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
return { value: undefined, done: true }
|
||||
}
|
||||
}
|
||||
|
63
src/iterator.js
Normal file
63
src/iterator.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { Option } from './option.js'
|
||||
import { range, tee } from './utils.js'
|
||||
|
||||
export class Stream {
|
||||
_iterator
|
||||
_index
|
||||
|
||||
constructor(iterator, index = 0) {
|
||||
this._iterator = Iterator.from(iterator)
|
||||
this._index = index
|
||||
}
|
||||
|
||||
get index() {
|
||||
return this._index
|
||||
}
|
||||
|
||||
clone() {
|
||||
const [a, b] = tee(this._iterator)
|
||||
this._iterator = a
|
||||
return new Stream(b, this.index)
|
||||
}
|
||||
|
||||
consume() {
|
||||
return Option.from(this.next().value)
|
||||
}
|
||||
|
||||
*take(limit = 1) {
|
||||
for (const _index of range(limit)) {
|
||||
const next = this.next()
|
||||
if (next.done) { return }
|
||||
yield next.value
|
||||
}
|
||||
}
|
||||
|
||||
drop(limit = 1) {
|
||||
while (limit > 0 && this.consume().isSome()) {
|
||||
limit -= 1
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
peek() {
|
||||
const [a, b] = tee(this._iterator)
|
||||
this._iterator = a
|
||||
return Option.from(b.next().value)
|
||||
}
|
||||
|
||||
next() {
|
||||
const result = this._iterator.next()
|
||||
|
||||
if (!result.done) {
|
||||
this._index += 1
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
68
src/multi.js
68
src/multi.js
|
@ -1,68 +0,0 @@
|
|||
import { ok } from 'kojima'
|
||||
import { curry, } from 'izuna'
|
||||
import { clone } from './fn.js'
|
||||
import { verify } from './cond.js'
|
||||
import { anyChar } from './byte.js'
|
||||
|
||||
/** @import { ParseError, ParserState } from './state.js' */
|
||||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||
|
||||
export const takeWhile = curry((predicate, state) => {
|
||||
let result = ok(state)
|
||||
|
||||
while (result.isOk()) {
|
||||
result = verify(anyChar, predicate, state)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
export const takeUntil = curry((parser, state) => not(takeWhile(parser, state)))
|
||||
|
||||
export const skip = curry((parser, state) => {
|
||||
const tokens = state[0]
|
||||
const result = parser(state)
|
||||
if (result.isOk()) {
|
||||
return result.map(other => [tokens, other[1]])
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
export const many = curry((parser, state) => {
|
||||
let result = ok(state)
|
||||
|
||||
while (true) {
|
||||
const res = parser(clone(state))
|
||||
if (res.isOk()) {
|
||||
result = res
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
export const many1 = parser => seq(parser, many(parser))
|
||||
|
||||
const _range = (start, end, step = 1, deny = []) => {
|
||||
const len = end - start - deny.length + 1
|
||||
const result = new Array(len)
|
||||
|
||||
for (let i = 0, n = start; i <= len; i += i, n += step) {
|
||||
if (deny.includes(n)) { continue }
|
||||
result[i] = n
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const code = s => s.codePointAt(0)
|
||||
|
||||
export const range = (start, end, deny = []) => {
|
||||
return anyChar(
|
||||
_range(code(start), code(end), deny.map(code)).map(String.fromCodePoint)
|
||||
)
|
||||
}
|
||||
|
106
src/option.js
Normal file
106
src/option.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
import { Result } from './result.js'
|
||||
|
||||
export class Option {
|
||||
static some(value) {
|
||||
return new Some(value)
|
||||
}
|
||||
|
||||
static none() {
|
||||
return None
|
||||
}
|
||||
|
||||
static from(value) {
|
||||
if (value == null) {
|
||||
return None
|
||||
} else {
|
||||
return new Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
isSome() {
|
||||
return this instanceof Some
|
||||
}
|
||||
|
||||
isNone() {
|
||||
return this === None
|
||||
}
|
||||
}
|
||||
|
||||
export class Some extends Option {
|
||||
#value
|
||||
|
||||
constructor(value) {
|
||||
super()
|
||||
this.#value = value
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
ap(value) {
|
||||
return this.#value(value)
|
||||
}
|
||||
|
||||
filter(predicate) {
|
||||
if (predicate(this.#value)) {
|
||||
return this
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
andThen(fn) {
|
||||
return fn(this.#value)
|
||||
}
|
||||
|
||||
map(fn) {
|
||||
return new Some(fn(this.#value))
|
||||
}
|
||||
|
||||
okOr(_error) {
|
||||
return Result.ok(this.#value)
|
||||
}
|
||||
|
||||
okOrElse(_fn) {
|
||||
return Result.ok(this.#value)
|
||||
}
|
||||
|
||||
unwrap() {
|
||||
return this.#value
|
||||
}
|
||||
}
|
||||
|
||||
class _None extends Option {
|
||||
constructor() {
|
||||
super()
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
ap(_value) {
|
||||
return this
|
||||
}
|
||||
|
||||
filter(_predicate) {
|
||||
return this
|
||||
}
|
||||
|
||||
map(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
andThen(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
okOr(error) {
|
||||
return Result.err(error)
|
||||
}
|
||||
|
||||
okOrElse(fn) {
|
||||
return Result.err(fn())
|
||||
}
|
||||
|
||||
unwrap() {
|
||||
throw new UnwrapError('attempted to unwrap a None value')
|
||||
}
|
||||
}
|
||||
|
||||
export const None = new _None()
|
194
src/parser.js
Normal file
194
src/parser.js
Normal file
|
@ -0,0 +1,194 @@
|
|||
import { Stream } from './iterator.js'
|
||||
import { Result } from './result.js'
|
||||
import { curry, range } from './utils.js'
|
||||
import * as string from './string.js'
|
||||
|
||||
export class ParseError extends Error {
|
||||
name
|
||||
index
|
||||
description
|
||||
|
||||
constructor(index, description) {
|
||||
super()
|
||||
this.name = 'ParseError'
|
||||
this.index = index
|
||||
this.description = description
|
||||
}
|
||||
}
|
||||
|
||||
export class EofError extends ParseError {
|
||||
name = 'EofError'
|
||||
description = 'End of stream reached'
|
||||
}
|
||||
|
||||
// convenience factory functions and cached callbacks
|
||||
const eofErr = index => () => new EofError(index)
|
||||
const parseErr = (index, desc) => () => new ParseError(index, desc)
|
||||
const eq = a => b => a === b
|
||||
const toString = value => value.toString()
|
||||
|
||||
export const pure = curry((value, input) => Result.ok([value, input]))
|
||||
export const fail = curry((error, input) => Result.err(new ParseError(input.index, error)))
|
||||
|
||||
export const anyItem = () => input => {
|
||||
return input.peek().okOrElse(eofErr(input.index))
|
||||
.map(value => [value, input.drop()])
|
||||
}
|
||||
|
||||
export const satisfy = curry((predicate, input) => {
|
||||
return input.peek()
|
||||
.okOrElse(eofErr(input.index))
|
||||
.filterOrElse(
|
||||
predicate,
|
||||
value => parseErr(input.index, `Value did not match predicate: ${value}`)
|
||||
)
|
||||
.map(value => [value, input.drop()])
|
||||
})
|
||||
|
||||
export const literal = curry((value, input) => (
|
||||
satisfy(eq(value), input)
|
||||
))
|
||||
|
||||
export const bind = curry((parser, transform, input) =>
|
||||
parser(input).andThen(([value, rest]) => transform(value)(rest))
|
||||
)
|
||||
|
||||
export const map = curry((parser, morphism, input) => (
|
||||
parser(input).map(([value, rest]) => [morphism(value), rest])
|
||||
))
|
||||
|
||||
export const seq = curry((a, b, input) => (
|
||||
bind(a, x => map(b, y => [x, y]), input)
|
||||
))
|
||||
|
||||
export const alt = (...parsers) => input => {
|
||||
for (const p of parsers) {
|
||||
const result = p(input.clone())
|
||||
|
||||
if (result.isOk()) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return Result.err(new ParseError(input.index, "No parsers matched alt"))
|
||||
}
|
||||
|
||||
export const many = curry((parser, input) => {
|
||||
const results = []
|
||||
let stream = input
|
||||
|
||||
while (true) {
|
||||
const result = parser(stream.clone())
|
||||
if (result.isOk()) {
|
||||
const [value, rest] = result.unwrap()
|
||||
results.push(value)
|
||||
stream = rest
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok([results, stream])
|
||||
})
|
||||
|
||||
export const many1 = curry((parser, input) => (
|
||||
parser(input).andThen(([first, rest]) => (
|
||||
many(parser, rest).map(([others, rest]) => (
|
||||
[[first, ...others], rest]
|
||||
))
|
||||
))
|
||||
))
|
||||
|
||||
export const optional = curry((parser, input) =>
|
||||
parser(input.clone()).orElse(() => Result.ok([undefined, input]))
|
||||
)
|
||||
|
||||
export const eof = () => input => (
|
||||
anyItem()(input)
|
||||
.andThen(([value, rest]) =>
|
||||
Result.err(
|
||||
new ParseError(rest.index, `Expected EOF, found ${value}`)
|
||||
)
|
||||
)
|
||||
.orElse(err => {
|
||||
if (err instanceof EofError) {
|
||||
return Result.ok([null, input])
|
||||
} else {
|
||||
return Result.err(err)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
export const take = curry((limit, input) => {
|
||||
const result = []
|
||||
const next = anyItem()
|
||||
let stream = input
|
||||
|
||||
for (const _index of range(limit)) {
|
||||
const nextResult = next(stream)
|
||||
if (nextResult.isOk()) {
|
||||
const [value, rest] = nextResult.unwrap()
|
||||
result.push(value)
|
||||
stream = rest
|
||||
} else {
|
||||
return nextResult
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok([result, stream])
|
||||
})
|
||||
|
||||
export const drop = curry((limit, input) =>
|
||||
skip(take(limit), input)
|
||||
)
|
||||
|
||||
export const skip = curry((parser, input) =>
|
||||
parser(input).map(([_, rest]) => [undefined, rest])
|
||||
)
|
||||
|
||||
const streamInfo = (stream, n = 3) => {
|
||||
const clone = stream.clone()
|
||||
const values = clone.take(n).map(toString).join(', ')
|
||||
const hasMore = clone.peek().isSome()
|
||||
const ellip = hasMore ? '...' : ''
|
||||
|
||||
return `Stream { index = ${stream.index}, values = [${values}${ellip}]`
|
||||
}
|
||||
|
||||
export const trace = curry((parser, label, input) => {
|
||||
const name = label || parser.name || 'anonymous parser'
|
||||
console.log(`trace(parser = ${name}, input = ${streamInfo(input)})`)
|
||||
const result = parser(input)
|
||||
if (result.isOk()) {
|
||||
const [value, rest] = result.unwrap()
|
||||
console.log(` success: value = ${toString(value)}, remaining = ${streamInfo(rest)}`)
|
||||
} else {
|
||||
const err = result.unwrapErr()
|
||||
console.log(` fail: error = ${err}`)
|
||||
}
|
||||
})
|
||||
|
||||
// cached parsers
|
||||
const LetterParser = alt(...Array.from(string.AsciiLetters).map(x => literal(x)))
|
||||
const DigitParser = alt(...Array.from(string.Digits).map(x => literal(x)))
|
||||
const WhitespaceParser = alt(...Array.from(string.Whitespace).map(x => literal(x)))
|
||||
|
||||
export const letter = () => LetterParser
|
||||
export const digit = () => DigitParser
|
||||
export const whitespace = () => WhitespaceParser
|
||||
|
||||
export const parseSome = curry((parser, value) =>
|
||||
parser(new Stream(value))
|
||||
)
|
||||
|
||||
export const parse = curry((parser, value) => {
|
||||
const result = parseSome(seq(parser, eof()), value)
|
||||
|
||||
if (result.isOk()) {
|
||||
const [[value, _rest], _stream] = result.unwrap()
|
||||
return Result.ok(value)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
163
src/result.js
Normal file
163
src/result.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
import { None } from './option.js'
|
||||
|
||||
class UnwrapError extends Error {
|
||||
constructor(message) {
|
||||
super(message)
|
||||
this.name = 'UnwrapError'
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, UnwrapError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Result {
|
||||
static ok(value) {
|
||||
return new Ok(value)
|
||||
}
|
||||
|
||||
static err(error) {
|
||||
return new Err(error)
|
||||
}
|
||||
|
||||
isOk() {
|
||||
return this instanceof Ok
|
||||
}
|
||||
|
||||
isErr() {
|
||||
return this instanceof Err
|
||||
}
|
||||
}
|
||||
|
||||
export class Ok extends Result {
|
||||
#value
|
||||
|
||||
constructor(value) {
|
||||
super()
|
||||
this.#value = value
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
ap(value) {
|
||||
return this.#value(value)
|
||||
}
|
||||
|
||||
andThen(fn) {
|
||||
return fn(this.#value)
|
||||
}
|
||||
|
||||
map(fn) {
|
||||
return new Ok(fn(this.#value))
|
||||
}
|
||||
|
||||
filterOr(predicate, err) {
|
||||
if (predicate(this.#value)) {
|
||||
return this
|
||||
} else {
|
||||
return Result.err(err)
|
||||
}
|
||||
}
|
||||
|
||||
filterOrElse(predicate, fn) {
|
||||
if (predicate(this.#value)) {
|
||||
return this
|
||||
} else {
|
||||
return Result.err(fn(this.#value))
|
||||
}
|
||||
}
|
||||
|
||||
inspect(fn) {
|
||||
fn(this.#value)
|
||||
return this
|
||||
}
|
||||
|
||||
ok() {
|
||||
return Option.some(this.#value)
|
||||
}
|
||||
|
||||
err() {
|
||||
return None
|
||||
}
|
||||
|
||||
orOther(_value) {
|
||||
return this
|
||||
}
|
||||
|
||||
orElse(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
unwrap() {
|
||||
return this.#value
|
||||
}
|
||||
|
||||
unwrapErr() {
|
||||
throw new UnwrapError('attempted to unwrapErr an Ok value')
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Ok(${this.#value})`
|
||||
}
|
||||
}
|
||||
|
||||
export class Err extends Result {
|
||||
#error
|
||||
|
||||
constructor(error) {
|
||||
super()
|
||||
this.#error = error
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
ap(_value) {
|
||||
return this
|
||||
}
|
||||
|
||||
andThen(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
map(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
filterOr(_predicate, _err) {
|
||||
return this
|
||||
}
|
||||
|
||||
filterOrElse(_predicate, _fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
inspect(_fn) {
|
||||
return this
|
||||
}
|
||||
|
||||
ok() {
|
||||
return None
|
||||
}
|
||||
|
||||
err() {
|
||||
return Option.some(this.#error)
|
||||
}
|
||||
|
||||
orOther(value) {
|
||||
return value
|
||||
}
|
||||
|
||||
orElse(fn) {
|
||||
return fn(this.#error)
|
||||
}
|
||||
|
||||
unwrap() {
|
||||
throw new UnwrapError('attempted to unwrap an Err value')
|
||||
}
|
||||
|
||||
unwrapErr() {
|
||||
return this.#error
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Err(${this.#error})`
|
||||
}
|
||||
}
|
||||
|
60
src/state.js
60
src/state.js
|
@ -1,60 +0,0 @@
|
|||
import { ok } from 'kojima'
|
||||
import { chain, compose, curry, pipe } from 'izuna'
|
||||
import { eof } from './byte.js'
|
||||
import { Tuple } from './tuple.js'
|
||||
import { iter } from './fn.js'
|
||||
import { Clone } from './clone.js'
|
||||
|
||||
/**
|
||||
* @typedef {Readonly<[any[], Iterator<any>]>} ParserState
|
||||
*/
|
||||
|
||||
export class ParseError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {ParserState} state
|
||||
* @param {Error} [cause]
|
||||
*/
|
||||
constructor(message, state, cause) {
|
||||
super(message, { cause })
|
||||
this.state = state
|
||||
}
|
||||
}
|
||||
|
||||
export class State extends Tuple {
|
||||
constructor(remaining, read = []) {
|
||||
super([['read', read], ['remaining', remaining]])
|
||||
}
|
||||
|
||||
static from(values) {
|
||||
return new State(iter(values))
|
||||
}
|
||||
|
||||
next() {
|
||||
return this.remaining.next()
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `State(${this.read}, ${[...this.clone().remaining]})`
|
||||
}
|
||||
|
||||
[Clone]() {
|
||||
return Object.freeze(new State(this.remaining.clone(), this.read))
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return super[Symbol.iterator]()
|
||||
}
|
||||
}
|
||||
|
||||
export const state = (remaining, read) => new State(remaining, read)
|
||||
|
||||
export const parse = curry((parser, input) => pipe(
|
||||
iter,
|
||||
state,
|
||||
ok,
|
||||
chain(parser),
|
||||
)(input))
|
||||
|
||||
export const parseAll = curry((parser, input) => compose(eof, parse(parser), input))
|
||||
|
10
src/string.js
Normal file
10
src/string.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export const LowerAscii = 'abcdefghijklmnopqrstuvwxyz'
|
||||
export const UpperAscii = LowerAscii.toLocaleLowerCase()
|
||||
export const AsciiLetters = LowerAscii + UpperAscii
|
||||
export const HexDigits = '0123456789abcdefABCDEF'
|
||||
export const OctDigits = '01234567'
|
||||
export const Punctuation = '!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~.'
|
||||
export const Whitespace = ' \t\r\n\x0c\x0b'
|
||||
export const Digits = '0123456789'
|
||||
export const Printable = Digits + AsciiLetters + Punctuation + Whitespace
|
||||
|
61
src/tuple.js
61
src/tuple.js
|
@ -1,61 +0,0 @@
|
|||
import { entries } from 'izuna'
|
||||
import { Clone } from './clone.js'
|
||||
import { IndexableIterator } from './iter.js'
|
||||
|
||||
/**
|
||||
* @param {...any} values
|
||||
*/
|
||||
export class Tuple {
|
||||
_values
|
||||
_length
|
||||
|
||||
get length() {
|
||||
return this._length
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (values.length > 0) {
|
||||
this._length = values.length
|
||||
values.forEach(this._setEntry.bind(this))
|
||||
}
|
||||
|
||||
this._values = values
|
||||
}
|
||||
|
||||
_setEntry([key, value], index) {
|
||||
if (typeof key !== 'number') {
|
||||
Object.defineProperty(this, key, {
|
||||
get() {
|
||||
return this[index]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this[index] = value
|
||||
}
|
||||
|
||||
static from(values) {
|
||||
return Object.freeze(new this(entries(values)))
|
||||
}
|
||||
|
||||
|
||||
toString() {
|
||||
return `(${this._values.map(([_, v]) => v.toString())})`
|
||||
}
|
||||
|
||||
clone() {
|
||||
return this[Clone]()
|
||||
}
|
||||
|
||||
[Clone]() {
|
||||
return Object.freeze(new this.constructor(this._values))
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return new IndexableIterator(this._values.map(([_, v]) => v))
|
||||
}
|
||||
}
|
||||
|
||||
export const tuple = Tuple.from
|
||||
|
||||
|
41
src/utils.js
Normal file
41
src/utils.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
export const curry = (fn, arity = fn.length) => {
|
||||
return function curried(...args) {
|
||||
if (args.length >= arity) {
|
||||
return fn.apply(this, args)
|
||||
} else {
|
||||
return function(...nextArgs) {
|
||||
return curried.apply(this, args.concat(nextArgs))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function* range(start, stop, step = 1) {
|
||||
if (stop == null) {
|
||||
stop = start
|
||||
start = 0
|
||||
}
|
||||
|
||||
for (let i = start; i < stop; i += step) {
|
||||
yield i
|
||||
}
|
||||
}
|
||||
|
||||
export function tee(iterator, n = 2) {
|
||||
iterator = Iterator.from(iterator)
|
||||
|
||||
function* gen(current) {
|
||||
while (true) {
|
||||
if (!current.next) {
|
||||
const { done, value } = iterator.next()
|
||||
if (done) return
|
||||
current.next = { value }
|
||||
}
|
||||
current = current.next
|
||||
yield current.value
|
||||
}
|
||||
}
|
||||
|
||||
return Array(n).fill({}).map(gen)
|
||||
}
|
||||
|
205
tests/combinators.test.js
Normal file
205
tests/combinators.test.js
Normal file
|
@ -0,0 +1,205 @@
|
|||
import { drop, skip, literal, bind, seq, many, parse, parseSome, ParseError, EofError, map, alt, many1, optional } from '../src/parser.js'
|
||||
import { Err } from '../src/result.js'
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
const eq = (a, b) => assert.deepStrictEqual(a, b)
|
||||
const parseErr = (i, desc) => new Err(new ParseError(i, desc))
|
||||
const eof = (i = 0) => new Err(new EofError(i))
|
||||
|
||||
const streamState = stream => {
|
||||
return [stream.index, Array.from(stream.clone())]
|
||||
}
|
||||
|
||||
describe('Combinators', () => {
|
||||
it('bind', () => {
|
||||
const a = literal('a')
|
||||
const b = literal('b')
|
||||
|
||||
// simple parser
|
||||
const p = bind(a, () => b)
|
||||
|
||||
let res = parseSome(p, 'ab')
|
||||
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, 'b')
|
||||
eq(streamState(r1), [2, []])
|
||||
|
||||
|
||||
// complex parser
|
||||
const pab = bind(a, x => map(b, y => `${x}${y}`))
|
||||
res = parseSome(pab, 'abc')
|
||||
const [v2, r2] = res.unwrap()
|
||||
assert(res.isOk())
|
||||
eq(v2, 'ab')
|
||||
eq(streamState(r2), [2, ['c']])
|
||||
|
||||
// fail on first parser
|
||||
res = parseSome(p, 'xb')
|
||||
eq(res, parseErr(0, 'Value did not match predicate: x'))
|
||||
|
||||
// fail on second parser
|
||||
res = parseSome(p, 'ax')
|
||||
eq(res, parseErr(1, 'Value did not match predicate: x'))
|
||||
|
||||
// eof on first parser
|
||||
res = parseSome(p, '')
|
||||
eq(res, eof())
|
||||
|
||||
// eof on second parser
|
||||
res = parseSome(p, 'a')
|
||||
eq(res, eof())
|
||||
})
|
||||
|
||||
it('map', () => {
|
||||
const p = map(literal('a'), char => char.toUpperCase())
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [value, rest] = res.unwrap()
|
||||
eq(value, 'A')
|
||||
eq(streamState(rest), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'xyz')
|
||||
eq(res, parseErr(0, 'Value did not match predicate: x'))
|
||||
})
|
||||
|
||||
it('seq', () => {
|
||||
const p = seq(literal('a'), literal('b'))
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [value, rest] = res.unwrap()
|
||||
eq(value, ['a', 'b'])
|
||||
eq(streamState(rest), [2, ['c']])
|
||||
|
||||
res = parseSome(p, 'xbc')
|
||||
eq(res, parseErr(0, 'Value did not match predicate: x'))
|
||||
|
||||
res = parseSome(p, 'axc')
|
||||
eq(res, parseErr(1, 'Value did not match predicate: x'))
|
||||
})
|
||||
|
||||
it('alt', () => {
|
||||
const p = alt(literal('a'), literal('b'))
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, 'a')
|
||||
eq(streamState(r1), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'bac')
|
||||
assert(res.isOk())
|
||||
const [v2, r2] = res.unwrap()
|
||||
eq(v2, 'b')
|
||||
eq(streamState(r2), [1, ['a', 'c']])
|
||||
|
||||
res = parseSome(p, 'xyz')
|
||||
eq(res, parseErr(1, 'No parsers matched alt'))
|
||||
|
||||
res = parseSome(alt(), 'abc')
|
||||
eq(res, parseErr(0, 'No parsers matched alt'))
|
||||
})
|
||||
|
||||
it('many', () => {
|
||||
const p = many(literal('a'))
|
||||
let res = parseSome(p, 'xyz')
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, [])
|
||||
eq(streamState(r1), [0, ['x', 'y', 'z']])
|
||||
|
||||
res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [v2, r2] = res.unwrap()
|
||||
eq(v2, ['a'])
|
||||
eq(streamState(r2), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'aaabc')
|
||||
assert(res.isOk())
|
||||
const [v3, r3] = res.unwrap()
|
||||
eq(v3, ['a', 'a', 'a'])
|
||||
eq(streamState(r3), [3, ['b', 'c']])
|
||||
|
||||
res = parse(p, 'aaa')
|
||||
assert(res.isOk())
|
||||
eq(res.unwrap(), ['a', 'a', 'a'])
|
||||
|
||||
res = parseSome(p, '')
|
||||
assert(res.isOk())
|
||||
const [v4, r4] = res.unwrap()
|
||||
eq(v4, [])
|
||||
eq(streamState(r4), [0, []])
|
||||
})
|
||||
|
||||
it('many1', () => {
|
||||
const p = many1(literal('a'))
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, ['a'])
|
||||
eq(streamState(r1), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'aaab')
|
||||
assert(res.isOk())
|
||||
const [v2, r2] = res.unwrap()
|
||||
eq(v2, ['a', 'a', 'a'])
|
||||
eq(streamState(r2), [3, ['b']])
|
||||
|
||||
res = parseSome(p, 'xyz')
|
||||
eq(res, parseErr(0, 'Value did not match predicate: x'))
|
||||
|
||||
res = parseSome(p, '')
|
||||
eq(res, eof())
|
||||
})
|
||||
|
||||
it('optional', () => {
|
||||
const p = optional(literal('a'))
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, 'a')
|
||||
eq(streamState(r1), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'xyz')
|
||||
assert(res.isOk())
|
||||
const [v2, r2] = res.unwrap()
|
||||
eq(v2, undefined)
|
||||
eq(streamState(r2), [0, ['x', 'y', 'z']])
|
||||
|
||||
res = parseSome(p, '')
|
||||
assert(res.isOk())
|
||||
const [v3, r3] = res.unwrap()
|
||||
eq(v3, undefined)
|
||||
eq(streamState(r3), [0, []])
|
||||
})
|
||||
|
||||
it('drop', () => {
|
||||
const p = drop(1)
|
||||
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, undefined)
|
||||
eq(streamState(r1), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, '')
|
||||
eq(res, eof())
|
||||
})
|
||||
|
||||
it('skip', () => {
|
||||
const p = skip(literal('a'))
|
||||
let res = parseSome(p, 'abc')
|
||||
assert(res.isOk())
|
||||
const [v1, r1] = res.unwrap()
|
||||
eq(v1, undefined)
|
||||
eq(streamState(r1), [1, ['b', 'c']])
|
||||
|
||||
res = parseSome(p, 'xyz')
|
||||
eq(res, parseErr(0, 'Value did not match predicate: x'))
|
||||
|
||||
res = parseSome(p, '')
|
||||
eq(res, eof())
|
||||
})
|
||||
})
|
||||
|
2
tests/index.d.ts
vendored
2
tests/index.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
export {};
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { TerminalRunner } from 'folktest'
|
||||
import * as Tests from './units/index.js'
|
||||
|
||||
console.log(TerminalRunner(Tests).toString())
|
||||
|
127
tests/iterator.test.js
Normal file
127
tests/iterator.test.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
import { Stream } from '../src/iterator.js'
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
describe('Stream class', () => {
|
||||
it('should iterate a range', () => {
|
||||
const stream = new Stream([1, 2, 3])
|
||||
|
||||
assert.equal(stream.index, 0)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 1, done: false })
|
||||
assert.equal(stream.index, 1)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 2, done: false })
|
||||
assert.equal(stream.index, 2)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 3, done: false })
|
||||
assert.equal(stream.index, 3)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: undefined, done: true })
|
||||
assert.equal(stream.index, 3)
|
||||
})
|
||||
|
||||
it('should handle an empty stream', () => {
|
||||
const stream = new Stream([])
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: undefined, done: true })
|
||||
assert.equal(stream.index, 0)
|
||||
})
|
||||
|
||||
it('should handle a single item', () => {
|
||||
const stream = new Stream([1])
|
||||
|
||||
assert.equal(stream.index, 0)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 1, done: false })
|
||||
assert.equal(stream.index, 1)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: undefined, done: true })
|
||||
assert.equal(stream.index, 1)
|
||||
})
|
||||
|
||||
it('should should an independent stream', () => {
|
||||
const stream = new Stream([1, 2, 3])
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 1, done: false })
|
||||
assert.equal(stream.index, 1)
|
||||
|
||||
const clone = stream.clone()
|
||||
assert.equal(clone.index, 1)
|
||||
|
||||
assert.deepStrictEqual(clone.next(), { value: 2, done: false })
|
||||
assert.equal(clone.index, 2)
|
||||
|
||||
assert.deepStrictEqual(clone.next(), { value: 3, done: false })
|
||||
assert.equal(clone.index, 3)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 2, done: false })
|
||||
assert.equal(stream.index, 2)
|
||||
})
|
||||
|
||||
it('should support multiple clones', () => {
|
||||
const stream = new Stream('abcdefg')
|
||||
stream.next()
|
||||
stream.next()
|
||||
|
||||
assert.deepStrictEqual(stream.index, 2)
|
||||
|
||||
const clone1 = stream.clone()
|
||||
assert.deepStrictEqual(clone1.index, 2)
|
||||
|
||||
stream.next()
|
||||
stream.next()
|
||||
assert.deepStrictEqual(stream.index, 4)
|
||||
|
||||
const clone2 = stream.clone()
|
||||
assert.deepStrictEqual(clone2.index, 4)
|
||||
|
||||
assert.deepStrictEqual(clone1.next(), { value: 'c', done: false })
|
||||
assert.deepStrictEqual(clone1.next(), { value: 'd', done: false })
|
||||
assert.deepStrictEqual(clone1.next(), { value: 'e', done: false })
|
||||
assert.deepStrictEqual(clone1.index, 5)
|
||||
|
||||
assert.deepStrictEqual(clone2.next(), { value: 'e', done: false })
|
||||
assert.deepStrictEqual(clone2.next(), { value: 'f', done: false })
|
||||
assert.deepStrictEqual(clone2.index, 6)
|
||||
|
||||
assert.deepStrictEqual(stream.next(), { value: 'e', done: false })
|
||||
assert.deepStrictEqual(stream.index, 5)
|
||||
})
|
||||
|
||||
it('should yield a value on consume', () => {
|
||||
const stream = new Stream('abc')
|
||||
assert.deepStrictEqual(stream.consume().unwrap(), 'a')
|
||||
assert.deepStrictEqual(stream.consume().unwrap(), 'b')
|
||||
assert.deepStrictEqual(stream.consume().unwrap(), 'c')
|
||||
assert(stream.consume().isNone())
|
||||
})
|
||||
|
||||
it('should take from a stream', () => {
|
||||
const stream5 = new Stream([1, 2, 3, 4, 5])
|
||||
const take3 = stream5.take(3)
|
||||
assert.deepStrictEqual(Array.from(take3), [1, 2, 3])
|
||||
|
||||
assert.deepStrictEqual(stream5.index, 3)
|
||||
assert.deepStrictEqual(Array.from(stream5), [4, 5])
|
||||
|
||||
const emptyStream = new Stream([])
|
||||
const take0 = emptyStream.take(3)
|
||||
assert.deepStrictEqual(Array.from(take0), [])
|
||||
assert.deepStrictEqual(emptyStream.index, 0)
|
||||
assert.deepStrictEqual(Array.from(emptyStream), [])
|
||||
|
||||
const streamDefault = new Stream('abc')
|
||||
const genDefault = streamDefault.take()
|
||||
assert.deepStrictEqual(Array.from(genDefault), ['a'])
|
||||
assert.deepStrictEqual(streamDefault.index, 1)
|
||||
assert.deepStrictEqual(Array.from(streamDefault), ['b', 'c'])
|
||||
|
||||
const stream3 = new Stream([10, 20, 30])
|
||||
const genZero = streamDefault.take(0)
|
||||
assert.deepStrictEqual(Array.from(genZero), [])
|
||||
assert.deepStrictEqual(stream3.index, 0)
|
||||
assert.deepStrictEqual(Array.from(stream3), [10, 20, 30])
|
||||
})
|
||||
})
|
||||
|
82
tests/primitives.test.js
Normal file
82
tests/primitives.test.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { pure, fail, anyItem, satisfy, literal, parse, parseSome, ParseError, EofError } from '../src/parser.js'
|
||||
import { Err, Result } from '../src/result.js'
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
const eq = (a, b) => assert.deepStrictEqual(a, b)
|
||||
const parseErr = (i, desc) => new Err(new ParseError(i, desc))
|
||||
const eof = (i = 0) => new Err(new EofError(i))
|
||||
|
||||
const streamState = stream => {
|
||||
return [stream.index, Array.from(stream.clone())]
|
||||
}
|
||||
|
||||
describe('Primitive parsers', () => {
|
||||
it('pure', () => {
|
||||
const result = parseSome(pure(123), 'abc')
|
||||
const [value, stream] = result.unwrap()
|
||||
|
||||
eq(value, 123)
|
||||
eq(streamState(stream), [0, ['a', 'b', 'c']])
|
||||
|
||||
const empty = parse(pure(123), [])
|
||||
eq(empty, Result.ok(123))
|
||||
})
|
||||
|
||||
it('fail', () => {
|
||||
const msg = 'Expected failure'
|
||||
const parser = fail(msg)
|
||||
const expected = parseErr(0, msg)
|
||||
|
||||
eq(parse(parser, 'abc'), expected)
|
||||
eq(parse(parser, ''), expected)
|
||||
})
|
||||
|
||||
it('anyItem', () => {
|
||||
const parser = anyItem()
|
||||
eq(parse(parser, 'a'), Result.ok('a'))
|
||||
|
||||
const many = parseSome(parser, 'abc')
|
||||
assert(many.isOk())
|
||||
const [value, rest] = many.unwrap()
|
||||
|
||||
eq(value, 'a')
|
||||
eq(streamState(rest), [1, ['b', 'c']])
|
||||
|
||||
eq(parse(parser, ''), eof())
|
||||
})
|
||||
|
||||
it('satisfy', () => {
|
||||
const pa = satisfy(x => x === 'a')
|
||||
|
||||
const result = parseSome(pa, 'abc')
|
||||
assert(result.isOk())
|
||||
|
||||
const [value, rest] = result.unwrap()
|
||||
|
||||
eq(value, 'a')
|
||||
eq(streamState(rest), [1, ['b', 'c']])
|
||||
|
||||
const err = parse(pa, 'cba')
|
||||
eq(err, parseErr(0, 'Value did not match predicate: c'))
|
||||
|
||||
const eofErr = parse(pa, '')
|
||||
eq(eofErr, eof())
|
||||
})
|
||||
|
||||
it('literal', () => {
|
||||
const px = literal('x')
|
||||
const result = parseSome(px, 'xyz')
|
||||
assert(result.isOk())
|
||||
|
||||
const [value, rest] = result.unwrap()
|
||||
eq(value, 'x')
|
||||
eq(streamState(rest), [1, ['y', 'z']])
|
||||
|
||||
const err = parse(px, 'yz')
|
||||
eq(err, parseErr(0, 'Value did not match predicate: y'))
|
||||
|
||||
const eofErr = parse(px, '')
|
||||
eq(eofErr, eof())
|
||||
})
|
||||
})
|
1
tests/units/bytes.d.ts
vendored
1
tests/units/bytes.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
export const Byte: any[];
|
|
@ -1,70 +0,0 @@
|
|||
import { it, assert, assertEq } from 'folktest'
|
||||
import { char, charNoCase, noneOf, oneOf, parse, tag, tagNoCase, take, takeWhile } from '../../src/index.js'
|
||||
import { Alphanumeric } from '../../src/const.js'
|
||||
import { chain, curry, map } from 'izuna'
|
||||
import { assertState, parseEq } from './common.js'
|
||||
|
||||
export const Byte = [
|
||||
it('char', () => {
|
||||
const parser = char('a')
|
||||
|
||||
parseEq(parser, 'abc', ['a'])
|
||||
assert(parse(parser, ' abc').isErr())
|
||||
assert(parse(parser, 'bc').isErr())
|
||||
assert(parse(parser, '').isErr())
|
||||
}),
|
||||
|
||||
it('oneOf', () => {
|
||||
parse(oneOf('abc'), 'b').chain(assertState('b'))
|
||||
assert(parse(oneOf('a'), 'bc').isErr())
|
||||
assert(parse(oneOf('a'), '').isErr())
|
||||
}),
|
||||
|
||||
it('noneOf', () => {
|
||||
parse(noneOf('abc'), 'z').chain(assertState(['z']))
|
||||
assert(parse(noneOf('ab'), 'a').isErr())
|
||||
assert(parse(noneOf('ab'), '').isErr())
|
||||
}),
|
||||
|
||||
it('tag', () => {
|
||||
const parser = tag('Hello')
|
||||
parse(parser, 'Hello, World!').chain(assertState(['Hello']))
|
||||
assert(parse(parser, 'Something').isErr())
|
||||
assert(parse(parser, '').isErr())
|
||||
}),
|
||||
|
||||
it('charNoCase', () => {
|
||||
const parser = charNoCase('a')
|
||||
parse(parser, 'abc').chain(assertState(['a']))
|
||||
parse(parser, 'Abc').chain(assertState(['A']))
|
||||
assert(parse(parser, ' abc').isErr())
|
||||
assert(parse(parser, 'bc').isErr())
|
||||
assert(parse(parser, '').isErr())
|
||||
}),
|
||||
|
||||
it('tagNoCase', () => {
|
||||
const parser = tagNoCase('hello')
|
||||
parse(parser, 'Hello, World!').chain(assertState(['Hello']))
|
||||
parse(parser, 'hello, World!').chain(assertState(['hello']))
|
||||
parse(parser, 'HeLlO, World!').chain(assertState(['HeLlO']))
|
||||
assert(parse(parser, 'Something').isErr())
|
||||
assert(parse(parser, '').isErr())
|
||||
}),
|
||||
|
||||
it('take', () => {
|
||||
const parser = take(6)
|
||||
parse(parser, '1234567').chain(assertState('123456'.split('')))
|
||||
parse(parser, 'things').chain(assertState('things'.split('')))
|
||||
assert(parse(parser, 'short').isErr())
|
||||
assert(parse(parser, '').isErr())
|
||||
}),
|
||||
|
||||
it('takeWhile', () => {
|
||||
const parser = takeWhile(x => Alphanumeric.includes(x))
|
||||
parse(parser, 'latin123').chain(assertState(['latin']))
|
||||
parse(parser, '123').chain(assertState([]))
|
||||
parse(parser, 'latin').chain(assertState(['latin']))
|
||||
parse(parser, '').chain(assertState([]))
|
||||
})
|
||||
]
|
||||
|
3
tests/units/common.d.ts
vendored
3
tests/units/common.d.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
export const assertState: any;
|
||||
export const parseEq: any;
|
||||
export const parseErr: any;
|
|
@ -1,15 +0,0 @@
|
|||
import { chain, curry } from 'izuna'
|
||||
import { parse } from '../../src/index.js'
|
||||
|
||||
export const assertState = curry((expected, state) => {
|
||||
assertEq(expected, state[0])
|
||||
})
|
||||
|
||||
export const parseEq = curry((parser, input, expected) =>
|
||||
chain(assertState(expected), parse(parser, input))
|
||||
)
|
||||
|
||||
export const parseErr = curry((parser, input) =>
|
||||
assert(parse(parser, input).isErr(), `expected an error but '${input}' parsed successfully`)
|
||||
)
|
||||
|
1
tests/units/cond.d.ts
vendored
1
tests/units/cond.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
export const Conditionals: any[];
|
|
@ -1,11 +0,0 @@
|
|||
import { it, assertEq } from 'folktest'
|
||||
import { anyChar, parse } from '../../src/index.js'
|
||||
import { skip } from '../../src/cond.js'
|
||||
import { parseEq } from './common.js'
|
||||
|
||||
export const Conditionals = [
|
||||
it('skip', () => {
|
||||
parseEq(skip(anyChar), 'test', [])
|
||||
})
|
||||
]
|
||||
|
3
tests/units/index.d.ts
vendored
3
tests/units/index.d.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
export * from "./bytes.js";
|
||||
export * from "./iter.js";
|
||||
export * from "./cond.js";
|
|
@ -1,4 +0,0 @@
|
|||
export * from './bytes.js'
|
||||
export * from './iter.js'
|
||||
export * from './cond.js'
|
||||
|
1
tests/units/iter.d.ts
vendored
1
tests/units/iter.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
export const Iterator: any[];
|
|
@ -1,21 +0,0 @@
|
|||
import { it, assert, assertEq } from 'folktest'
|
||||
import { IndexableIterator } from '../../src/iter.js'
|
||||
import { state } from '../../src/state.js'
|
||||
|
||||
export const Iterator = [
|
||||
it('should be cloneable', () => {
|
||||
const hi = new IndexableIterator('hi :3')
|
||||
const hihi = hi.clone()
|
||||
|
||||
assertEq(hi.next().value, 'h')
|
||||
|
||||
assertEq(hi.next().value, 'i')
|
||||
assertEq(hihi.next().value, 'h')
|
||||
}),
|
||||
|
||||
it('should have iterator helpers', () => {
|
||||
const aaaaa = new IndexableIterator('awawawawa').filter(x => x !== 'w')
|
||||
assertEq([...aaaaa].join(''), 'aaaaa')
|
||||
})
|
||||
]
|
||||
|
116
tsconfig.json
116
tsconfig.json
|
@ -1,116 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "libReplacement": true, /* Enable lib replacement. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./typings", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
"declarationDir": "./typings", /* Specify the output directory for generated declaration files. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
16
typings/byte.d.ts
vendored
16
typings/byte.d.ts
vendored
|
@ -1,16 +0,0 @@
|
|||
/** @import { ParserState } from './state.js' */
|
||||
export const char: any;
|
||||
export const tag: any;
|
||||
export const charNoCase: any;
|
||||
export const tagNoCase: any;
|
||||
export function anyChar(state: any): any;
|
||||
export const take: any;
|
||||
export const oneOf: any;
|
||||
export const noneOf: any;
|
||||
export const digit: any;
|
||||
export const lowerAlpha: any;
|
||||
export const upperAlpha: any;
|
||||
export const alpha: (state: ParserState) => any;
|
||||
export const alphanumeric: (state: ParserState) => any;
|
||||
export function eof(state: any): any;
|
||||
import type { ParserState } from './state.js';
|
2
typings/clone.d.ts
vendored
2
typings/clone.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
export const Clone: unique symbol;
|
||||
export function clone(target: any): any;
|
14
typings/combinator.d.ts
vendored
14
typings/combinator.d.ts
vendored
|
@ -1,14 +0,0 @@
|
|||
export function seq(...parsers: Parser[]): (
|
||||
/** @param {ParserState} state */
|
||||
state: ParserState) => any;
|
||||
export function any(...parsers: any[]): (
|
||||
/**
|
||||
* @param {ParserState} state
|
||||
*/
|
||||
state: ParserState) => any;
|
||||
export const map: any;
|
||||
export const delimited: any;
|
||||
export const preceded: any;
|
||||
export const terminated: any;
|
||||
export const separatedPair: any;
|
||||
import type { ParserState } from './state.js';
|
7
typings/cond.d.ts
vendored
7
typings/cond.d.ts
vendored
|
@ -1,7 +0,0 @@
|
|||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||
/** @import { ParserState } from './state.js' */
|
||||
export const maybe: any;
|
||||
export const not: any;
|
||||
export const until: any;
|
||||
export const verify: any;
|
||||
export const skip: any;
|
5
typings/const.d.ts
vendored
5
typings/const.d.ts
vendored
|
@ -1,5 +0,0 @@
|
|||
export const LowerAlpha: "abcdefghijklmnopqrstuvwxyz";
|
||||
export const UpperAlpha: string;
|
||||
export const Alpha: string;
|
||||
export const Digits: "1234567890";
|
||||
export const Alphanumeric: string;
|
11
typings/fn.d.ts
vendored
11
typings/fn.d.ts
vendored
|
@ -1,11 +0,0 @@
|
|||
export function iter(value: any): IndexableIterator;
|
||||
export function clone(state: ParserState): any;
|
||||
export function succeed<T>(v: T | T[], [x, y]: ParserState): any;
|
||||
export function fail(msg: string, state: ParserState, e?: Error): any;
|
||||
export function nth<T>(n: number & keyof T, iter: T[]): T[][number & keyof T];
|
||||
export function next(state: ParserState): any;
|
||||
export function diff<T>(a: T[], b: T[]): T[];
|
||||
export const join: any;
|
||||
export const mapStr: any;
|
||||
import { IndexableIterator } from './iter.js';
|
||||
import type { ParserState } from './state.js';
|
5
typings/index.d.ts
vendored
5
typings/index.d.ts
vendored
|
@ -1,5 +0,0 @@
|
|||
export * from "./byte.js";
|
||||
export * from "./combinator.js";
|
||||
export * from "./cond.js";
|
||||
export * from "./multi.js";
|
||||
export * from "./state.js";
|
14
typings/iter.d.ts
vendored
14
typings/iter.d.ts
vendored
|
@ -1,14 +0,0 @@
|
|||
export class IndexableIterator {
|
||||
constructor(value: any, index?: number);
|
||||
_value: any;
|
||||
_index: number;
|
||||
clone(): IndexableIterator;
|
||||
done(): boolean;
|
||||
next(): {
|
||||
value: any;
|
||||
done: boolean;
|
||||
};
|
||||
[Clone](): IndexableIterator;
|
||||
[Symbol.iterator](): IndexableIterator;
|
||||
}
|
||||
import { Clone } from './clone.js';
|
8
typings/multi.d.ts
vendored
8
typings/multi.d.ts
vendored
|
@ -1,8 +0,0 @@
|
|||
/** @import { ParseError, ParserState } from './state.js' */
|
||||
/** @import { Result } from '../vendor/kojima/src/index.js' */
|
||||
export const takeWhile: any;
|
||||
export const takeUntil: any;
|
||||
export const skip: any;
|
||||
export const many: any;
|
||||
export function many1(parser: any): any;
|
||||
export function range(start: any, end: any, deny?: any[]): any;
|
24
typings/state.d.ts
vendored
24
typings/state.d.ts
vendored
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
* @typedef {Readonly<[any[], Iterator<any>]>} ParserState
|
||||
*/
|
||||
export class ParseError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {ParserState} state
|
||||
* @param {Error} [cause]
|
||||
*/
|
||||
constructor(message: string, state: ParserState, cause?: Error);
|
||||
state: readonly [any[], Iterator<any, any, any>];
|
||||
}
|
||||
export class State extends Tuple {
|
||||
static from(values: any): State;
|
||||
constructor(remaining: any, read?: any[]);
|
||||
next(): any;
|
||||
[Clone](): Readonly<State>;
|
||||
}
|
||||
export function state(remaining: any, read: any): State;
|
||||
export const parse: any;
|
||||
export const parseAll: any;
|
||||
export type ParserState = Readonly<[any[], Iterator<any>]>;
|
||||
import { Tuple } from './tuple.js';
|
||||
import { Clone } from './clone.js';
|
18
typings/tuple.d.ts
vendored
18
typings/tuple.d.ts
vendored
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* @param {...any} values
|
||||
*/
|
||||
export class Tuple {
|
||||
static from(values: any): Readonly<Tuple>;
|
||||
constructor(values: any);
|
||||
_values: any;
|
||||
_length: any;
|
||||
get length(): any;
|
||||
_setEntry([key, value]: [any, any], index: any): void;
|
||||
toString(): string;
|
||||
clone(): any;
|
||||
[Clone](): any;
|
||||
[Symbol.iterator](): IndexableIterator;
|
||||
}
|
||||
export function tuple(values: any): Readonly<Tuple>;
|
||||
import { Clone } from './clone.js';
|
||||
import { IndexableIterator } from './iter.js';
|
Loading…
Add table
Reference in a new issue