From 770c3023424569a6baef13ca61b5647e5a42ee56 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 18 Aug 2023 10:57:30 -0700 Subject: [PATCH 01/21] feat: Precisely reflect the type of an implementation at runtime (!!) --- README.md | 4 + package.json5 | 2 + pnpm-lock.yaml | 163 ++++++++++++++++++++++++++++++++++++++ src/generic/arithmetic.ts | 17 +++- src/index.ts | 3 + tsconfig.json | 5 +- 6 files changed, 192 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d626b30..2965280 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,7 @@ Convenience scripts: * `pnpm build` -- compile the package * `pnpm exec` -- run the compiled code produced by `pnpm build` * `pnpm go` -- both of the above in sequence. + +Important installation note: + +after `pnpm install`, you must execute `npx ts-patch install` to activate the ts-macros compiler plugin. diff --git a/package.json5 b/package.json5 index f233b62..ae60d58 100644 --- a/package.json5 +++ b/package.json5 @@ -24,6 +24,8 @@ devDependencies: { '@types/node': '^20.5.0', 'source-map': '^0.7.4', + 'ts-macros': '^2.3.1', + 'ts-patch': '^3.0.2', typescript: '^5.1.6', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 682e112..60e7ed0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,12 @@ devDependencies: source-map: specifier: ^0.7.4 version: 0.7.4 + ts-macros: + specifier: ^2.3.1 + version: 2.3.1(typescript@5.1.6) + ts-patch: + specifier: ^3.0.2 + version: 3.0.2 typescript: specifier: ^5.1.6 version: 5.1.6 @@ -21,13 +27,170 @@ packages: resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} dev: true + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} dev: true + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /ts-macros@2.3.1(typescript@5.1.6): + resolution: {integrity: sha512-XArcrL7ViJRirjeSzw79ptovc7oscA3s9IpgrmRfqEONm1822jdFgplmaYuQ60wqfj+RrnR9cgIhNPIhevJTdg==} + peerDependencies: + typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x + dependencies: + typescript: 5.1.6 + dev: true + + /ts-patch@3.0.2: + resolution: {integrity: sha512-iTg8euqiNsNM1VDfOsVIsP0bM4kAVXU38n7TGQSkky7YQX/syh6sDPIRkvSS0HjT8ZOr0pq1h+5Le6jdB3hiJQ==} + hasBin: true + dependencies: + chalk: 4.1.2 + global-prefix: 3.0.0 + minimist: 1.2.8 + resolve: 1.22.4 + semver: 7.5.4 + strip-ansi: 6.0.1 + dev: true + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} hasBin: true dev: true + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 6f4e949..afe4aec 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,7 +1,22 @@ import type {Dependencies, Signature} from '../interfaces/type.js' +import {$$typeToString} from 'ts-macros' export const square = (dep: Dependencies<'multiply', T>): Signature<'square', T> => z => dep.multiply(z, z) // z => dep.fooBar(z, z) // fails as desired - // z => dep.multiply(z, 'foo') // fails as desired + // z => dep.multiply(z, 'foo') // still fails as desired + +// make sure ts-macros is running +function $contains(value: T, ...possible: Array) { + // repetition which goes over all the elements in the "possible" array + return +["||", [possible], (item: T) => value === item]; +} + +const searchItem = "needle"; +console.log("In generic arithmetic") +console.log($contains!(searchItem, "erwin", "tj")); +// Transpiles to: console.log(false); + +// OK, record the type of square: +square.reflectedType = $$typeToString!(); diff --git a/src/index.ts b/src/index.ts index 297b271..81ad66d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,3 +18,6 @@ const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) const typeTest: typeof myabs = 7 // check myabs is just a number console.log('Result is', myabs) + +// Check type of the generic square implementation +console.log('Type of square is', Specifications.generic.square.reflectedType) diff --git a/tsconfig.json b/tsconfig.json index 13b7b6c..a5c4a3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,9 @@ "target": "ES2022", "rootDir": "./src", "outDir": "./build", - "moduleResolution": "nodenext" + "moduleResolution": "nodenext", + "plugins": [ + {"transform": "ts-macros" } + ] } } From da5b2c346768acf6f0d38e6272f3451297abfca2 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 18 Aug 2023 15:00:21 -0700 Subject: [PATCH 02/21] feat: Demonstrate a trick for further resolving the dep type at compiletime --- src/generic/arithmetic.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index afe4aec..57bacad 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -20,3 +20,8 @@ console.log($contains!(searchItem, "erwin", "tj")); // OK, record the type of square: square.reflectedType = $$typeToString!(); + +type Expand = T extends unknown ? { [K in keyof T]: T[K] } : never; +type Out = Expand>; + +console.log("Deps type is", $$typeToString!()) From af02f1cb29e85bb6eae53c63f59f691b8b4ce09b Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 18 Aug 2023 20:29:24 -0700 Subject: [PATCH 03/21] feat: Simplify the reflected type of an implementation --- src/generic/arithmetic.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 57bacad..5e6fc7e 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -21,7 +21,21 @@ console.log($contains!(searchItem, "erwin", "tj")); // OK, record the type of square: square.reflectedType = $$typeToString!(); -type Expand = T extends unknown ? { [K in keyof T]: T[K] } : never; -type Out = Expand>; +type ShallowExpand = T extends unknown ? { [K in keyof T]: T[K] } : never; +type ExpandedParameters any> = {[Index in keyof Parameters]: ShallowExpand[Index]>} +type ExpandTwice = {[Index in keyof T] : ShallowExpand} +type AsArray = T extends any[] ? T : []; +type DeepExpand = + T extends (...args: any) => any ? (...pars: AsArray>>) => DeepExpand> : + T extends unknown ? { [K in keyof T]: DeepExpand } : never; +type Out = ShallowExpand>; -console.log("Deps type is", $$typeToString!()) +console.log("Deps type is", $$typeToString!<() => Out>()) + +type OutB = ExpandTwice>; + +console.log("Or perhaps", $$typeToString!()) + +console.log("Or maybe even", $$typeToString!< + (...args: DeepExpand>>) => DeepExpand>> + >()) From 9eff2bc265450048fe0d835f6322fe08f401af3b Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 20 Aug 2023 10:28:39 -0700 Subject: [PATCH 04/21] feat: Demonstrate macros to encapsulate the type-reflection process --- src/generic/arithmetic.ts | 64 +++++++++++++++++++++++++++++++++++++-- src/index.ts | 14 +++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 5e6fc7e..a61946f 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,5 +1,6 @@ import type {Dependencies, Signature} from '../interfaces/type.js' -import {$$typeToString} from 'ts-macros' +import {$$typeToString, $$ts, $$define, $$raw, $$ident, $$escape} from 'ts-macros' +import * as ts from 'typescript' export const square = (dep: Dependencies<'multiply', T>): Signature<'square', T> => @@ -19,15 +20,23 @@ console.log($contains!(searchItem, "erwin", "tj")); // Transpiles to: console.log(false); // OK, record the type of square: -square.reflectedType = $$typeToString!(); +square.reflectedType = //$$typeToString!(); + $$typeToString!< + (...args: DeepExpand>>) + => DeepExpand>> + >() type ShallowExpand = T extends unknown ? { [K in keyof T]: T[K] } : never; +// Short alias of ShallowExpand for convenience below: +type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; type ExpandedParameters any> = {[Index in keyof Parameters]: ShallowExpand[Index]>} type ExpandTwice = {[Index in keyof T] : ShallowExpand} type AsArray = T extends any[] ? T : []; type DeepExpand = - T extends (...args: any) => any ? (...pars: AsArray>>) => DeepExpand> : + T extends (...args: any) => any + ? (...pars: AsArray>>) => DeepExpand> : T extends unknown ? { [K in keyof T]: DeepExpand } : never; + type Out = ShallowExpand>; console.log("Deps type is", $$typeToString!<() => Out>()) @@ -39,3 +48,52 @@ console.log("Or perhaps", $$typeToString!()) console.log("Or maybe even", $$typeToString!< (...args: DeepExpand>>) => DeepExpand>> >()) + +// Now try to wrap it up in a macro + +// From the creator of ts-macros; temporary until next release +function $export(name: string, value: unknown) { + $$raw!((ctx, name: ts.Expression, value: ts.Expression) => { + if (!ctx.ts.isStringLiteral(name)) throw ctx.error(name, "Expected a string literal."); + return [ctx.factory.createVariableStatement([ctx.factory.createToken(ctx.ts.SyntaxKind.ExportKeyword)], ctx.factory.createVariableDeclarationList([ + ctx.factory.createVariableDeclaration(name.text, undefined, undefined, value) + ], ctx.ts.NodeFlags.Const))]; + }); +} + +// Obviously we'd prefer the name before the expression, but that won't work +// until the next release +function $exportImpl(expr: Impl, name: string) { + $export!(name, expr); + $$ident!(name).reflectedType = $$typeToString!< + // (...args: DeepExpand>>) => DeepExpand>> // see comment in reflect below. + Impl + >(); +} + +// works but then not visible at import with current ts-macros. +// Author says he will be enhancing this "soon." +$exportImpl!((dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z), + 'squire') + +function $reflect(expr: Impl) { + return $$escape!(() => { + const temp: Impl & {reflectedType?: string} = expr; + temp.reflectedType = $$typeToString!< + // (...args: DeepExpand>>) => + // DeepExpand>> // error; can't instantiate + // Impl; even if we constrain it to be the type of a generic function, + // that's still not a generic _type_, ugh. + Impl>(); + return temp; + }) as Impl & {reflectedType: string} +} + +export const squre = $reflect!( + (dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z)) + +export const sqre = $reflect!( + (dep: Deps>): Signature<'square', T> => + z => dep.multiply(z, z)) diff --git a/src/index.ts b/src/index.ts index 81ad66d..79791d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,3 +21,17 @@ console.log('Result is', myabs) // Check type of the generic square implementation console.log('Type of square is', Specifications.generic.square.reflectedType) + +// Now check the ones that came via macros: + +// Auto-generated export is invisible to TypeScript at the moment, author +// says he will fix: +console.log('Type of squire (auto-exported) is', + // @ts-ignore + Specifications.generic.squire.reflectedType) + +// Via a macro wrapper around the definition, two ways: +console.log('Type of squre (unexpanded) is', + Specifications.generic.squre.reflectedType) +console.log('Type of sqre (expanded) is', + Specifications.generic.sqre.reflectedType) From ae2303af7cabc248f48dc50128e0ff1342468674 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 26 Aug 2023 10:14:02 -0700 Subject: [PATCH 05/21] chore: Update to ts-macros@2.4.0 --- package.json5 | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json5 b/package.json5 index ae60d58..8f26409 100644 --- a/package.json5 +++ b/package.json5 @@ -24,7 +24,7 @@ devDependencies: { '@types/node': '^20.5.0', 'source-map': '^0.7.4', - 'ts-macros': '^2.3.1', + 'ts-macros': '^2.4.0', 'ts-patch': '^3.0.2', typescript: '^5.1.6', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60e7ed0..e828fc9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ devDependencies: specifier: ^0.7.4 version: 0.7.4 ts-macros: - specifier: ^2.3.1 - version: 2.3.1(typescript@5.1.6) + specifier: ^2.4.0 + version: 2.4.0(typescript@5.1.6) ts-patch: specifier: ^3.0.2 version: 3.0.2 @@ -158,8 +158,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-macros@2.3.1(typescript@5.1.6): - resolution: {integrity: sha512-XArcrL7ViJRirjeSzw79ptovc7oscA3s9IpgrmRfqEONm1822jdFgplmaYuQ60wqfj+RrnR9cgIhNPIhevJTdg==} + /ts-macros@2.4.0(typescript@5.1.6): + resolution: {integrity: sha512-HKt4/r1KvtnBKu+RLUPFB4BJk2L4VjN5SlHHJJQSGBq5ycGhYSpgGi6VvbJjYFQscCUXOvSQRyISYVC+FF7Svg==} peerDependencies: typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x dependencies: From dc6cf5165daa3c05a6348d9675f0551efab7e3ba Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 26 Aug 2023 12:19:32 -0700 Subject: [PATCH 06/21] issue: ts-macros appears to be generating temp variable clashes --- package.json5 | 4 ++-- src/generic/arithmetic.ts | 25 ++++++------------------- src/index.ts | 2 +- tsconfig.json | 7 ++++--- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/package.json5 b/package.json5 index 8f26409..9caaad9 100644 --- a/package.json5 +++ b/package.json5 @@ -6,8 +6,8 @@ scripts: { test: 'echo "Error: no test specified" && exit 1', build: 'tsc && cp etc/package.json build', - exec: 'node build', - go: 'pnpm --sequential "/build|exec/"', + start: 'node build', + go: 'pnpm --sequential "/build|start/"', }, packageManager: 'pnpm', keywords: [ diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index a61946f..720fe57 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,5 +1,5 @@ import type {Dependencies, Signature} from '../interfaces/type.js' -import {$$typeToString, $$ts, $$define, $$raw, $$ident, $$escape} from 'ts-macros' +import {$$typeToString, $$ident, $$escape, $$define} from 'ts-macros' import * as ts from 'typescript' export const square = @@ -50,21 +50,8 @@ console.log("Or maybe even", $$typeToString!< >()) // Now try to wrap it up in a macro - -// From the creator of ts-macros; temporary until next release -function $export(name: string, value: unknown) { - $$raw!((ctx, name: ts.Expression, value: ts.Expression) => { - if (!ctx.ts.isStringLiteral(name)) throw ctx.error(name, "Expected a string literal."); - return [ctx.factory.createVariableStatement([ctx.factory.createToken(ctx.ts.SyntaxKind.ExportKeyword)], ctx.factory.createVariableDeclarationList([ - ctx.factory.createVariableDeclaration(name.text, undefined, undefined, value) - ], ctx.ts.NodeFlags.Const))]; - }); -} - -// Obviously we'd prefer the name before the expression, but that won't work -// until the next release -function $exportImpl(expr: Impl, name: string) { - $export!(name, expr); +function $exportImpl(name: string, expr: Impl) { + $$define!(name, expr, false, true); // Final `true` is export $$ident!(name).reflectedType = $$typeToString!< // (...args: DeepExpand>>) => DeepExpand>> // see comment in reflect below. Impl @@ -73,9 +60,9 @@ function $exportImpl(expr: Impl, name: string) { // works but then not visible at import with current ts-macros. // Author says he will be enhancing this "soon." -$exportImpl!((dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z), - 'squire') +$exportImpl!('squire', + (dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z)) function $reflect(expr: Impl) { return $$escape!(() => { diff --git a/src/index.ts b/src/index.ts index 79791d0..a3a5953 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,7 @@ console.log('Type of square is', Specifications.generic.square.reflectedType) // Auto-generated export is invisible to TypeScript at the moment, author // says he will fix: console.log('Type of squire (auto-exported) is', - // @ts-ignore + // not ts-ignore Specifications.generic.squire.reflectedType) // Via a macro wrapper around the definition, two ways: diff --git a/tsconfig.json b/tsconfig.json index a5c4a3d..43f365a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,8 +4,9 @@ "rootDir": "./src", "outDir": "./build", "moduleResolution": "nodenext", - "plugins": [ - {"transform": "ts-macros" } - ] + "plugins": [ { + "transform": "ts-macros/dist/type-resolve", + "transformProgram": true + } ] } } From 0e2a1e830a799bfab948ab33a6fea85a3427fa75 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Mon, 18 Sep 2023 10:14:11 -0700 Subject: [PATCH 07/21] WIP: try to use ts-macros for more implementations --- package.json5 | 7 ++-- pnpm-lock.yaml | 16 ++++---- src/Complex/arithmetic.ts | 5 ++- src/generic/arithmetic.ts | 85 ++------------------------------------- src/index.ts | 15 +------ src/interfaces/type.ts | 11 ++++- tsconfig.json | 2 +- 7 files changed, 30 insertions(+), 111 deletions(-) diff --git a/package.json5 b/package.json5 index 9caaad9..ad38308 100644 --- a/package.json5 +++ b/package.json5 @@ -5,9 +5,10 @@ main: 'index.ts', scripts: { test: 'echo "Error: no test specified" && exit 1', - build: 'tsc && cp etc/package.json build', + build: 'mkdir -p build && cp etc/package.json build && tsc', start: 'node build', go: 'pnpm --sequential "/build|start/"', + clean: 'rm -r build', }, packageManager: 'pnpm', keywords: [ @@ -22,9 +23,9 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - '@types/node': '^20.5.0', + '@types/node': '^20.6.2', 'source-map': '^0.7.4', - 'ts-macros': '^2.4.0', + 'ts-macros': '^2.4.1', 'ts-patch': '^3.0.2', typescript: '^5.1.6', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e828fc9..ab1dcc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,14 +6,14 @@ settings: devDependencies: '@types/node': - specifier: ^20.5.0 - version: 20.5.0 + specifier: ^20.6.2 + version: 20.6.2 source-map: specifier: ^0.7.4 version: 0.7.4 ts-macros: - specifier: ^2.4.0 - version: 2.4.0(typescript@5.1.6) + specifier: ^2.4.1 + version: 2.4.1(typescript@5.1.6) ts-patch: specifier: ^3.0.2 version: 3.0.2 @@ -23,8 +23,8 @@ devDependencies: packages: - /@types/node@20.5.0: - resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} + /@types/node@20.6.2: + resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==} dev: true /ansi-regex@5.0.1: @@ -158,8 +158,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-macros@2.4.0(typescript@5.1.6): - resolution: {integrity: sha512-HKt4/r1KvtnBKu+RLUPFB4BJk2L4VjN5SlHHJJQSGBq5ycGhYSpgGi6VvbJjYFQscCUXOvSQRyISYVC+FF7Svg==} + /ts-macros@2.4.1(typescript@5.1.6): + resolution: {integrity: sha512-DmrftFZ5pgM7dw8ySYcd90wGEfjp7yx9MFw/YyJHNdwKYdUb//lcy/XI/Lvg3LbrGIbjBMEj1rzuc5Kfkzvafg==} peerDependencies: typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x dependencies: diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 90c8eab..501d09f 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -2,6 +2,7 @@ import {Complex} from './type.js' import type { Dependencies, Signature, Returns, RealType, AliasOf } from '../interfaces/type.js' +import {$implement} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -73,7 +74,7 @@ export const divide = // and we have to get it straight which operations we need on each type, and // in fact, we need `addReal` on both T and Complex, hence the dependency // with a custom name, not generated via Dependencies<...> -export const sqrt = +$implement!('sqrt', (dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType> & Dependencies<'zero' | 'complex', T> & Dependencies<'absquare' | 're' | 'divideReal', Complex> @@ -96,6 +97,6 @@ export const sqrt = const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r)) const denom = dep.conservativeSqrt(denomsq) return dep.divideReal(num, denom) - } + }) export const conservativeSqrt = sqrt diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 720fe57..3903b8d 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,86 +1,7 @@ import type {Dependencies, Signature} from '../interfaces/type.js' -import {$$typeToString, $$ident, $$escape, $$define} from 'ts-macros' -import * as ts from 'typescript' +import {$implement} from '../interfaces/type.js' -export const square = - (dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z) +$implement!('square', + (dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z)) // z => dep.fooBar(z, z) // fails as desired // z => dep.multiply(z, 'foo') // still fails as desired - -// make sure ts-macros is running -function $contains(value: T, ...possible: Array) { - // repetition which goes over all the elements in the "possible" array - return +["||", [possible], (item: T) => value === item]; -} - -const searchItem = "needle"; -console.log("In generic arithmetic") -console.log($contains!(searchItem, "erwin", "tj")); -// Transpiles to: console.log(false); - -// OK, record the type of square: -square.reflectedType = //$$typeToString!(); - $$typeToString!< - (...args: DeepExpand>>) - => DeepExpand>> - >() - -type ShallowExpand = T extends unknown ? { [K in keyof T]: T[K] } : never; -// Short alias of ShallowExpand for convenience below: -type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; -type ExpandedParameters any> = {[Index in keyof Parameters]: ShallowExpand[Index]>} -type ExpandTwice = {[Index in keyof T] : ShallowExpand} -type AsArray = T extends any[] ? T : []; -type DeepExpand = - T extends (...args: any) => any - ? (...pars: AsArray>>) => DeepExpand> : - T extends unknown ? { [K in keyof T]: DeepExpand } : never; - -type Out = ShallowExpand>; - -console.log("Deps type is", $$typeToString!<() => Out>()) - -type OutB = ExpandTwice>; - -console.log("Or perhaps", $$typeToString!()) - -console.log("Or maybe even", $$typeToString!< - (...args: DeepExpand>>) => DeepExpand>> - >()) - -// Now try to wrap it up in a macro -function $exportImpl(name: string, expr: Impl) { - $$define!(name, expr, false, true); // Final `true` is export - $$ident!(name).reflectedType = $$typeToString!< - // (...args: DeepExpand>>) => DeepExpand>> // see comment in reflect below. - Impl - >(); -} - -// works but then not visible at import with current ts-macros. -// Author says he will be enhancing this "soon." -$exportImpl!('squire', - (dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z)) - -function $reflect(expr: Impl) { - return $$escape!(() => { - const temp: Impl & {reflectedType?: string} = expr; - temp.reflectedType = $$typeToString!< - // (...args: DeepExpand>>) => - // DeepExpand>> // error; can't instantiate - // Impl; even if we constrain it to be the type of a generic function, - // that's still not a generic _type_, ugh. - Impl>(); - return temp; - }) as Impl & {reflectedType: string} -} - -export const squre = $reflect!( - (dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z)) - -export const sqre = $reflect!( - (dep: Deps>): Signature<'square', T> => - z => dep.multiply(z, z)) diff --git a/src/index.ts b/src/index.ts index a3a5953..4419dee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,17 +21,4 @@ console.log('Result is', myabs) // Check type of the generic square implementation console.log('Type of square is', Specifications.generic.square.reflectedType) - -// Now check the ones that came via macros: - -// Auto-generated export is invisible to TypeScript at the moment, author -// says he will fix: -console.log('Type of squire (auto-exported) is', - // not ts-ignore - Specifications.generic.squire.reflectedType) - -// Via a macro wrapper around the definition, two ways: -console.log('Type of squre (unexpanded) is', - Specifications.generic.squre.reflectedType) -console.log('Type of sqre (expanded) is', - Specifications.generic.sqre.reflectedType) +console.log('Type of complex square root is', Specifications.Complex.sqrt.reflectedType) diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 9fb1163..3728f11 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -1,3 +1,5 @@ +import {$$typeToString, $$ident, $$define} from 'ts-macros' + /***** * Every typocomath type has some associated types; they need * to be published in the following interface. The key is the @@ -74,6 +76,13 @@ type SignatureKey = keyof Signatures export type Signature, T> = Signatures[Name] export type Returns, T> = ReturnType[Name]> -export type Dependencies, T> = {[K in Name]: Signature} +type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; +export type Dependencies, T> = Deps<{[K in Name]: Signature}> export type AliasOf = T & {aliasOf?: Name} + +// For defining implementations with type reflection +export function $implement(name: string, expr: Impl) { + $$define!(name, expr, false, true); // Final `true` is export + $$ident!(name).reflectedType = $$typeToString!(); +} diff --git a/tsconfig.json b/tsconfig.json index 43f365a..63c0b04 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2022", + "target": "esnext", "rootDir": "./src", "outDir": "./build", "moduleResolution": "nodenext", From 4947a80cb4c90b8224070839cfbce216bd203d6b Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 14:11:10 +0200 Subject: [PATCH 08/21] chore: make `pnpm go` work cross platform --- package.json5 | 7 +- pnpm-lock.yaml | 711 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 716 insertions(+), 2 deletions(-) diff --git a/package.json5 b/package.json5 index ad38308..cd006e8 100644 --- a/package.json5 +++ b/package.json5 @@ -5,10 +5,10 @@ main: 'index.ts', scripts: { test: 'echo "Error: no test specified" && exit 1', - build: 'mkdir -p build && cp etc/package.json build && tsc', + build: 'mkdirp build && cpy etc/package.json build --flat && tsc', start: 'node build', go: 'pnpm --sequential "/build|start/"', - clean: 'rm -r build', + clean: 'del-cli build', }, packageManager: 'pnpm', keywords: [ @@ -24,6 +24,9 @@ }, devDependencies: { '@types/node': '^20.6.2', + 'cpy-cli': '5.0.0', + 'del-cli': '5.1.0', + mkdirp: '3.0.1', 'source-map': '^0.7.4', 'ts-macros': '^2.4.1', 'ts-patch': '^3.0.2', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab1dcc1..afb4e0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,15 @@ devDependencies: '@types/node': specifier: ^20.6.2 version: 20.6.2 + cpy-cli: + specifier: 5.0.0 + version: 5.0.0 + del-cli: + specifier: 5.1.0 + version: 5.1.0 + mkdirp: + specifier: 3.0.1 + version: 3.0.1 source-map: specifier: ^0.7.4 version: 0.7.4 @@ -23,15 +32,81 @@ devDependencies: packages: + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@types/minimist@1.2.2: + resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + dev: true + /@types/node@20.6.2: resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==} dev: true + /@types/normalize-package-data@2.4.1: + resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + dev: true + + /aggregate-error@4.0.1: + resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} + engines: {node: '>=12'} + dependencies: + clean-stack: 4.2.0 + indent-string: 5.0.0 + dev: true + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -39,6 +114,58 @@ packages: color-convert: 2.0.1 dev: true + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + + /arrify@3.0.0: + resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} + engines: {node: '>=12'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /camelcase-keys@7.0.2: + resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} + engines: {node: '>=12'} + dependencies: + camelcase: 6.3.0 + map-obj: 4.3.0 + quick-lru: 5.1.1 + type-fest: 1.4.0 + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -47,6 +174,19 @@ packages: supports-color: 7.2.0 dev: true + /clean-stack@4.2.0: + resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} + engines: {node: '>=12'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -54,14 +194,172 @@ packages: color-name: 1.1.4 dev: true + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cp-file@10.0.0: + resolution: {integrity: sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==} + engines: {node: '>=14.16'} + dependencies: + graceful-fs: 4.2.11 + nested-error-stacks: 2.1.1 + p-event: 5.0.1 + dev: true + + /cpy-cli@5.0.0: + resolution: {integrity: sha512-fb+DZYbL9KHc0BC4NYqGRrDIJZPXUmjjtqdw4XRRg8iV8dIfghUX/WiL+q4/B/KFTy3sK6jsbUhBaz0/Hxg7IQ==} + engines: {node: '>=16'} + hasBin: true + dependencies: + cpy: 10.1.0 + meow: 12.1.1 + dev: true + + /cpy@10.1.0: + resolution: {integrity: sha512-VC2Gs20JcTyeQob6UViBLnyP0bYHkBh6EiKzot9vi2DmeGlFT9Wd7VG3NBrkNx/jYvFBeyDOMMHdHQhbtKLgHQ==} + engines: {node: '>=16'} + dependencies: + arrify: 3.0.0 + cp-file: 10.0.0 + globby: 13.2.2 + junk: 4.0.1 + micromatch: 4.0.5 + nested-error-stacks: 2.1.1 + p-filter: 3.0.0 + p-map: 6.0.0 + dev: true + + /decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /decamelize@5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + dev: true + + /del-cli@5.1.0: + resolution: {integrity: sha512-xwMeh2acluWeccsfzE7VLsG3yTr7nWikbfw+xhMnpRrF15pGSkw+3/vJZWlGoE4I86UiLRNHicmKt4tkIX9Jtg==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + del: 7.1.0 + meow: 10.1.5 + dev: true + + /del@7.1.0: + resolution: {integrity: sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==} + engines: {node: '>=14.16'} + dependencies: + globby: 13.2.2 + graceful-fs: 4.2.11 + is-glob: 4.0.3 + is-path-cwd: 3.0.0 + is-path-inside: 4.0.0 + p-map: 5.5.0 + rimraf: 3.0.2 + slash: 4.0.0 + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /global-prefix@3.0.0: resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} engines: {node: '>=6'} @@ -71,6 +369,31 @@ packages: which: 1.3.1 dev: true + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 4.0.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -83,25 +406,113 @@ packages: function-bind: 1.1.1 dev: true + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-cwd@3.0.0: + resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + dev: true + + /is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /junk@4.0.1: + resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} + engines: {node: '>=12.20'} + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} dev: true + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -109,14 +520,217 @@ packages: yallist: 4.0.0 dev: true + /map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + + /meow@10.1.5: + resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + '@types/minimist': 1.2.2 + camelcase-keys: 7.0.2 + decamelize: 5.0.1 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 8.0.0 + redent: 4.0.0 + trim-newlines: 4.1.1 + type-fest: 1.4.0 + yargs-parser: 20.2.9 + dev: true + + /meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true + /mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /nested-error-stacks@2.1.1: + resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} + dev: true + + /normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.13.0 + semver: 7.5.4 + validate-npm-package-license: 3.0.4 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /p-event@5.0.1: + resolution: {integrity: sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-timeout: 5.1.0 + dev: true + + /p-filter@3.0.0: + resolution: {integrity: sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-map: 5.5.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-map@5.5.0: + resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} + engines: {node: '>=12'} + dependencies: + aggregate-error: 4.0.1 + dev: true + + /p-map@6.0.0: + resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} + engines: {node: '>=16'} + dev: true + + /p-timeout@5.1.0: + resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} + engines: {node: '>=12'} + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /read-pkg-up@8.0.0: + resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} + engines: {node: '>=12'} + dependencies: + find-up: 5.0.0 + read-pkg: 6.0.0 + type-fest: 1.4.0 + dev: true + + /read-pkg@6.0.0: + resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} + engines: {node: '>=12'} + dependencies: + '@types/normalize-package-data': 2.4.1 + normalize-package-data: 3.0.3 + parse-json: 5.2.0 + type-fest: 1.4.0 + dev: true + + /redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + dependencies: + indent-string: 5.0.0 + strip-indent: 4.0.0 + dev: true + /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -126,6 +740,24 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -134,11 +766,38 @@ packages: lru-cache: 6.0.0 dev: true + /slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: true + /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} dev: true + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.15 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.15 + dev: true + + /spdx-license-ids@3.0.15: + resolution: {integrity: sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==} + dev: true + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -146,6 +805,20 @@ packages: ansi-regex: 5.0.1 dev: true + /strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + dependencies: + min-indent: 1.0.1 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -158,6 +831,18 @@ packages: engines: {node: '>= 0.4'} dev: true + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /trim-newlines@4.1.1: + resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} + engines: {node: '>=12'} + dev: true + /ts-macros@2.4.1(typescript@5.1.6): resolution: {integrity: sha512-DmrftFZ5pgM7dw8ySYcd90wGEfjp7yx9MFw/YyJHNdwKYdUb//lcy/XI/Lvg3LbrGIbjBMEj1rzuc5Kfkzvafg==} peerDependencies: @@ -178,12 +863,24 @@ packages: strip-ansi: 6.0.1 dev: true + /type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: true + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} hasBin: true dev: true + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -191,6 +888,20 @@ packages: isexe: 2.0.0 dev: true + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true From ce974e2a99b8f60b96b1a7e29cbb847f7992c840 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 14:24:52 +0200 Subject: [PATCH 09/21] log the reflectedType of complex square root --- src/Complex/arithmetic.ts | 4 +--- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 501d09f..5776cd6 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -79,7 +79,7 @@ $implement!('sqrt', & Dependencies<'zero' | 'complex', T> & Dependencies<'absquare' | 're' | 'divideReal', Complex> & { - addTR: Signature<'addReal', T>, + addTR: Signature<'addReal', T>, addRR: Signature<'add', RealType>, addCR: Signature<'addReal', Complex> }): @@ -98,5 +98,3 @@ $implement!('sqrt', const denom = dep.conservativeSqrt(denomsq) return dep.divideReal(num, denom) }) - -export const conservativeSqrt = sqrt diff --git a/src/index.ts b/src/index.ts index 4419dee..8b4ecfd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,4 +21,4 @@ console.log('Result is', myabs) // Check type of the generic square implementation console.log('Type of square is', Specifications.generic.square.reflectedType) -console.log('Type of complex square root is', Specifications.Complex.sqrt.reflectedType) +console.log('Type of complex square root is', Specifications.complex.sqrt.reflectedType) From 20078d2c879bcbaa495eb60ec734665171b10433 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 14:58:51 +0200 Subject: [PATCH 10/21] use ts-macros directly from Github until v2.4.2 is published on npm --- package.json5 | 2 +- pnpm-lock.yaml | 23 +++++++++++++---------- src/interfaces/type.ts | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/package.json5 b/package.json5 index cd006e8..a0cd6c3 100644 --- a/package.json5 +++ b/package.json5 @@ -28,7 +28,7 @@ 'del-cli': '5.1.0', mkdirp: '3.0.1', 'source-map': '^0.7.4', - 'ts-macros': '^2.4.1', + 'ts-macros': 'github:GoogleFeud/ts-macros', 'ts-patch': '^3.0.2', typescript: '^5.1.6', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afb4e0a..8c865d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ devDependencies: specifier: ^0.7.4 version: 0.7.4 ts-macros: - specifier: ^2.4.1 - version: 2.4.1(typescript@5.1.6) + specifier: github:GoogleFeud/ts-macros + version: github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15(typescript@5.1.6) ts-patch: specifier: ^3.0.2 version: 3.0.2 @@ -843,14 +843,6 @@ packages: engines: {node: '>=12'} dev: true - /ts-macros@2.4.1(typescript@5.1.6): - resolution: {integrity: sha512-DmrftFZ5pgM7dw8ySYcd90wGEfjp7yx9MFw/YyJHNdwKYdUb//lcy/XI/Lvg3LbrGIbjBMEj1rzuc5Kfkzvafg==} - peerDependencies: - typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - dependencies: - typescript: 5.1.6 - dev: true - /ts-patch@3.0.2: resolution: {integrity: sha512-iTg8euqiNsNM1VDfOsVIsP0bM4kAVXU38n7TGQSkky7YQX/syh6sDPIRkvSS0HjT8ZOr0pq1h+5Le6jdB3hiJQ==} hasBin: true @@ -905,3 +897,14 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15(typescript@5.1.6): + resolution: {tarball: https://codeload.github.com/GoogleFeud/ts-macros/tar.gz/4f8c22db77e3b5840e3a2f285e30436f71c27e15} + id: github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15 + name: ts-macros + version: 2.4.2 + peerDependencies: + typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x + dependencies: + typescript: 5.1.6 + dev: true diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 3728f11..75d1b4c 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -84,5 +84,5 @@ export type AliasOf = T & {aliasOf?: Name} // For defining implementations with type reflection export function $implement(name: string, expr: Impl) { $$define!(name, expr, false, true); // Final `true` is export - $$ident!(name).reflectedType = $$typeToString!(); + $$ident!(name).reflectedType = $$typeToString!(true, false, true); } From 0ebaaf35eee8a67c97a442f52be17e777386278b Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 15:02:42 +0200 Subject: [PATCH 11/21] feat: convert sqrt for numbers --- package.json5 | 2 +- src/index.ts | 19 ++++++++++++++++--- src/numbers/arithmetic.ts | 22 ++++++++++++---------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/package.json5 b/package.json5 index a0cd6c3..b6575a1 100644 --- a/package.json5 +++ b/package.json5 @@ -7,7 +7,7 @@ test: 'echo "Error: no test specified" && exit 1', build: 'mkdirp build && cpy etc/package.json build --flat && tsc', start: 'node build', - go: 'pnpm --sequential "/build|start/"', + go: 'pnpm --sequential "/clean|build|start/"', clean: 'del-cli build', }, packageManager: 'pnpm', diff --git a/src/index.ts b/src/index.ts index 8b4ecfd..dc389e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,17 +8,30 @@ import {absquare as absquare_complex} from './Complex/arithmetic.js' const mockRealAdd = (a: number, b: number) => a+b const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im +const mockComplex = (re: number, im: number) => ({ re, im }) + +const config = { + predictable: false, + epsilon: 1e-14 +} const quatAbsquare = absquare_complex({ - add: mockRealAdd, - absquare: mockComplexAbsquare + add: mockRealAdd, + absquare: mockComplexAbsquare }) const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) const typeTest: typeof myabs = 7 // check myabs is just a number +console.log('Result is myabs=', myabs) -console.log('Result is', myabs) +const sqrt = Specifications.numbers.sqrt({ + config, + complex: mockComplex +}) +console.log('Result of sqrt(16)=', sqrt(16)) +console.log('Result of sqrt(-4)=', sqrt(-4)) // Check type of the generic square implementation +console.log('Type of sqrt (number) is', Specifications.numbers.sqrt.reflectedType) console.log('Type of square is', Specifications.generic.square.reflectedType) console.log('Type of complex square root is', Specifications.complex.sqrt.reflectedType) diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 11da5c2..d2d3414 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,5 +1,6 @@ import type {configDependency} from '../core/Config.js' import type {Dependencies, Signature} from '../interfaces/type.js' +import { $implement } from '../interfaces/type.js' export const add: Signature<'add', number> = (a, b) => a + b export const unaryMinus: Signature<'unaryMinus', number> = a => -a @@ -13,13 +14,14 @@ export const divide: Signature<'divide', number> = (a, b) => a / b const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a) export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt -export const sqrt = - (dep: configDependency & Dependencies<'complex', number>): - Signature<'sqrt', number> => { - if (dep.config.predictable || !dep.complex) return basicSqrt - return a => { - if (isNaN(a)) return NaN - if (a >= 0) return Math.sqrt(a) - return dep.complex(0, Math.sqrt(unaryMinus(a))) - } - } +$implement!('sqrt', + (dep: configDependency & Dependencies<'complex', number>): Signature<'sqrt', number> => { + if (dep.config.predictable || !dep.complex) { + return basicSqrt + } + return a => { + if (isNaN(a)) return NaN + if (a >= 0) return Math.sqrt(a) + return dep.complex(0, Math.sqrt(unaryMinus(a))) + } + }) From fd4ecd70ba3e612b67627567d6fe9515f333d3a6 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 15:04:18 +0200 Subject: [PATCH 12/21] chore: use exact version numbers --- package.json5 | 8 ++++---- pnpm-lock.yaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json5 b/package.json5 index b6575a1..97ced5b 100644 --- a/package.json5 +++ b/package.json5 @@ -23,13 +23,13 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - '@types/node': '^20.6.2', + '@types/node': '20.6.2', 'cpy-cli': '5.0.0', 'del-cli': '5.1.0', mkdirp: '3.0.1', - 'source-map': '^0.7.4', + 'source-map': '0.7.4', 'ts-macros': 'github:GoogleFeud/ts-macros', - 'ts-patch': '^3.0.2', - typescript: '^5.1.6', + 'ts-patch': '3.0.2', + typescript: '5.1.6', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c865d2..948d6ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,7 +6,7 @@ settings: devDependencies: '@types/node': - specifier: ^20.6.2 + specifier: 20.6.2 version: 20.6.2 cpy-cli: specifier: 5.0.0 @@ -18,16 +18,16 @@ devDependencies: specifier: 3.0.1 version: 3.0.1 source-map: - specifier: ^0.7.4 + specifier: 0.7.4 version: 0.7.4 ts-macros: specifier: github:GoogleFeud/ts-macros version: github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15(typescript@5.1.6) ts-patch: - specifier: ^3.0.2 + specifier: 3.0.2 version: 3.0.2 typescript: - specifier: ^5.1.6 + specifier: 5.1.6 version: 5.1.6 packages: From 1ca0ac42d0b26f5cecf76361efc6634a2c44b1cd Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 21:01:38 +0200 Subject: [PATCH 13/21] feate: implement a basic parser for the reflected types --- src/core/Dispatcher.ts | 8 +- src/core/parseReflectedType.ts | 232 +++++++++++++++++++++++++++++++++ src/index.ts | 26 +++- 3 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 src/core/parseReflectedType.ts diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index ec5491f..230a286 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -47,13 +47,19 @@ export class Dispatcher { // that's really possible, though. ) { console.log('Pretending to install', name, signature, '=>', returns) + // @ts-ignore + if (behavior.reflectedType) { + // @ts-ignore + console.log(' Reflected type:', behavior.reflectedType) + // TODO: parse the reflected type + } //TODO: implement me } installType(name: TypeName, typespec: TypeSpecification) { console.log('Pretending to install type', name, typespec) //TODO: implement me } - constructor(collection: SpecificationsGroup) { + constructor(collection: SpecificationsGroup) { const implementations = [] for (const key in collection) { console.log('Working on', key) diff --git a/src/core/parseReflectedType.ts b/src/core/parseReflectedType.ts new file mode 100644 index 0000000..18705fe --- /dev/null +++ b/src/core/parseReflectedType.ts @@ -0,0 +1,232 @@ +export interface FunctionDef { + name: string, + signatures: Array<{ + args: Array<{ name: string, type: string }> + // FIXME: remove undefined in the end + returns: string | undefined + }> +} + +export interface DependencyDef extends FunctionDef { + aliasOf: string | undefined +} + +/** + * Parse a reflected type coming out of TypeScript into a structured object, for example: + * + * '(dep: configDependency & { complex: ((re: number) => Complex) | ((re: number, im: number) => Complex); }) => (a: number) => number | Complex' + */ +export function parseReflectedType(name: string, reflectedType: string) : { fn: FunctionDef, dependencies: Record } { + const [factoryArgs, fnArgsBlock, returns] = split(reflectedType, '=>').map(trim) + const args = parseArgs(fnArgsBlock) + + const factoryArgsInner = findBlockContents(factoryArgs, '(', ')') + const depArg = split(factoryArgsInner.innerText, ':').map(trim)[1] + const depArgBlocks = split(depArg, '&').map(trim) + + const deps = depArgBlocks + .filter(depArgBlock => { + if (depArgBlock.startsWith('{')) { + return true + } else { + console.error(`ERROR: Cannot parse dependency "${depArgBlock}"`) + } + }) + .flatMap(parseDependencies) + + const dependencies: Record = groupBy(deps, 'name') + + return { + fn: { name, signatures: [{args, returns}] }, + dependencies + } + + function parseDependencies(deps: string) { + const inner = findBlockContents(deps, '{', '}').innerText + return split(inner, ';') + .map(trim) + .filter(notEmpty) + .map(parseDependency) + } + + // parse a dependency like "complex: ((re: number) => Complex) | ((re: number, im: number) => Complex)" + function parseDependency(dep: string) { + const [name, def] = split(dep, ':').map(trim) + + const { aliasOf, innerSignature } = parseAliasOf(def) + + const signatures = split(innerSignature, '|') + .map(trim) + .map(stripParenthesis) + .map(signature => { + const [argsBlock, returns] = split(signature, '=>').map(trim) + + if (!returns) { + console.warn(`ERROR: failed to find return type in '${signature}'`) + } + + return { + args: parseArgs(argsBlock), + returns + } + }) + + return { name, signatures, aliasOf } + } + + // parse args like "(re: number, im: number)" + function parseArgs(argsBlock: string) : Array<{name: string, type: string}> { + const args = findBlockContents(argsBlock, '(', ')').innerText + + return split(args, ',') + .map(trim) + .map(arg => { + const [name, type] = split(arg, ':').map(trim) + return { name, type} + }) + } + + // parse "AliasOf<"divide", (a: Complex, b: RealType>) => Complex>" + function parseAliasOf(signature: string) : { innerSignature: string, aliasOf: string | undefined } { + if (!signature.startsWith('AliasOf')) { + return { + innerSignature: signature, + aliasOf: undefined + } + } + + const inner = findBlockContents(signature, '<', '>').innerText.trim() + const [aliasOfWithQuotes, innerSignature] = split(inner, ',').map(trim) + return { + innerSignature, + aliasOf: aliasOfWithQuotes.substring(1, aliasOfWithQuotes.length - 1) // remove double quotes + } + } + + // remove the outer parenthesis, for example "((re: number) => Complex)" returns "(re: number) => Complex" + function stripParenthesis(text: string) : string { + return text.startsWith('(') && text.endsWith(')') + ? text.substring(1, text.length - 1) + : text + } +} + +function findBlockContents(text: string, blockStart: string, blockEnd: string, startIndex = 0) : { start: number, end: number, innerText: string } | undefined { + let i = startIndex + + while (!matchSubString(text, blockStart, i) && i < text.length) { + i++ + } + + if (i >= text.length) { + return undefined + } + + i++ + const start = i + + while (!matchSubString(text, blockEnd, i) || matchSubString(text, '=>', i - 1)) { + i = skipBrackets(text, i) + + i++ + } + + if (i >= text.length) { + return undefined + } + const end = i + + return { + start, + end, + innerText: text.substring(start, end) + } +} + +/** + * Split a string by a delimiter, but ignore all occurrences of the delimiter + * that are inside bracket pairs <> () [] {} + */ +export function split(text: string, delimiter: string) : string[] { + const parts: string[] = [] + + let i = 0 + let start = 0 + while (i < text.length) { + i = skipBrackets(text, i) + + if (matchSubString(text, delimiter, i)) { + parts.push(text.substring(start, i)) + i += delimiter.length + start = i + } + + i++ + } + + parts.push(text.substring(start)) + + return parts +} + +function skipBrackets(text: string, startIndex: number) : number { + let level = 0 + let i = startIndex + + do { + if (isBracketOpen(text, i)) { + level++ + } + + if (isBracketClose(text, i) && level > 0) { + level-- + } + + if (level === 0) { + break + } + + i++ + } while(i < text.length) + + return i +} + +function isBracketOpen(text: string, index: number) { + const char = text[index] + return char === '(' || char === '<' || char === '[' || char === '{' +} + +function isBracketClose(text: string, index: number) { + const char = text[index] + // we need to take care of not matching the ">" of the operator "=>" + return char === ')' || (char === '>' && text[index - 1] !== '=') || char === ']' || char === '}' +} + +function matchSubString(text: string, search: string, index: number) : boolean { + for (let i = 0; i < search.length; i++) { + if (text[i + index] !== search[i]) { + return false + } + } + + return true +} + +function trim(text: string) : string { + return text.trim() +} + +function notEmpty(text: string) : boolean { + return text.length > 0 +} + +function groupBy(items: T[], key: string) : Record { + const obj: Record = {} + + items.forEach((item) => { + obj[item[key]] = item + }) + + return obj +} diff --git a/src/index.ts b/src/index.ts index dc389e8..5eec64d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import { inspect} from 'node:util' import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' @@ -5,6 +6,7 @@ export default new Dispatcher(Specifications) import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' +import { parseReflectedType, split } from './core/parseReflectedType.js' const mockRealAdd = (a: number, b: number) => a+b const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im @@ -31,7 +33,23 @@ const sqrt = Specifications.numbers.sqrt({ console.log('Result of sqrt(16)=', sqrt(16)) console.log('Result of sqrt(-4)=', sqrt(-4)) -// Check type of the generic square implementation -console.log('Type of sqrt (number) is', Specifications.numbers.sqrt.reflectedType) -console.log('Type of square is', Specifications.generic.square.reflectedType) -console.log('Type of complex square root is', Specifications.complex.sqrt.reflectedType) +console.log() +console.log('1) NUMBER SQRT') +console.log('1.1) REFLECTED TYPE:', Specifications.numbers.sqrt.reflectedType) +console.log('1.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType), { depth: null, colors: true })) + +console.log() +console.log('2) GENERIC SQUARE') +console.log('2.1) REFLECTED TYPE:', Specifications.generic.square.reflectedType) +console.log('2.2) PARSED TYPE:', inspect(parseReflectedType('square', Specifications.generic.square.reflectedType), { depth: null, colors: true })) + +console.log() +console.log('3) COMPLEX SQRT') +console.log('3.1) REFLECTED TYPE:', Specifications.complex.sqrt.reflectedType) +console.log('3.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.complex.sqrt.reflectedType), { depth: null, colors: true })) + +// FIXME: cleanup +// console.log() +// console.log('split', split('hello**world**how**are**you', '**')) +// console.log('split', split('hello(test**world)**how**are**you', '**')) +// console.log('split', split('(dep: { multiply: (a: T, b: T) => T; }) => (z: T) => T', '=>')) From 2f7677c371409ee1d2793b86405bbe5afae95236 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 21 Sep 2023 21:09:04 +0200 Subject: [PATCH 14/21] chore: output the reflected types enclosed in quotes for clarity --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5eec64d..759df12 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,17 +35,17 @@ console.log('Result of sqrt(-4)=', sqrt(-4)) console.log() console.log('1) NUMBER SQRT') -console.log('1.1) REFLECTED TYPE:', Specifications.numbers.sqrt.reflectedType) +console.log(`1.1) REFLECTED TYPE: "${Specifications.numbers.sqrt.reflectedType}"`) console.log('1.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType), { depth: null, colors: true })) console.log() console.log('2) GENERIC SQUARE') -console.log('2.1) REFLECTED TYPE:', Specifications.generic.square.reflectedType) +console.log(`1.1) REFLECTED TYPE: "${Specifications.generic.square.reflectedType}"`) console.log('2.2) PARSED TYPE:', inspect(parseReflectedType('square', Specifications.generic.square.reflectedType), { depth: null, colors: true })) console.log() console.log('3) COMPLEX SQRT') -console.log('3.1) REFLECTED TYPE:', Specifications.complex.sqrt.reflectedType) +console.log(`1.1) REFLECTED TYPE: "${Specifications.complex.sqrt.reflectedType}"`) console.log('3.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.complex.sqrt.reflectedType), { depth: null, colors: true })) // FIXME: cleanup From 632c82966b65b419b3ad2522d61ee1023ce3658f Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 28 Sep 2023 11:30:44 +0200 Subject: [PATCH 15/21] chore: upgrade to `ts-macros@5.2.0` instead of directly from git --- package.json5 | 2 +- pnpm-lock.yaml | 30 +++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/package.json5 b/package.json5 index 97ced5b..c27ab64 100644 --- a/package.json5 +++ b/package.json5 @@ -28,7 +28,7 @@ 'del-cli': '5.1.0', mkdirp: '3.0.1', 'source-map': '0.7.4', - 'ts-macros': 'github:GoogleFeud/ts-macros', + 'ts-macros': '2.5.0', 'ts-patch': '3.0.2', typescript: '5.1.6', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 948d6ca..ea413ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ devDependencies: specifier: 0.7.4 version: 0.7.4 ts-macros: - specifier: github:GoogleFeud/ts-macros - version: github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15(typescript@5.1.6) + specifier: 2.5.0 + version: 2.5.0(typescript@5.1.6) ts-patch: specifier: 3.0.2 version: 3.0.2 @@ -843,6 +843,16 @@ packages: engines: {node: '>=12'} dev: true + /ts-macros@2.5.0(typescript@5.1.6): + resolution: {integrity: sha512-Uha8rei70+YtQS7nJ1F9m4BVUUV78vky3hIeH6JaHo2cqrKFpA9VWKzkzt4CAQU7g1pheNKr0iOkBmCzrKy2jw==} + hasBin: true + peerDependencies: + typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x + dependencies: + typescript: 5.1.6 + yargs-parser: 21.1.1 + dev: true + /ts-patch@3.0.2: resolution: {integrity: sha512-iTg8euqiNsNM1VDfOsVIsP0bM4kAVXU38n7TGQSkky7YQX/syh6sDPIRkvSS0HjT8ZOr0pq1h+5Le6jdB3hiJQ==} hasBin: true @@ -893,18 +903,12 @@ packages: engines: {node: '>=10'} dev: true + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true - - github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15(typescript@5.1.6): - resolution: {tarball: https://codeload.github.com/GoogleFeud/ts-macros/tar.gz/4f8c22db77e3b5840e3a2f285e30436f71c27e15} - id: github.com/GoogleFeud/ts-macros/4f8c22db77e3b5840e3a2f285e30436f71c27e15 - name: ts-macros - version: 2.4.2 - peerDependencies: - typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - dependencies: - typescript: 5.1.6 - dev: true From 20236355c1dfecb86bcb7e82fd03e37be9589dc8 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 29 Sep 2023 15:36:22 +0200 Subject: [PATCH 16/21] feat: do not generate export via macro but keep the export in TS for better DX --- package.json5 | 2 +- src/Complex/arithmetic.ts | 4 ++-- src/generic/arithmetic.ts | 4 ++-- src/interfaces/type.ts | 5 +++-- src/numbers/arithmetic.ts | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/package.json5 b/package.json5 index c27ab64..0d15af2 100644 --- a/package.json5 +++ b/package.json5 @@ -7,7 +7,7 @@ test: 'echo "Error: no test specified" && exit 1', build: 'mkdirp build && cpy etc/package.json build --flat && tsc', start: 'node build', - go: 'pnpm --sequential "/clean|build|start/"', + go: 'pnpm clean && pnpm build && pnpm start', clean: 'del-cli build', }, packageManager: 'pnpm', diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 5776cd6..4f78bb5 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -2,7 +2,7 @@ import {Complex} from './type.js' import type { Dependencies, Signature, Returns, RealType, AliasOf } from '../interfaces/type.js' -import {$implement} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -74,7 +74,7 @@ export const divide = // and we have to get it straight which operations we need on each type, and // in fact, we need `addReal` on both T and Complex, hence the dependency // with a custom name, not generated via Dependencies<...> -$implement!('sqrt', +export const sqrt = $reflect!('sqrt', (dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType> & Dependencies<'zero' | 'complex', T> & Dependencies<'absquare' | 're' | 'divideReal', Complex> diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 3903b8d..de5b608 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,7 +1,7 @@ import type {Dependencies, Signature} from '../interfaces/type.js' -import {$implement} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' -$implement!('square', +export const square = $reflect!('square', (dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z)) // z => dep.fooBar(z, z) // fails as desired // z => dep.multiply(z, 'foo') // still fails as desired diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 75d1b4c..2b38374 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -82,7 +82,8 @@ export type Dependencies, T> = Deps<{[K in Name]: S export type AliasOf = T & {aliasOf?: Name} // For defining implementations with type reflection -export function $implement(name: string, expr: Impl) { - $$define!(name, expr, false, true); // Final `true` is export +export function $reflect(name: string, expr: Impl) : Impl & { reflectedType: string} { + $$define!(name, expr, false, false); $$ident!(name).reflectedType = $$typeToString!(true, false, true); + return $$ident!(name) } diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index d2d3414..4b4d2cb 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,6 +1,6 @@ import type {configDependency} from '../core/Config.js' import type {Dependencies, Signature} from '../interfaces/type.js' -import { $implement } from '../interfaces/type.js' +import { $reflect } from '../interfaces/type.js' export const add: Signature<'add', number> = (a, b) => a + b export const unaryMinus: Signature<'unaryMinus', number> = a => -a @@ -11,10 +11,10 @@ export const absquare: Signature<'absquare', number> = a => a * a export const reciprocal: Signature<'reciprocal', number> = a => 1 / a export const divide: Signature<'divide', number> = (a, b) => a / b -const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a) +const basicSqrt = (a: number) => isNaN(a) ? NaN : Math.sqrt(a) export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt -$implement!('sqrt', +export const sqrt = $reflect!('sqrt', (dep: configDependency & Dependencies<'complex', number>): Signature<'sqrt', number> => { if (dep.config.predictable || !dep.complex) { return basicSqrt From 457b9cdc91a9246103804fac65bfd378040781da Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 30 Sep 2023 15:52:14 -0700 Subject: [PATCH 17/21] refactor: Possible alternate syntax for reflection --- package.json5 | 2 +- pnpm-lock.yaml | 8 ++++---- src/Complex/arithmetic.ts | 7 ++++--- src/interfaces/type.ts | 4 ++++ src/numbers/arithmetic.ts | 10 ++++++---- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/package.json5 b/package.json5 index 0d15af2..b00af7c 100644 --- a/package.json5 +++ b/package.json5 @@ -23,7 +23,7 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - '@types/node': '20.6.2', + '@types/node': '20.8.0', 'cpy-cli': '5.0.0', 'del-cli': '5.1.0', mkdirp: '3.0.1', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea413ae..b3f667b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: devDependencies: '@types/node': - specifier: 20.6.2 - version: 20.6.2 + specifier: 20.8.0 + version: 20.8.0 cpy-cli: specifier: 5.0.0 version: 5.0.0 @@ -79,8 +79,8 @@ packages: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true - /@types/node@20.6.2: - resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==} + /@types/node@20.8.0: + resolution: {integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==} dev: true /@types/normalize-package-data@2.4.1: diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 4f78bb5..6cc916d 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -2,7 +2,7 @@ import {Complex} from './type.js' import type { Dependencies, Signature, Returns, RealType, AliasOf } from '../interfaces/type.js' -import {$reflect} from '../interfaces/type.js' +import {$reflecType} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -74,7 +74,7 @@ export const divide = // and we have to get it straight which operations we need on each type, and // in fact, we need `addReal` on both T and Complex, hence the dependency // with a custom name, not generated via Dependencies<...> -export const sqrt = $reflect!('sqrt', +export const sqrt = (dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType> & Dependencies<'zero' | 'complex', T> & Dependencies<'absquare' | 're' | 'divideReal', Complex> @@ -97,4 +97,5 @@ export const sqrt = $reflect!('sqrt', const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r)) const denom = dep.conservativeSqrt(denomsq) return dep.divideReal(num, denom) - }) + } +$reflecType!(sqrt) diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 2b38374..7e697cc 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -87,3 +87,7 @@ export function $reflect(name: string, expr: Impl) : Impl & { reflectedTyp $$ident!(name).reflectedType = $$typeToString!(true, false, true); return $$ident!(name) } + +export function $reflecType(expr: Impl) { + expr.reflectedType = $$typeToString!(true, false, true); +} diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 4b4d2cb..ef92bfe 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,6 +1,6 @@ import type {configDependency} from '../core/Config.js' import type {Dependencies, Signature} from '../interfaces/type.js' -import { $reflect } from '../interfaces/type.js' +import { $reflecType } from '../interfaces/type.js' export const add: Signature<'add', number> = (a, b) => a + b export const unaryMinus: Signature<'unaryMinus', number> = a => -a @@ -14,8 +14,9 @@ export const divide: Signature<'divide', number> = (a, b) => a / b const basicSqrt = (a: number) => isNaN(a) ? NaN : Math.sqrt(a) export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt -export const sqrt = $reflect!('sqrt', - (dep: configDependency & Dependencies<'complex', number>): Signature<'sqrt', number> => { +export const sqrt = + (dep: configDependency + & Dependencies<'complex', number>): Signature<'sqrt', number> => { if (dep.config.predictable || !dep.complex) { return basicSqrt } @@ -24,4 +25,5 @@ export const sqrt = $reflect!('sqrt', if (a >= 0) return Math.sqrt(a) return dep.complex(0, Math.sqrt(unaryMinus(a))) } - }) + } +$reflecType!(sqrt) From 49b133291777d5cd9023852cf8ad25b4c19ec7c4 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Mon, 9 Oct 2023 21:10:15 -0700 Subject: [PATCH 18/21] feat: Add reflecTypes that allows multiple reflections at once --- package.json5 | 4 ++-- pnpm-lock.yaml | 22 ++++++++++++++-------- src/interfaces/type.ts | 5 +++++ src/numbers/arithmetic.ts | 11 ++++++----- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/package.json5 b/package.json5 index b00af7c..5e1b58b 100644 --- a/package.json5 +++ b/package.json5 @@ -23,12 +23,12 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - '@types/node': '20.8.0', + '@types/node': '20.8.4', 'cpy-cli': '5.0.0', 'del-cli': '5.1.0', mkdirp: '3.0.1', 'source-map': '0.7.4', - 'ts-macros': '2.5.0', + 'ts-macros': '2.6.0', 'ts-patch': '3.0.2', typescript: '5.1.6', }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3f667b..6791304 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: devDependencies: '@types/node': - specifier: 20.8.0 - version: 20.8.0 + specifier: 20.8.4 + version: 20.8.4 cpy-cli: specifier: 5.0.0 version: 5.0.0 @@ -21,8 +21,8 @@ devDependencies: specifier: 0.7.4 version: 0.7.4 ts-macros: - specifier: 2.5.0 - version: 2.5.0(typescript@5.1.6) + specifier: 2.6.0 + version: 2.6.0(typescript@5.1.6) ts-patch: specifier: 3.0.2 version: 3.0.2 @@ -79,8 +79,10 @@ packages: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true - /@types/node@20.8.0: - resolution: {integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==} + /@types/node@20.8.4: + resolution: {integrity: sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==} + dependencies: + undici-types: 5.25.3 dev: true /@types/normalize-package-data@2.4.1: @@ -843,8 +845,8 @@ packages: engines: {node: '>=12'} dev: true - /ts-macros@2.5.0(typescript@5.1.6): - resolution: {integrity: sha512-Uha8rei70+YtQS7nJ1F9m4BVUUV78vky3hIeH6JaHo2cqrKFpA9VWKzkzt4CAQU7g1pheNKr0iOkBmCzrKy2jw==} + /ts-macros@2.6.0(typescript@5.1.6): + resolution: {integrity: sha512-X7c4rHPTpBYY+MJUkIDIQMbTPAcv1y1sylrnDMsTvcbImleT4Wlheg3wNbORwnX8Zvq2ldZnttNXcZ1a0VE2Kg==} hasBin: true peerDependencies: typescript: 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x @@ -876,6 +878,10 @@ packages: hasBin: true dev: true + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 7e697cc..e56d385 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -91,3 +91,8 @@ export function $reflect(name: string, expr: Impl) : Impl & { reflectedTyp export function $reflecType(expr: Impl) { expr.reflectedType = $$typeToString!(true, false, true); } + +export function $reflecTypes(tup: ImplTuple) { + +[[tup], (elt: T) => + elt.reflectedType = $$typeToString!(true, false, true)] +} diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index ef92bfe..ba75bfe 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,11 +1,11 @@ import type {configDependency} from '../core/Config.js' import type {Dependencies, Signature} from '../interfaces/type.js' -import { $reflecType } from '../interfaces/type.js' +import { $reflecType, $reflecTypes } from '../interfaces/type.js' -export const add: Signature<'add', number> = (a, b) => a + b +export const add /*: Signature<'add', number>*/ = (a, b) => a + b export const unaryMinus: Signature<'unaryMinus', number> = a => -a -export const conj: Signature<'conj', number> = a => a -export const subtract: Signature<'subtract', number> = (a, b) => a - b +export const conj /*: Signature<'conj', number>*/ = a => a +export const subtract /*: Signature<'subtract', number>*/ = (a, b) => a - b export const multiply: Signature<'multiply', number> = (a, b) => a * b export const absquare: Signature<'absquare', number> = a => a * a export const reciprocal: Signature<'reciprocal', number> = a => 1 / a @@ -25,5 +25,6 @@ export const sqrt = if (a >= 0) return Math.sqrt(a) return dep.complex(0, Math.sqrt(unaryMinus(a))) } - } + } $reflecType!(sqrt) +$reflecTypes!([add, conj, subtract]) From ebe7cf831e8b7397fb2053504820496c8a7320fc Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 15 Oct 2023 19:58:38 -0700 Subject: [PATCH 19/21] refactor: Explore final patterns for runtime typing with ts-macros The idea is to have a single macro which lets you reflect multiple implementations. That's provided in this commit. However, there is a hitch with implementations that have no dependencies: the reflectedType property did not exist on the Signature type. However, if you add it, then the Signature type becomes opaque, so we would have to look up signatures; but that appears to be tricky, as there doesn't seem to be a way to reflect the full generic type of the Signatures interface. So this commit provides three example resolutions: (A) Export a type RTT and all no-dependency implementations have to intersect with RTT (see 'add' in number/arithmetic.ts) (B) Write no-dependency implementations as functions of no arguments (representing no dependencies passed in) returning the actual implementation (see 'subtract' in number/arithmetic.ts) (C) Make a special DSignature<> generic type (short for "Direct Signature" used only (and always) for implementations with no dependencies, and a matching $Dreflect!() macro for generating their type info. Of course, there may be other possibilities I haven't thought of. But we need to pick one. I don't think it matters _too_ much, since there only a small fraction of all implementations have no dependencies. --- src/Complex/arithmetic.ts | 7 +++---- src/generic/arithmetic.ts | 6 ++++-- src/index.ts | 24 +++++++++++++++--------- src/interfaces/type.ts | 28 +++++++++++++--------------- src/numbers/arithmetic.ts | 14 +++++++------- 5 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 6cc916d..d2155d1 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -2,7 +2,7 @@ import {Complex} from './type.js' import type { Dependencies, Signature, Returns, RealType, AliasOf } from '../interfaces/type.js' -import {$reflecType} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -82,8 +82,7 @@ export const sqrt = addTR: Signature<'addReal', T>, addRR: Signature<'add', RealType>, addCR: Signature<'addReal', Complex> - }): - Signature<'sqrt', Complex> => + }): Signature<'sqrt', Complex> => z => { const myabs = dep.conservativeSqrt(dep.absquare(z)) const r = dep.re(z) @@ -98,4 +97,4 @@ export const sqrt = const denom = dep.conservativeSqrt(denomsq) return dep.divideReal(num, denom) } -$reflecType!(sqrt) +$reflect!([sqrt]) diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index de5b608..37ea84a 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,7 +1,9 @@ import type {Dependencies, Signature} from '../interfaces/type.js' import {$reflect} from '../interfaces/type.js' -export const square = $reflect!('square', - (dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z)) +export const square = + (dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z) // z => dep.fooBar(z, z) // fails as desired // z => dep.multiply(z, 'foo') // still fails as desired +$reflect!([square]) diff --git a/src/index.ts b/src/index.ts index 759df12..0952d95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,20 @@ -import { inspect} from 'node:util' +import {inspect} from 'node:util' import {Dispatcher} from './core/Dispatcher.js' +import {Signatures, Deps} from './interfaces/type.js' +import {$$typeToString} from 'ts-macros' import * as Specifications from './all.js' +class Placeholder {} +const allSignatures = + $$typeToString!>>(true, false, true) + +console.log('Found signatures', allSignatures) + export default new Dispatcher(Specifications) import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' -import { parseReflectedType, split } from './core/parseReflectedType.js' +import {parseReflectedType} from './core/parseReflectedType.js' const mockRealAdd = (a: number, b: number) => a+b const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im @@ -36,7 +44,11 @@ console.log('Result of sqrt(-4)=', sqrt(-4)) console.log() console.log('1) NUMBER SQRT') console.log(`1.1) REFLECTED TYPE: "${Specifications.numbers.sqrt.reflectedType}"`) -console.log('1.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType), { depth: null, colors: true })) +console.log( + '1.2) PARSED TYPE:', + inspect( + parseReflectedType('sqrt', Specifications.numbers.sqrt.reflectedType), + { depth: null, colors: true })) console.log() console.log('2) GENERIC SQUARE') @@ -47,9 +59,3 @@ console.log() console.log('3) COMPLEX SQRT') console.log(`1.1) REFLECTED TYPE: "${Specifications.complex.sqrt.reflectedType}"`) console.log('3.2) PARSED TYPE:', inspect(parseReflectedType('sqrt', Specifications.complex.sqrt.reflectedType), { depth: null, colors: true })) - -// FIXME: cleanup -// console.log() -// console.log('split', split('hello**world**how**are**you', '**')) -// console.log('split', split('hello(test**world)**how**are**you', '**')) -// console.log('split', split('(dep: { multiply: (a: T, b: T) => T; }) => (z: T) => T', '=>')) diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index e56d385..446c761 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -1,4 +1,4 @@ -import {$$typeToString, $$ident, $$define} from 'ts-macros' +import {$$typeToString} from 'ts-macros' /***** * Every typocomath type has some associated types; they need @@ -74,25 +74,23 @@ export interface Signatures { type SignatureKey = keyof Signatures -export type Signature, T> = Signatures[Name] +export type Signature, T> = + Signatures[Name] +export type DSignature, T> = + Signatures[Name] & {reflectedType?: string, actualType?: Signatures[Name]} export type Returns, T> = ReturnType[Name]> -type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; +export type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; export type Dependencies, T> = Deps<{[K in Name]: Signature}> export type AliasOf = T & {aliasOf?: Name} // For defining implementations with type reflection -export function $reflect(name: string, expr: Impl) : Impl & { reflectedType: string} { - $$define!(name, expr, false, false); - $$ident!(name).reflectedType = $$typeToString!(true, false, true); - return $$ident!(name) -} - -export function $reflecType(expr: Impl) { - expr.reflectedType = $$typeToString!(true, false, true); -} - -export function $reflecTypes(tup: ImplTuple) { +export function $reflect(tup: ImplTuple) { +[[tup], (elt: T) => - elt.reflectedType = $$typeToString!(true, false, true)] + elt.reflectedType = $$typeToString!(true, false, true)] } +export function $Dreflect(tup: ImplTuple) { + +[[tup], (elt: T) => + elt.reflectedType = $$typeToString!(true, false, true)] +} +export type RTT = {reflectedType?: string} diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index ba75bfe..354667d 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,11 +1,11 @@ import type {configDependency} from '../core/Config.js' -import type {Dependencies, Signature} from '../interfaces/type.js' -import { $reflecType, $reflecTypes } from '../interfaces/type.js' +import type {Dependencies, Signature, RTT, DSignature} from '../interfaces/type.js' +import {$reflect, $Dreflect} from '../interfaces/type.js' -export const add /*: Signature<'add', number>*/ = (a, b) => a + b +export const add: Signature<'add', number> & RTT = (a, b) => a + b export const unaryMinus: Signature<'unaryMinus', number> = a => -a -export const conj /*: Signature<'conj', number>*/ = a => a -export const subtract /*: Signature<'subtract', number>*/ = (a, b) => a - b +export const conj: DSignature<'conj', number> = a => a +export const subtract = (): Signature<'subtract', number> => (a, b) => a - b export const multiply: Signature<'multiply', number> = (a, b) => a * b export const absquare: Signature<'absquare', number> = a => a * a export const reciprocal: Signature<'reciprocal', number> = a => 1 / a @@ -26,5 +26,5 @@ export const sqrt = return dep.complex(0, Math.sqrt(unaryMinus(a))) } } -$reflecType!(sqrt) -$reflecTypes!([add, conj, subtract]) +$reflect!([add, sqrt, subtract]) +$Dreflect!([conj]) From d8199341e0c9a360bff95201e09cee15c561a6eb Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Mon, 16 Oct 2023 18:48:26 -0700 Subject: [PATCH 20/21] refactor: Settle on making all implementations (possibly vacuous) factories --- src/Complex/arithmetic.ts | 6 +++++- src/index.ts | 8 -------- src/interfaces/type.ts | 18 ++++++------------ src/numbers/arithmetic.ts | 33 +++++++++++++++++++-------------- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index d2155d1..0f11f2e 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -97,4 +97,8 @@ export const sqrt = const denom = dep.conservativeSqrt(denomsq) return dep.divideReal(num, denom) } -$reflect!([sqrt]) + +$reflect!([ + add, addReal, unaryMinus, conj, subtract, multiply, absquare, divideReal, + reciprocal, divide, sqrt +]) diff --git a/src/index.ts b/src/index.ts index 0952d95..e3377e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,7 @@ import {inspect} from 'node:util' import {Dispatcher} from './core/Dispatcher.js' -import {Signatures, Deps} from './interfaces/type.js' -import {$$typeToString} from 'ts-macros' import * as Specifications from './all.js' -class Placeholder {} -const allSignatures = - $$typeToString!>>(true, false, true) - -console.log('Found signatures', allSignatures) - export default new Dispatcher(Specifications) import {Complex} from './Complex/type.js' diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 446c761..3ab1b9a 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -74,13 +74,12 @@ export interface Signatures { type SignatureKey = keyof Signatures -export type Signature, T> = - Signatures[Name] -export type DSignature, T> = - Signatures[Name] & {reflectedType?: string, actualType?: Signatures[Name]} -export type Returns, T> = ReturnType[Name]> -export type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; -export type Dependencies, T> = Deps<{[K in Name]: Signature}> +export type Signature, T> = Signatures[Name] +export type Returns, T> = + ReturnType[Name]> +type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; +export type Dependencies, T> = + Deps<{[K in Name]: Signature}> export type AliasOf = T & {aliasOf?: Name} @@ -89,8 +88,3 @@ export function $reflect(tup: ImplTuple) { +[[tup], (elt: T) => elt.reflectedType = $$typeToString!(true, false, true)] } -export function $Dreflect(tup: ImplTuple) { - +[[tup], (elt: T) => - elt.reflectedType = $$typeToString!(true, false, true)] -} -export type RTT = {reflectedType?: string} diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 354667d..51d2ce5 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,18 +1,20 @@ import type {configDependency} from '../core/Config.js' -import type {Dependencies, Signature, RTT, DSignature} from '../interfaces/type.js' -import {$reflect, $Dreflect} from '../interfaces/type.js' +import type {Dependencies, Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' -export const add: Signature<'add', number> & RTT = (a, b) => a + b -export const unaryMinus: Signature<'unaryMinus', number> = a => -a -export const conj: DSignature<'conj', number> = a => a -export const subtract = (): Signature<'subtract', number> => (a, b) => a - b -export const multiply: Signature<'multiply', number> = (a, b) => a * b -export const absquare: Signature<'absquare', number> = a => a * a -export const reciprocal: Signature<'reciprocal', number> = a => 1 / a -export const divide: Signature<'divide', number> = (a, b) => a / b +export const add = (): Signature<'add', number> => (a, b) => a + b +const unaMinus = (a: number) => -a +export const unaryMinus = (): Signature<'unaryMinus', number> => unaMinus +export const conj = (): Signature<'conj', number> => a => a +export const subtract = (): Signature<'subtract', number> => (a, b) => a - b +export const multiply = (): Signature<'multiply', number> => (a, b) => a * b +export const absquare = (): Signature<'absquare', number> => a => a * a +export const reciprocal = (): Signature<'reciprocal', number> => a => 1 / a +export const divide = (): Signature<'divide', number> => (a, b) => a / b const basicSqrt = (a: number) => isNaN(a) ? NaN : Math.sqrt(a) -export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt +export const conservativeSqrt = (): Signature<'conservativeSqrt', number> => + basicSqrt export const sqrt = (dep: configDependency @@ -23,8 +25,11 @@ export const sqrt = return a => { if (isNaN(a)) return NaN if (a >= 0) return Math.sqrt(a) - return dep.complex(0, Math.sqrt(unaryMinus(a))) + return dep.complex(0, Math.sqrt(unaMinus(a))) } } -$reflect!([add, sqrt, subtract]) -$Dreflect!([conj]) + +$reflect!([ + add, unaryMinus, conj, subtract, multiply, absquare, reciprocal, divide, + conservativeSqrt, sqrt +]) From 6f4456730690998516fc22b1c5842c9d39911dc1 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Tue, 17 Oct 2023 14:54:10 -0700 Subject: [PATCH 21/21] refactor: Extend reflection to all implementations so far And also enhances the reflected type parsing so that the types of all implementations so far will parse. --- package.json5 | 2 +- src/Complex/predicate.ts | 6 +- src/Complex/relational.ts | 2 + src/Complex/type.ts | 3 + src/core/Dispatcher.ts | 21 ++-- src/core/parseReflectedType.ts | 191 ++++++++++++++++++++------------- src/generic/relational.ts | 2 + src/numbers/native.ts | 2 + src/numbers/predicate.ts | 7 +- src/numbers/relational.ts | 2 + src/numbers/type.ts | 11 +- 11 files changed, 156 insertions(+), 93 deletions(-) diff --git a/package.json5 b/package.json5 index 5e1b58b..02e46f3 100644 --- a/package.json5 +++ b/package.json5 @@ -1,6 +1,6 @@ { name: 'typocomath', - version: '0.0.1', + version: '0.0.2', description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { diff --git a/src/Complex/predicate.ts b/src/Complex/predicate.ts index ffcefaa..fe26906 100644 --- a/src/Complex/predicate.ts +++ b/src/Complex/predicate.ts @@ -1,9 +1,13 @@ import {Complex} from './type.js' import type {Dependencies, Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' export const isReal = (dep: Dependencies<'add' | 'equal' | 'isReal', T>): Signature<'isReal', Complex> => z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im)) -export const isSquare: Signature<'isSquare', Complex> = z => true // FIXME: not correct for Complex once we get there +export const isSquare = (): Signature<'isSquare', Complex> => + z => true // FIXME: not correct for Complex once we get there + +$reflect!([isReal, isSquare]) diff --git a/src/Complex/relational.ts b/src/Complex/relational.ts index 78550d7..ae0cebc 100644 --- a/src/Complex/relational.ts +++ b/src/Complex/relational.ts @@ -1,6 +1,8 @@ import {Complex} from './type.js' import {Dependencies, Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' export const equal = (dep: Dependencies<'equal', T>): Signature<'equal', Complex> => (w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im) +$reflect!([equal]) diff --git a/src/Complex/type.ts b/src/Complex/type.ts index 10f76b6..998003e 100644 --- a/src/Complex/type.ts +++ b/src/Complex/type.ts @@ -2,6 +2,7 @@ import {joinTypes, typeOfDependency} from '../core/Dispatcher.js' import type { ZeroType, OneType, NaNType, Dependencies, Signature, Returns } from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' export type Complex = { re: T; im: T; } @@ -62,3 +63,5 @@ export const nan = export const re = (dep: Dependencies<'re', T>): Signature<'re', Complex> => z => dep.re(z.re) + +$reflect!([complex, zero, one, nan, re]) diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index 230a286..4ede7cd 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -5,6 +5,8 @@ * for specific types (including their own). */ +import {parseReflectedType, ImplementationDef} from './parseReflectedType.js' + // First helper types and functions for the Dispatcher type TypeName = string @@ -39,19 +41,17 @@ type SpecificationsGroup = Record export class Dispatcher { installSpecification( name: string, - signature: Signature, - returns: TypeName, - dependencies: Record, + defn: ImplementationDef, behavior: Function // possible todo: constrain this type based // on the signature, return type, and dependencies. Not sure if // that's really possible, though. ) { - console.log('Pretending to install', name, signature, '=>', returns) - // @ts-ignore - if (behavior.reflectedType) { - // @ts-ignore - console.log(' Reflected type:', behavior.reflectedType) - // TODO: parse the reflected type + console.log('Pretending to install', name, 'with signatures') + for (const signature of defn.fn.signatures) { + console.log(' ', signature.args, '=>', signature.returns) + } + if (defn.fn.aliasOf) { + console.log(' As an alias of', defn.fn.aliasOf) } //TODO: implement me } @@ -77,7 +77,8 @@ export class Dispatcher { for (const trio of implementations) { const [k, id, imp] = trio console.log('Handling implementation', id, 'from', k) - this.installSpecification(id, ['dunno'], 'unsure', {}, imp) + this.installSpecification( + id, parseReflectedType(id, imp.reflectedType), imp) } } } diff --git a/src/core/parseReflectedType.ts b/src/core/parseReflectedType.ts index 18705fe..e4b72a3 100644 --- a/src/core/parseReflectedType.ts +++ b/src/core/parseReflectedType.ts @@ -1,14 +1,16 @@ -export interface FunctionDef { +export type FunctionDef { name: string, + aliasOf?: string, signatures: Array<{ args: Array<{ name: string, type: string }> - // FIXME: remove undefined in the end - returns: string | undefined + returns: string }> } -export interface DependencyDef extends FunctionDef { - aliasOf: string | undefined + +export type ImplementationDef = { + fn: FunctionDef, + dependencies: Record } /** @@ -16,101 +18,116 @@ export interface DependencyDef extends FunctionDef { * * '(dep: configDependency & { complex: ((re: number) => Complex) | ((re: number, im: number) => Complex); }) => (a: number) => number | Complex' */ -export function parseReflectedType(name: string, reflectedType: string) : { fn: FunctionDef, dependencies: Record } { - const [factoryArgs, fnArgsBlock, returns] = split(reflectedType, '=>').map(trim) - const args = parseArgs(fnArgsBlock) +export function parseReflectedType(name: string, reflectedType: string): ImplementationDef { + console.log('For', name, 'parsing', reflectedType) + const [factoryArgs, fnsClause] = split(reflectedType, '=>', 2).map(trim) + const fn = parseAlias(name, fnsClause) const factoryArgsInner = findBlockContents(factoryArgs, '(', ')') const depArg = split(factoryArgsInner.innerText, ':').map(trim)[1] - const depArgBlocks = split(depArg, '&').map(trim) + const depArgBlocks: string[] = depArg ? split(depArg, '&').map(trim) : [] const deps = depArgBlocks .filter(depArgBlock => { - if (depArgBlock.startsWith('{')) { + if (depArgBlock.startsWith('{') || depArgBlock === 'configDependency') { return true } else { - console.error(`ERROR: Cannot parse dependency "${depArgBlock}"`) + throw new SyntaxError(`Cannot parse dependency "${depArgBlock}"`) } }) .flatMap(parseDependencies) - const dependencies: Record = groupBy(deps, 'name') + const dependencies: Record = groupBy(deps, 'name') - return { - fn: { name, signatures: [{args, returns}] }, - dependencies + return {fn, dependencies} +} + +function parseDependencies(deps: string): FunctionDef[] { + if (deps === 'configDependency') { + return [{name: 'config', signatures: [{args: [], returns: 'Config'}]}] } + const inner = findBlockContents(deps, '{', '}').innerText + return split(inner, ';') + .map(trim) + .filter(notEmpty) + .map(parseDependency) +} - function parseDependencies(deps: string) { - const inner = findBlockContents(deps, '{', '}').innerText - return split(inner, ';') - .map(trim) - .filter(notEmpty) - .map(parseDependency) - } +// parse a dependency like "complex: ((re: number) => Complex) | ((re: number, im: number) => Complex)" +function parseDependency(dep: string): FunctionDef { + const [name, def] = split(dep, ':').map(trim) - // parse a dependency like "complex: ((re: number) => Complex) | ((re: number, im: number) => Complex)" - function parseDependency(dep: string) { - const [name, def] = split(dep, ':').map(trim) + return parseAlias(name, def) +} - const { aliasOf, innerSignature } = parseAliasOf(def) +// Parse a possibly aliased function +function parseAlias(name: string, alias: string): FunctionDef { + const { aliasOf, innerSignature } = parseAliasOf(alias) - const signatures = split(innerSignature, '|') - .map(trim) - .map(stripParenthesis) - .map(signature => { - const [argsBlock, returns] = split(signature, '=>').map(trim) + return { name, signatures: parseSignatures(innerSignature), aliasOf } +} - if (!returns) { - console.warn(`ERROR: failed to find return type in '${signature}'`) - } +// parse function signatures like ((re: number) => Complex) | ((re: number, im: number) => Complex) +// But also have to succeed on (a: number) => number | Complex +// That's why we only split on an alternation bar `|` that's followed by +// a parenthesis; that way we avoid splitting a union return type. Note +// this is not necessarily foolproof, as there could be a return type that +// is a union with a complicated piece that has to be enclosed in parens; +// but so far it seems to work in practice. +function parseSignatures(sigs: string) { + return split(sigs, /[|]\s*(?=[(])/) + .map(trim) + .map(stripParenthesis) + .map(signature => { + const [argsBlock, returns] = split(signature, '=>').map(trim) - return { - args: parseArgs(argsBlock), - returns - } - }) - - return { name, signatures, aliasOf } - } - - // parse args like "(re: number, im: number)" - function parseArgs(argsBlock: string) : Array<{name: string, type: string}> { - const args = findBlockContents(argsBlock, '(', ')').innerText - - return split(args, ',') - .map(trim) - .map(arg => { - const [name, type] = split(arg, ':').map(trim) - return { name, type} - }) - } - - // parse "AliasOf<"divide", (a: Complex, b: RealType>) => Complex>" - function parseAliasOf(signature: string) : { innerSignature: string, aliasOf: string | undefined } { - if (!signature.startsWith('AliasOf')) { - return { - innerSignature: signature, - aliasOf: undefined + if (!returns) { + throw new SyntaxError(`Failed to find return type in '${signature}'`) } - } - const inner = findBlockContents(signature, '<', '>').innerText.trim() - const [aliasOfWithQuotes, innerSignature] = split(inner, ',').map(trim) + return { + args: parseArgs(argsBlock), + returns + } + }) +} + +// parse args like "(re: number, im: number)" +function parseArgs(argsBlock: string) : Array<{name: string, type: string}> { + const args = findBlockContents(argsBlock, '(', ')').innerText + + return split(args, ',') + .map(trim) + .map(arg => { + const [name, type] = split(arg, ':').map(trim) + return { name, type} + }) +} + +// parse "AliasOf<"divide", (a: Complex, b: RealType>) => Complex>" +function parseAliasOf(signature: string) : { innerSignature: string, aliasOf: string | undefined } { + if (!signature.startsWith('AliasOf')) { return { - innerSignature, - aliasOf: aliasOfWithQuotes.substring(1, aliasOfWithQuotes.length - 1) // remove double quotes + innerSignature: signature, + aliasOf: undefined } } - // remove the outer parenthesis, for example "((re: number) => Complex)" returns "(re: number) => Complex" - function stripParenthesis(text: string) : string { - return text.startsWith('(') && text.endsWith(')') - ? text.substring(1, text.length - 1) - : text + const inner = findBlockContents(signature, '<', '>').innerText.trim() + const [aliasOfWithQuotes, innerSignature] = split(inner, ',').map(trim) + return { + innerSignature, + aliasOf: aliasOfWithQuotes.substring(1, aliasOfWithQuotes.length - 1) // remove double quotes } } +// remove the outer parenthesis, for example "((re: number) => Complex)" returns "(re: number) => Complex" +function stripParenthesis(text: string) : string { + return text.startsWith('(') && text.endsWith(')') + ? text.substring(1, text.length - 1) + : text +} + function findBlockContents(text: string, blockStart: string, blockEnd: string, startIndex = 0) : { start: number, end: number, innerText: string } | undefined { let i = startIndex @@ -143,21 +160,45 @@ function findBlockContents(text: string, blockStart: string, blockEnd: string, s } } +/** + * Given a string, generate a string source for a regexp that will match + * exactly the given string. + * Uses the fact that the only characters that need to be escaped in + * a character class are \, ], and ^ + */ +function regexpQuote(s: string) { + const special = '\\]^' + let re = '' + for (const char of s) { + if (special.includes(char)) re += `\\${char}` + else re += `[${char}]` + } + return re +} + /** * Split a string by a delimiter, but ignore all occurrences of the delimiter * that are inside bracket pairs <> () [] {} */ -export function split(text: string, delimiter: string) : string[] { +export function split( + text: string, delimiter: string | RegExp, pieces = 0): string[] { + const delim: RegExp = typeof delimiter === 'string' + ? new RegExp(regexpQuote(delimiter), 'y') + : new RegExp(delimiter.source, 'y') const parts: string[] = [] let i = 0 + let n = 1 let start = 0 - while (i < text.length) { + while (i < text.length && (pieces === 0 || n < pieces)) { i = skipBrackets(text, i) - if (matchSubString(text, delimiter, i)) { + delim.lastIndex = i + const result = delim.exec(text) + if (result) { parts.push(text.substring(start, i)) - i += delimiter.length + n += 1 + i += result[0].length start = i } diff --git a/src/generic/relational.ts b/src/generic/relational.ts index 714cb49..a05c535 100644 --- a/src/generic/relational.ts +++ b/src/generic/relational.ts @@ -1,5 +1,7 @@ import {Dependencies, Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' export const unequal = (dep: Dependencies<'equal', T>): Signature<'unequal', T> => (x, y) => !dep.equal(x, y) +$reflect!([unequal]) diff --git a/src/numbers/native.ts b/src/numbers/native.ts index 10cd111..ea2f66b 100644 --- a/src/numbers/native.ts +++ b/src/numbers/native.ts @@ -1,2 +1,4 @@ export * from './type.js' export * from './arithmetic.js' +export * from './predicate.js' +export * from './relational.js' diff --git a/src/numbers/predicate.ts b/src/numbers/predicate.ts index 03bd80f..0a51d65 100644 --- a/src/numbers/predicate.ts +++ b/src/numbers/predicate.ts @@ -1,4 +1,7 @@ import type {Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' -export const isReal: Signature<'isReal', number> = (a) => true -export const isSquare: Signature<'isSquare', number> = (a) => a >= 0 +export const isReal = (): Signature<'isReal', number> => (a) => true +export const isSquare = (): Signature<'isSquare', number> => (a) => a >= 0 + +$reflect!([isReal, isSquare]) diff --git a/src/numbers/relational.ts b/src/numbers/relational.ts index 8f1ac92..9dc9891 100644 --- a/src/numbers/relational.ts +++ b/src/numbers/relational.ts @@ -1,5 +1,6 @@ import {configDependency} from '../core/Config.js' import {Signature} from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16 @@ -19,3 +20,4 @@ export const equal = return false } +$reflect!([equal]) diff --git a/src/numbers/type.ts b/src/numbers/type.ts index f234a8f..fde9337 100644 --- a/src/numbers/type.ts +++ b/src/numbers/type.ts @@ -1,4 +1,5 @@ import type { Signature } from '../interfaces/type.js' +import {$reflect} from '../interfaces/type.js' export const number_type = { name: 'number', // just until we have reflection to tell us @@ -20,7 +21,9 @@ declare module "../interfaces/type" { } // I don't like the redundancy of repeating 'zero'; any way to eliminate that? -export const zero: Signature<'zero', number> = (a) => 0 -export const one: Signature<'one', number> = (a) => 1 -export const nan: Signature<'nan', number> = (a) => NaN -export const re: Signature<'re', number> = (a) => a +export const zero = (): Signature<'zero', number> => (a) => 0 +export const one = (): Signature<'one', number> => (a) => 1 +export const nan = (): Signature<'nan', number> => (a) => NaN +export const re = (): Signature<'re', number> => (a) => a + +$reflect!([zero, one, nan, re])