From 35a8c62ff2320ef2cc9ebb42c22f7afd37c57c2e Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 25 Jan 2023 14:42:23 +0100 Subject: [PATCH 01/29] Set up typescript-rtti (WIP) --- .gitignore | 3 +- README.md | 4 +- build/package.json | 3 + package.json5 | 11 ++- pnpm-lock.yaml | 194 +++++++++++++++++++++++++++++++++++++- src/core/Dispatcher.ts | 7 ++ src/index.ts | 52 +++++++++- src/numbers/arithmetic.ts | 2 +- tsconfig.json | 11 ++- 9 files changed, 273 insertions(+), 14 deletions(-) create mode 100644 build/package.json diff --git a/.gitignore b/.gitignore index d0c9d9e..f63a763 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,8 @@ *~ # Typescript # emitted code -obj +build/* +!build/package.json # ---> Node # Logs diff --git a/README.md b/README.md index d15fcc4..49e5266 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,6 @@ A final (?) prototype for a refactor of mathjs, culminating the picomath, pocoma To build and run the prototype, run: ``` -npx tsc -node obj +pnpm install +pnpm build-and-run ``` diff --git a/build/package.json b/build/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/build/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/package.json5 b/package.json5 index 874d5e3..1632098 100644 --- a/package.json5 +++ b/package.json5 @@ -4,6 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { + 'build-and-run': 'ttsc -b && node build', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ @@ -17,7 +18,15 @@ type: 'git', url: 'https://code.studioinfinity.org/glen/typocomath.git', }, + dependencies: { + 'reflect-metadata': '0.1.13', + 'typescript-rtti': '0.8.2', + }, devDependencies: { - typescript: '^4.9.3', + '@types/node': '18.11.18', + 'ts-node': '10.9.1', + ttypescript: '1.5.15', + typescript: '4.8.4', + 'typescript-rtti': '0.8.2', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095704b..153c7e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,15 +1,199 @@ lockfileVersion: 5.4 specifiers: - typescript: ^4.9.3 + '@types/node': 18.11.18 + reflect-metadata: 0.1.13 + ts-node: 10.9.1 + ttypescript: 1.5.15 + typescript: 4.8.4 + typescript-rtti: 0.8.2 + +dependencies: + reflect-metadata: 0.1.13 + typescript-rtti: 0.8.2_tjkpoysld524rvy7hjviaz6rje devDependencies: - typescript: 4.9.3 + '@types/node': 18.11.18 + ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm + ttypescript: 1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka + typescript: 4.8.4 packages: - /typescript/4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} + /@cspotcode/source-map-support/0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@tsconfig/node10/1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12/1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14/1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16/1.0.3: + resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true + + /@types/node/18.11.18: + resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + dev: true + + /acorn-walk/8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} hasBin: true dev: true + + /arg/4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /create-require/1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /diff/4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + 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 + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + + /make-error/1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /reflect-metadata/0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + dev: false + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /ts-node/10.9.1_vqcafhj4xvr2nzknlrdklk55zm: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.11.18 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.8.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ttypescript/1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka: + resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} + hasBin: true + peerDependencies: + ts-node: '>=8.0.2' + typescript: '>=3.2.2' + dependencies: + resolve: 1.22.1 + ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm + typescript: 4.8.4 + dev: true + + /typescript-rtti/0.8.2_tjkpoysld524rvy7hjviaz6rje: + resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==} + engines: {node: '>=10'} + peerDependencies: + reflect-metadata: ^0.1.13 + typescript: '4.6' + dependencies: + reflect-metadata: 0.1.13 + typescript: 4.8.4 + dev: false + + /typescript/4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + engines: {node: '>=4.2.0'} + hasBin: true + + /v8-compile-cache-lib/3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /yn/3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index def1bd3..1da89ff 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -1,3 +1,6 @@ +import "reflect-metadata" +import { reflect, type CallSite } from 'typescript-rtti' + /* A Dispatcher is a collection of operations that do run-time * dispatch on the types of their arguments. Thus, every individual * method is like a typed-function (from the library by that name), @@ -46,6 +49,10 @@ export class Dispatcher { // that's really possible, though. ) { console.log('Pretending to install', name, signature, '=>', returns) + + // @ts-ignore + console.log(name, 'signature', reflect(signature)) + console.log(name, 'dependencies', reflect(dependencies)) //TODO: implement me } installType(name: TypeName, typespec: TypeSpecification) { diff --git a/src/index.ts b/src/index.ts index 297b271..5d8c0e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,57 @@ import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' - -export default new Dispatcher(Specifications) - import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' +import 'reflect-metadata' +import { ReflectedFunctionParameter, ReflectedObjectRef, reflect } from 'typescript-rtti' +import { square } from './generic/arithmetic.js' + +// verify that typescript-rtti works (just as experiment) +const add = (a: number, b: number): number => a + b +console.log('reflect add') +console.log('parameterNames', reflect(add).parameterNames) +console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString())) +console.log('returnType', reflect(add).returnType.toString()) +console.log() +// output: +// reflect function add +// parameterNames [ 'a', 'b' ] +// parameterTypes [ 'class Number', 'class Number' ] +// returnType class Number + +// try out a very simple case (just as experiment) +function createSquare(deps: { + multiply: (a: number, b: number) => number, + subtract: (a: number, b: number) => number +}) { + return (a: number) => deps.multiply(a, a) +} +console.log('reflect createSquare') +console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) +console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) +// @ts-ignore +console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) +console.log('parameterTypes[0][0]', + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] +) +// @ts-ignore +console.log('parameters[0]', reflect(createSquare).parameters[0]) +// FIXME: where to find the information of the types of the dependencies multiply and subtract? + + + +// FIXME: get all types out of Specifications +// console.log('Specifications', reflect(Specifications)) // Throws errors + + +// TODO: import all specificiations (turned off for debugging purposes) +// export default new Dispatcher(Specifications) + + const mockRealAdd = (a: number, b: number) => a+b const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 11da5c2..ef0af93 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -10,7 +10,7 @@ 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 export const sqrt = diff --git a/tsconfig.json b/tsconfig.json index aae3a94..4470b45 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,15 @@ "compilerOptions": { "target": "ES2022", "rootDir": "./src", - "outDir": "./obj" + "outDir": "./build", + "esModuleInterop": true, + "allowJs": false, + "noImplicitAny": false, + "moduleResolution": "Node", + "plugins": [ + { + "transform": "typescript-rtti/dist/transformer" + } + ] } } From 1f2a59c8025260a2ed0e3832243a4089e2827dc1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:05:55 +0100 Subject: [PATCH 02/29] fix some exports not being understood by typescript-rtti --- src/Complex/all.ts | 8 ++------ src/numbers/all.ts | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Complex/all.ts b/src/Complex/all.ts index 3ff6311..ea13e82 100644 --- a/src/Complex/all.ts +++ b/src/Complex/all.ts @@ -1,6 +1,2 @@ -import * as Complex from './native.js' -import * as complex from './arithmetic.js' - -export { complex } - -export {Complex} +export * as Complex from './native.js' +export * as complex from './arithmetic.js' diff --git a/src/numbers/all.ts b/src/numbers/all.ts index deb4a8e..b71b4c3 100644 --- a/src/numbers/all.ts +++ b/src/numbers/all.ts @@ -1,3 +1 @@ -import * as numbers from './native.js' - -export {numbers} +export * as numbers from './native.js' From 5872bd85377ff4128e33b8f30a316f4e2e10d94a Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:07:47 +0100 Subject: [PATCH 03/29] chore: switch to the `typescript` and `typescript-rtti` versions used in the online playground --- package.json5 | 5 ++--- pnpm-lock.yaml | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/package.json5 b/package.json5 index 1632098..68400a2 100644 --- a/package.json5 +++ b/package.json5 @@ -20,13 +20,12 @@ }, dependencies: { 'reflect-metadata': '0.1.13', - 'typescript-rtti': '0.8.2', }, devDependencies: { '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', - typescript: '4.8.4', - 'typescript-rtti': '0.8.2', + typescript: '4.7.4', + 'typescript-rtti': '0.8.3', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 153c7e0..e7c135f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,18 +5,18 @@ specifiers: reflect-metadata: 0.1.13 ts-node: 10.9.1 ttypescript: 1.5.15 - typescript: 4.8.4 - typescript-rtti: 0.8.2 + typescript: 4.7.4 + typescript-rtti: 0.8.3 dependencies: reflect-metadata: 0.1.13 - typescript-rtti: 0.8.2_tjkpoysld524rvy7hjviaz6rje devDependencies: '@types/node': 18.11.18 - ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm - ttypescript: 1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka - typescript: 4.8.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm + typescript: 4.7.4 + typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu packages: @@ -114,7 +114,6 @@ packages: /reflect-metadata/0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} - dev: false /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} @@ -130,7 +129,7 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_vqcafhj4xvr2nzknlrdklk55zm: + /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -156,12 +155,12 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.8.4 + typescript: 4.7.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true - /ttypescript/1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka: + /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -169,25 +168,26 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.1 - ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm - typescript: 4.8.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + typescript: 4.7.4 dev: true - /typescript-rtti/0.8.2_tjkpoysld524rvy7hjviaz6rje: - resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==} + /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: + resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: reflect-metadata: ^0.1.13 - typescript: '4.6' + typescript: ^4.5 || ^4.6 || ^4.7 dependencies: reflect-metadata: 0.1.13 - typescript: 4.8.4 - dev: false + typescript: 4.7.4 + dev: true - /typescript/4.8.4: - resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} From 86688ca12935423127b358257b33f723185526d4 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 15:52:15 +0100 Subject: [PATCH 04/29] fix: generate CommonJS output instead of ESM as a workaround for typescript-rtti issue #94 --- .gitignore | 3 +-- build/package.json | 3 --- package.json5 | 2 +- src/index.ts | 43 +++++++++++++++++++++++++++++++++++-------- tsconfig.json | 1 + 5 files changed, 38 insertions(+), 14 deletions(-) delete mode 100644 build/package.json diff --git a/.gitignore b/.gitignore index f63a763..aeea740 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,7 @@ *~ # Typescript # emitted code -build/* -!build/package.json +build # ---> Node # Logs diff --git a/build/package.json b/build/package.json deleted file mode 100644 index 3dbc1ca..0000000 --- a/build/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/package.json5 b/package.json5 index 68400a2..959a089 100644 --- a/package.json5 +++ b/package.json5 @@ -20,12 +20,12 @@ }, dependencies: { 'reflect-metadata': '0.1.13', + 'typescript-rtti': '0.8.3', }, devDependencies: { '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', typescript: '4.7.4', - 'typescript-rtti': '0.8.3', }, } diff --git a/src/index.ts b/src/index.ts index 5d8c0e7..13c39fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ +import 'reflect-metadata' + import {Dispatcher} from './core/Dispatcher.js' import * as Specifications from './all.js' import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' -import 'reflect-metadata' -import { ReflectedFunctionParameter, ReflectedObjectRef, reflect } from 'typescript-rtti' +import { CallSite, ReflectedObjectRef, reflect } from 'typescript-rtti' import { square } from './generic/arithmetic.js' // verify that typescript-rtti works (just as experiment) @@ -21,29 +22,55 @@ console.log() // returnType class Number // try out a very simple case (just as experiment) -function createSquare(deps: { - multiply: (a: number, b: number) => number, - subtract: (a: number, b: number) => number +function createSquare(deps: { + multiply: (a: number, b: number) => number, + subtract: (a: number, b: number) => number }) { return (a: number) => deps.multiply(a, a) } console.log('reflect createSquare') console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) -console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) +// console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) // @ts-ignore console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) -console.log('parameterTypes[0][0]', +console.log('parameterTypes[0].ref.m[0]', // @ts-ignore (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, // @ts-ignore (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] ) +console.log('parameterTypes[0].ref.m[0].t.m', + // @ts-ignore + (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].t.m +) // @ts-ignore -console.log('parameters[0]', reflect(createSquare).parameters[0]) +// console.log('parameters[0]', reflect(createSquare).parameters[0]) // FIXME: where to find the information of the types of the dependencies multiply and subtract? +// Test whether we loose the type information when casting to a generic interface +// Conclusion: we keep the information, that is good. +console.log() +console.log('reflect createFunction') +type MathjsDependencies = Record +type MathjsCreateFunction = (deps: MathjsDependencies) => Function +const createFunction: MathjsCreateFunction = createSquare as MathjsCreateFunction +console.log('parameter names', reflect(createFunction).parameters.map(parameter => parameter.name)) +// @ts-ignore +console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) + +console.log() +console.log('CallSite') +function foo(call? : CallSite) { + console.log(reflect(call).typeParameters[0].isClass(String)) +} + +function bar(call? : CallSite) { + foo(); +} +bar(); + // FIXME: get all types out of Specifications // console.log('Specifications', reflect(Specifications)) // Throws errors diff --git a/tsconfig.json b/tsconfig.json index 4470b45..c761525 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "allowJs": false, "noImplicitAny": false, "moduleResolution": "Node", + "module": "commonjs", "plugins": [ { "transform": "typescript-rtti/dist/transformer" From 946b4a495f7c64190e7ac763e05df7a2304e583f Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 2 Feb 2023 16:14:25 +0100 Subject: [PATCH 05/29] use `CallSite` reflection to get some information out of `specifications` (WIP) --- src/index.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index 13c39fa..ce04c21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import 'reflect-metadata' import {Dispatcher} from './core/Dispatcher.js' -import * as Specifications from './all.js' +import * as specifications from './all.js' import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.js' @@ -58,24 +58,29 @@ console.log('parameter names', reflect(createFunction).parameters.map(parameter // @ts-ignore console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) - +// TODO: more specific definition of Specifications +type Specifications = Record> console.log() console.log('CallSite') -function foo(call? : CallSite) { - console.log(reflect(call).typeParameters[0].isClass(String)) +function reflectSpecifications(specifications: Specifications, callSite? : CallSite) { + console.log('specifications', reflect(callSite).parameters[0]) + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref) // shows 'numbers', 'Complex, 'complex', 'generic' + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref.m + .find(item => item.n === 'generic').t.m) // shows 'square', 'unequal' + // @ts-ignore + console.log('specifications', reflect(callSite).parameters[0]._ref.m + .find(item => item.n === 'generic').t.m + .find(item => item.n === 'square').t.p) // [ { n: 'dep', t: [Function: t], b: undefined, v: null } ] + // @ts-ignore + // FIXME: now, we should be able to get the signature of the multiply dependency of the function square, but how? } - -function bar(call? : CallSite) { - foo(); -} -bar(); - -// FIXME: get all types out of Specifications -// console.log('Specifications', reflect(Specifications)) // Throws errors +reflectSpecifications(specifications); -// TODO: import all specificiations (turned off for debugging purposes) +// TODO: import all specifications (turned off for debugging purposes) // export default new Dispatcher(Specifications) @@ -90,4 +95,5 @@ const quatAbsquare = absquare_complex({ 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() console.log('Result is', myabs) From b9cfe706fc04558b32231d84fd0328972548a845 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 13 Mar 2023 16:04:11 +0100 Subject: [PATCH 06/29] Experiment of creating a TypeScript plugin (WIP) --- package.json5 | 2 + plugins/.gitignore | 1 + plugins/infer.ts | 98 +++++++++++++++++++++++++++++++++ src/generic/arithmeticDirect.ts | 8 +++ src/generic/infer.ts | 4 ++ 5 files changed, 113 insertions(+) create mode 100644 plugins/.gitignore create mode 100644 plugins/infer.ts create mode 100644 src/generic/arithmeticDirect.ts create mode 100644 src/generic/infer.ts diff --git a/package.json5 b/package.json5 index 959a089..c6dd1cb 100644 --- a/package.json5 +++ b/package.json5 @@ -5,6 +5,8 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', + 'experiment-infer': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment-infer-direct': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmeticDirect.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 0000000..a6c7c28 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/plugins/infer.ts b/plugins/infer.ts new file mode 100644 index 0000000..03b792d --- /dev/null +++ b/plugins/infer.ts @@ -0,0 +1,98 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +/** + * # The idea + * + * Create a TypeScript plugin which can replace structures like: + * + * infer(factoryFunction) + * + * where `factoryFunction` is a mathjs factory function in TypeScript, with something like: + * + * infer({ signature: factoryFunction }) + * + * where `signature` is a string containing the type of the factory function and its dependencies. + * + * # How to run + * + * pnpm experiment-infer + * pnpm experiment-infer-direct + * + * # Read more + * + * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API + */ + +export function infer(sourceFile: ts.SourceFile) { + recurse(sourceFile); + + function getType(kind: number) { + switch(kind) { + case ts.SyntaxKind.NumberKeyword: return 'number' + case ts.SyntaxKind.StringKeyword: return 'string' + case ts.SyntaxKind.BooleanKeyword: return 'boolean' + default: return String(ts.SyntaxKind[kind]) // TODO: work out all types + } + } + + function recurse(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Identifier) { + console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) + } + + // recognize a structure like: + // + // export const square = infer((dep: { + // multiply: (a: number, b: number) => number + // }): (a: number) => number => + // z => dep.multiply(z, z) + // ) + if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { + console.log('dep', getType(node['type'].kind), node) + + node['type']?.members?.forEach(member => { + console.log('member', { + name: member.name.escapedText, + parameters: member.type.parameters.map(parameter => { + return parameter.name.escapedText + ': ' + getType(parameter.type.kind) + }), + returns: getType(member.type.type.kind) + }) + }) + } + + // recognize a structure like: + // + // export const square = + // (dep: Dependencies<'multiply' | 'unaryMinus', T>): Signature<'square', T> => + // z => dep.multiply(z, z) + if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { + // TODO + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) +fileNames.forEach(fileName => { + // Parse a file + const sourceFile = ts.createSourceFile( + fileName, + readFileSync(fileName).toString(), + ts.ScriptTarget.ES2022, + /*setParentNodes */ true + ); + + console.log('AST', fileName, inspect(sourceFile, { depth: null, colors: true })) + + console.log(sourceFile.text) + console.log() + + infer(sourceFile); +}); diff --git a/src/generic/arithmeticDirect.ts b/src/generic/arithmeticDirect.ts new file mode 100644 index 0000000..65a7782 --- /dev/null +++ b/src/generic/arithmeticDirect.ts @@ -0,0 +1,8 @@ +import {infer} from './infer' + +export const square = infer((dep: { + multiply: (a: number, b: number) => number, + unaryMinus: (x: number) => number, // just for the experiment +}): (a: number) => number => + z => dep.multiply(z, z) +) diff --git a/src/generic/infer.ts b/src/generic/infer.ts new file mode 100644 index 0000000..6e654cb --- /dev/null +++ b/src/generic/infer.ts @@ -0,0 +1,4 @@ +export function infer(arg: T) : T { + console.error('infer should be replace with runtime type information by a magic TypeScript plugin') + return arg +} From aa044a54e78f547516fa0f7ef497d91da27c7c3f Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 13 Mar 2023 16:31:41 +0100 Subject: [PATCH 07/29] Move the experiment into src/plugins and src/experiment --- package.json5 | 4 ++-- plugins/.gitignore | 1 - .../arithmeticDirect.ts => experiment/arithmeticInfer.ts} | 2 +- {plugins => src/plugins}/infer.ts | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 plugins/.gitignore rename src/{generic/arithmeticDirect.ts => experiment/arithmeticInfer.ts} (84%) rename {plugins => src/plugins}/infer.ts (97%) diff --git a/package.json5 b/package.json5 index c6dd1cb..2fcf926 100644 --- a/package.json5 +++ b/package.json5 @@ -5,8 +5,8 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment-infer': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment-infer-direct': 'tsc plugins/infer.ts && node plugins/infer.js ./src/generic/arithmeticDirect.ts', + 'experiment-infer': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment-infer-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/plugins/.gitignore b/plugins/.gitignore deleted file mode 100644 index a6c7c28..0000000 --- a/plugins/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/src/generic/arithmeticDirect.ts b/src/experiment/arithmeticInfer.ts similarity index 84% rename from src/generic/arithmeticDirect.ts rename to src/experiment/arithmeticInfer.ts index 65a7782..92aeffd 100644 --- a/src/generic/arithmeticDirect.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,4 +1,4 @@ -import {infer} from './infer' +import {infer} from '../generic/infer' export const square = infer((dep: { multiply: (a: number, b: number) => number, diff --git a/plugins/infer.ts b/src/plugins/infer.ts similarity index 97% rename from plugins/infer.ts rename to src/plugins/infer.ts index 03b792d..8efdc22 100644 --- a/plugins/infer.ts +++ b/src/plugins/infer.ts @@ -24,6 +24,7 @@ import { inspect } from 'util' * * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API + * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API */ From f8553aa748fff0e843661fe9b71a09583a035aac Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Tue, 14 Mar 2023 09:51:28 +0100 Subject: [PATCH 08/29] Add another experiment infer2 (WIP) --- README.md | 50 +++++++++++++ package.json5 | 5 +- ...arithmeticInfer.ts => arithmeticInfer1.ts} | 0 src/experiment/arithmeticInfer2.ts | 15 ++++ src/plugins/{infer.ts => infer1.ts} | 26 ------- src/plugins/infer2.ts | 74 +++++++++++++++++++ 6 files changed, 142 insertions(+), 28 deletions(-) rename src/experiment/{arithmeticInfer.ts => arithmeticInfer1.ts} (100%) create mode 100644 src/experiment/arithmeticInfer2.ts rename src/plugins/{infer.ts => infer1.ts} (75%) create mode 100644 src/plugins/infer2.ts diff --git a/README.md b/README.md index 49e5266..44b4a46 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,53 @@ To build and run the prototype, run: pnpm install pnpm build-and-run ``` + +## experiment + +See: the section under `/src/experiment` and `/src/plugins`. + +### The idea + +Create a TypeScript plugin which can replace structures like: + + infer(factoryFunction) + +where `factoryFunction` is a mathjs factory function in TypeScript, with something like: + + infer({ signature: factoryFunction }) + +where `signature` is a string containing the type of the factory function and its dependencies. + +Relevant methods of the TypeScript compiler are: + +```ts +const program = ts.createProgram(fileNames, options) +const typeChecker = program.getTypeChecker() + +// relevant methods: +// +// typeChecker.getSymbolAtLocation +// typeChecker.getTypeOfSymbolAtLocation +// typeChecker.getResolvedSignature +// typeChecker.getSignaturesOfType +``` + +### Status + +None of the experiments (`infer1` and `infer2`) are outputting something useful yet. + + +### How to run + + pnpm experiment:infer1 + pnpm experiment:infer1-direct + pnpm experiment:infer2 + +### Read more + +- https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker +- https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API +- https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments +- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function \ No newline at end of file diff --git a/package.json5 b/package.json5 index 2fcf926..a140cb8 100644 --- a/package.json5 +++ b/package.json5 @@ -5,8 +5,9 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment-infer': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment-infer-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer.ts', + 'experiment:infer1': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', + 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer1.ts', + 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer1.ts similarity index 100% rename from src/experiment/arithmeticInfer.ts rename to src/experiment/arithmeticInfer1.ts diff --git a/src/experiment/arithmeticInfer2.ts b/src/experiment/arithmeticInfer2.ts new file mode 100644 index 0000000..ab4eebf --- /dev/null +++ b/src/experiment/arithmeticInfer2.ts @@ -0,0 +1,15 @@ +import { infer } from '../generic/infer' +import { Dependencies, Signature } from '../interfaces/type' + +export type multiplyDep = Dependencies<'multiply', T> + +export const square1 = + (dep: Dependencies<'multiply', T>): Signature<'square', T> => + z => dep.multiply(z, z) + +export const square2 = infer((dep: { + multiply: (a: number, b: number) => number, + unaryMinus: (x: number) => number, // just for the experiment +}): (a: number) => number => + z => dep.multiply(z, z) +) diff --git a/src/plugins/infer.ts b/src/plugins/infer1.ts similarity index 75% rename from src/plugins/infer.ts rename to src/plugins/infer1.ts index 8efdc22..81fa0a6 100644 --- a/src/plugins/infer.ts +++ b/src/plugins/infer1.ts @@ -2,32 +2,6 @@ import { readFileSync } from "fs"; import * as ts from "typescript"; import { inspect } from 'util' -/** - * # The idea - * - * Create a TypeScript plugin which can replace structures like: - * - * infer(factoryFunction) - * - * where `factoryFunction` is a mathjs factory function in TypeScript, with something like: - * - * infer({ signature: factoryFunction }) - * - * where `signature` is a string containing the type of the factory function and its dependencies. - * - * # How to run - * - * pnpm experiment-infer - * pnpm experiment-infer-direct - * - * # Read more - * - * - https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - * - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API - */ - export function infer(sourceFile: ts.SourceFile) { recurse(sourceFile); diff --git a/src/plugins/infer2.ts b/src/plugins/infer2.ts new file mode 100644 index 0000000..43fc64b --- /dev/null +++ b/src/plugins/infer2.ts @@ -0,0 +1,74 @@ +import * as ts from "typescript" + +// based on: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker + +infer2(process.argv.slice(2), { + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.CommonJS +}) + +function infer2( + fileNames: string[], + options: ts.CompilerOptions +): void { + const program = ts.createProgram(fileNames, options) + const typeChecker = program.getTypeChecker() + + for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + ts.forEachChild(sourceFile, visit) + } + } + + return + + function visit(node: ts.Node) { + // // Only consider exported nodes + // if (!isNodeExported(node)) { + // return; + // } + + // console.log('Node', node.kind, node?.['name']?.escapedText) + + if (ts.isModuleDeclaration(node)) { + // This is a namespace, visit its children + console.log('check') + ts.forEachChild(node, visit); + } else if (ts.isTypeAliasDeclaration(node)) { + console.log('isTypeAliasDeclaration', node.name.escapedText) + + let symbol = typeChecker.getSymbolAtLocation(node.name); + if (symbol) { + const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) + const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) + + // checker.getResolvedSignature(symbol) + console.log('symbol', symbol.getName(), symbolSignature) + + // getTypeOfSymbolAtLocation + // getResolvedSignature + } + } else if (ts.isCallExpression(node)) { + console.log('isCallExpression', node.expression) + } else if (ts.isFunctionDeclaration(node)) { + console.log('isFunctionDeclaration', node.name.escapedText, { typeParameter0: node.typeParameters[0] }) + + if (node.name.escapedText === 'infer') { + const param0 = node.typeParameters[0] + if (ts.isPropertyDeclaration(param0)) { + const symbol = typeChecker.getSymbolAtLocation(param0) + + // TODO: get resolving + + // console.log('getResolvedSignature', typeChecker.getResolvedSignature(node) ) + + // const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) + // const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) + // console.log('symbol', symbol.getName(), symbolSignature) + + // console.log('getSignaturesOfType', typeChecker.getSignaturesOfType(param0) + } + } + } + } +} From 6a063d738540c72db3ea098af8511e4010447f4e Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Tue, 14 Mar 2023 10:15:56 +0100 Subject: [PATCH 09/29] Add a useful resource to the list with articles --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44b4a46..ebd41b6 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,5 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API - https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments -- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function \ No newline at end of file +- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function +- https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ From 16eb09fe6107bb1e352dbad5b40d9ab6fa7fff15 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Thu, 17 Aug 2023 23:34:03 -0700 Subject: [PATCH 10/29] chore: update a couple packages --- package.json5 | 1 + pnpm-lock.yaml | 106 ++++++++++++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/package.json5 b/package.json5 index a140cb8..0044401 100644 --- a/package.json5 +++ b/package.json5 @@ -23,6 +23,7 @@ }, dependencies: { 'reflect-metadata': '0.1.13', + 'source-map': '^0.7.4', 'typescript-rtti': '0.8.3', }, devDependencies: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7c135f..8ec9e4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,121 +1,133 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - '@types/node': 18.11.18 - reflect-metadata: 0.1.13 - ts-node: 10.9.1 - ttypescript: 1.5.15 - typescript: 4.7.4 - typescript-rtti: 0.8.3 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: - reflect-metadata: 0.1.13 + reflect-metadata: + specifier: 0.1.13 + version: 0.1.13 + source-map: + specifier: ^0.7.4 + version: 0.7.4 + typescript-rtti: + specifier: 0.8.3 + version: 0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4) devDependencies: - '@types/node': 18.11.18 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi - ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm - typescript: 4.7.4 - typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu + '@types/node': + specifier: 18.11.18 + version: 18.11.18 + ts-node: + specifier: 10.9.1 + version: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) + ttypescript: + specifier: 1.5.15 + version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) + typescript: + specifier: 4.7.4 + version: 4.7.4 packages: - /@cspotcode/source-map-support/0.8.1: + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@jridgewell/resolve-uri/3.1.0: + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.14: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@tsconfig/node10/1.0.9: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.3: + /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/node/18.11.18: + /@types/node@18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.8.2: + /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /is-core-module/2.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /reflect-metadata/0.1.13: + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + dev: false - /resolve/1.22.1: + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: @@ -124,12 +136,17 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: false + + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: + /ts-node@10.9.1(@types/node@18.11.18)(typescript@4.7.4): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -160,7 +177,7 @@ packages: yn: 3.1.1 dev: true - /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: + /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -168,11 +185,11 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.1 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) typescript: 4.7.4 dev: true - /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: + /typescript-rtti@0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4): resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: @@ -181,19 +198,18 @@ packages: dependencies: reflect-metadata: 0.1.13 typescript: 4.7.4 - dev: true + dev: false - /typescript/4.7.4: + /typescript@4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true From 3653077c9507ca1ac0b552a931333eff5ad353fb Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 18 Aug 2023 17:39:57 +0200 Subject: [PATCH 11/29] Add an experiment reading out a JSDoc comment --- README.md | 2 + package.json5 | 5 +- pnpm-lock.yaml | 125 +++++++++++++---------------- src/experiment/arithmeticInfer3.js | 16 ++++ src/generic/infer.ts | 2 +- src/plugins/infer1.ts | 2 +- src/plugins/infer3.ts | 67 ++++++++++++++++ 7 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 src/experiment/arithmeticInfer3.js create mode 100644 src/plugins/infer3.ts diff --git a/README.md b/README.md index ebd41b6..6402234 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful pnpm experiment:infer1 pnpm experiment:infer1-direct pnpm experiment:infer2 + pnpm experiment:infer3 ### Read more @@ -59,3 +60,4 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://stackoverflow.com/questions/63944135/typescript-compiler-api-how-to-get-type-with-resolved-type-arguments - https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function - https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ +- https://github.com/itsdouges/typescript-transformer-handbook#transforms diff --git a/package.json5 b/package.json5 index 0044401..3c214e5 100644 --- a/package.json5 +++ b/package.json5 @@ -5,9 +5,10 @@ main: 'index.ts', scripts: { 'build-and-run': 'ttsc -b && node build', - 'experiment:infer1': 'ttsc -b && node build/plugins/infer.js ./src/generic/arithmetic.ts', - 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer.js ./src/experiment/arithmeticInfer1.ts', + 'experiment:infer1': 'ttsc -b && node build/plugins/infer1.js ./src/generic/arithmetic.ts', + 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', + 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ec9e4e..47c4737 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,152 +1,143 @@ -lockfileVersion: '6.0' +lockfileVersion: 5.4 -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false +specifiers: + '@types/node': 18.11.18 + reflect-metadata: 0.1.13 + source-map: ^0.7.4 + ts-node: 10.9.1 + ttypescript: 1.5.15 + typescript: 4.7.4 + typescript-rtti: 0.8.3 dependencies: - reflect-metadata: - specifier: 0.1.13 - version: 0.1.13 - source-map: - specifier: ^0.7.4 - version: 0.7.4 - typescript-rtti: - specifier: 0.8.3 - version: 0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4) + reflect-metadata: 0.1.13 + source-map: 0.7.4 + typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu devDependencies: - '@types/node': - specifier: 18.11.18 - version: 18.11.18 - ts-node: - specifier: 10.9.1 - version: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) - ttypescript: - specifier: 1.5.15 - version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) - typescript: - specifier: 4.7.4 - version: 4.7.4 + '@types/node': 18.11.18 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm + typescript: 4.7.4 packages: - /@cspotcode/source-map-support@0.8.1: + /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + /@jridgewell/resolve-uri/3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec/1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@jridgewell/trace-mapping@0.3.9: + /@jridgewell/trace-mapping/0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@tsconfig/node10@1.0.9: + /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12@1.0.11: + /@tsconfig/node12/1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14@1.0.3: + /@tsconfig/node14/1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16@1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + /@tsconfig/node16/1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node@18.11.18: + /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /acorn-walk@8.2.0: + /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + /acorn/8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg@4.1.3: + /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require@1.1.1: + /create-require/1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff@4.0.2: + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind@1.1.1: + /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has@1.0.3: + /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /is-core-module@2.11.0: - resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + /is-core-module/2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true - /make-error@1.3.6: + /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse@1.0.7: + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /reflect-metadata@0.1.13: + /reflect-metadata/0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} dev: false - /resolve@1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + /resolve/1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true dependencies: - is-core-module: 2.11.0 + is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true - /source-map@0.7.4: + /source-map/0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} dev: false - /supports-preserve-symlinks-flag@1.0.0: + /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node@10.9.1(@types/node@18.11.18)(typescript@4.7.4): + /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -164,9 +155,9 @@ packages: '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 + '@tsconfig/node16': 1.0.4 '@types/node': 18.11.18 - acorn: 8.8.2 + acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -177,19 +168,19 @@ packages: yn: 3.1.1 dev: true - /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): + /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: ts-node: '>=8.0.2' typescript: '>=3.2.2' dependencies: - resolve: 1.22.1 - ts-node: 10.9.1(@types/node@18.11.18)(typescript@4.7.4) + resolve: 1.22.4 + ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi typescript: 4.7.4 dev: true - /typescript-rtti@0.8.3(reflect-metadata@0.1.13)(typescript@4.7.4): + /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} engines: {node: '>=10'} peerDependencies: @@ -200,16 +191,16 @@ packages: typescript: 4.7.4 dev: false - /typescript@4.7.4: + /typescript/4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - /v8-compile-cache-lib@3.0.1: + /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn@3.1.1: + /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true diff --git a/src/experiment/arithmeticInfer3.js b/src/experiment/arithmeticInfer3.js new file mode 100644 index 0000000..2d0b6da --- /dev/null +++ b/src/experiment/arithmeticInfer3.js @@ -0,0 +1,16 @@ +// @ts-check + +/** + * Function square + * + * Description of function square bla bla bla + * + * @param {{ + * multiply: (a: number, b: number) => number, + * unaryMinus: (x: number) => number + * }} dep + * @return {(a: number) => number} + */ +export function square3 (dep) { + return z => dep.multiply(z, z) +} diff --git a/src/generic/infer.ts b/src/generic/infer.ts index 6e654cb..ac038dd 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -1,4 +1,4 @@ export function infer(arg: T) : T { - console.error('infer should be replace with runtime type information by a magic TypeScript plugin') + console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') return arg } diff --git a/src/plugins/infer1.ts b/src/plugins/infer1.ts index 81fa0a6..9aea0da 100644 --- a/src/plugins/infer1.ts +++ b/src/plugins/infer1.ts @@ -27,7 +27,7 @@ export function infer(sourceFile: ts.SourceFile) { // z => dep.multiply(z, z) // ) if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - console.log('dep', getType(node['type'].kind), node) + // console.log('dep', getType(node['type'].kind), node) node['type']?.members?.forEach(member => { console.log('member', { diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts new file mode 100644 index 0000000..c57fd22 --- /dev/null +++ b/src/plugins/infer3.ts @@ -0,0 +1,67 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +export function infer3(sourceFile: ts.SourceFile) { + recurse(sourceFile); + + function getType(kind: number) { + switch(kind) { + case ts.SyntaxKind.NumberKeyword: return 'number' + case ts.SyntaxKind.StringKeyword: return 'string' + case ts.SyntaxKind.BooleanKeyword: return 'boolean' + case ts.SyntaxKind.JSDoc: return 'jsdoc' + default: return String(ts.SyntaxKind[kind]) // TODO: work out all types + } + } + + function recurse(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Identifier) { + console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) + } + + if (node['jsDoc']) { + console.log('Found a JSDoc comment:') + // console.log(inspect(node['jsDoc'], { depth: null, colors: true })) + + const fullComment = sourceFile.text.slice(node.pos, node.end) + console.log(fullComment) + // TODO: next steps: + // - either get the types from the TypeScript AST, + // or extract them ourselves with regex or anything from the comment text + // - After that, we have to transform the source file and insert the comments + // as string or object that is runtime accessible in JavaScript + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) +fileNames.forEach(fileName => { + // Parse a file + const sourceFile = ts.createSourceFile( + fileName, + readFileSync(fileName).toString(), + ts.ScriptTarget.ES2022, + /*setParentNodes */ true + ); + + console.log('FILE') + console.log(fileName) + console.log() + + console.log('SOURCE') + console.log(sourceFile.text) + console.log() + + console.log('AST') + console.log(inspect(sourceFile, { depth: null, colors: true })) + console.log() + + console.log('INFER') + infer3(sourceFile); +}); + +console.log('test!!!') From 2cb8bc0099d151379e88ec2592ff68044453a609 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 18 Aug 2023 17:40:13 +0200 Subject: [PATCH 12/29] Cleanup a temporary console.log --- src/plugins/infer3.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts index c57fd22..5c599db 100644 --- a/src/plugins/infer3.ts +++ b/src/plugins/infer3.ts @@ -63,5 +63,3 @@ fileNames.forEach(fileName => { console.log('INFER') infer3(sourceFile); }); - -console.log('test!!!') From f06943ba1a47a2738999a7d75f82c6f11c8334ee Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 18 Aug 2023 10:36:11 -0700 Subject: [PATCH 13/29] chore: Update to latest TypeScript and make sure instructions work --- README.md | 1 + package.json5 | 4 +++- pnpm-lock.yaml | 32 +++++++++++++++++++++++++------- src/generic/arithmetic.ts | 2 ++ tsconfig.json | 3 ++- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d15fcc4..73af828 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,6 @@ To build and run the prototype, run: ``` npx tsc +echo '{"type": "module"}' > obj/package.json node obj ``` diff --git a/package.json5 b/package.json5 index 874d5e3..9cfd021 100644 --- a/package.json5 +++ b/package.json5 @@ -18,6 +18,8 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - typescript: '^4.9.3', + '@types/node': '^20.5.0', + 'source-map': '^0.7.4', + typescript: '^5.1.6', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095704b..682e112 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,15 +1,33 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - typescript: ^4.9.3 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false devDependencies: - typescript: 4.9.3 + '@types/node': + specifier: ^20.5.0 + version: 20.5.0 + source-map: + specifier: ^0.7.4 + version: 0.7.4 + typescript: + specifier: ^5.1.6 + version: 5.1.6 packages: - /typescript/4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} + /@types/node@20.5.0: + resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} + dev: true + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} hasBin: true dev: true diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index 78ad57d..6f4e949 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -3,3 +3,5 @@ import type {Dependencies, Signature} from '../interfaces/type.js' 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 diff --git a/tsconfig.json b/tsconfig.json index aae3a94..b45999b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ES2022", "rootDir": "./src", - "outDir": "./obj" + "outDir": "./obj", + "moduleResolution": "nodenext" } } From 76e144bc2a28d1c130b6c2a53a4ca6ec8c5e67a7 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Wed, 23 Aug 2023 03:20:10 +0000 Subject: [PATCH 14/29] feat: add build script (#13) This commit adds pnpm scripts for compiling and running the typocomath package, and a convenience script `pnpm go` that does both in succession. It also configure pnpm to use a shell emulator so that it should work on Windows as well. Finally, it changes the directory for object files from obj to build. Resolves #9. Reviewed-on: https://code.studioinfinity.org/glen/typocomath/pulls/13 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- .gitignore | 2 +- .npmrc | 1 + README.md | 11 +++++------ etc/package.json | 1 + package.json5 | 4 ++++ tsconfig.json | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .npmrc create mode 100644 etc/package.json diff --git a/.gitignore b/.gitignore index d0c9d9e..aeea740 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *~ # Typescript # emitted code -obj +build # ---> Node # Logs diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..3bd3b7d --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shell-emulator=true diff --git a/README.md b/README.md index 73af828..d626b30 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ A final (?) prototype for a refactor of mathjs, culminating the picomath, pocomath, typomath series. Provides an extensible core with "fuzzy" types for its operations, that can at any time generate exact .d.ts file for its current state. -To build and run the prototype, run: -``` -npx tsc -echo '{"type": "module"}' > obj/package.json -node obj -``` +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. diff --git a/etc/package.json b/etc/package.json new file mode 100644 index 0000000..6990891 --- /dev/null +++ b/etc/package.json @@ -0,0 +1 @@ +{"type": "module"} diff --git a/package.json5 b/package.json5 index 9cfd021..f233b62 100644 --- a/package.json5 +++ b/package.json5 @@ -5,7 +5,11 @@ main: 'index.ts', scripts: { test: 'echo "Error: no test specified" && exit 1', + build: 'tsc && cp etc/package.json build', + exec: 'node build', + go: 'pnpm --sequential "/build|exec/"', }, + packageManager: 'pnpm', keywords: [ 'math', 'algebra', diff --git a/tsconfig.json b/tsconfig.json index b45999b..13b7b6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2022", "rootDir": "./src", - "outDir": "./obj", + "outDir": "./build", "moduleResolution": "nodenext" } } From 327a9385edbdec572c2bb320530f5984c863ad35 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Wed, 23 Aug 2023 16:52:16 +0000 Subject: [PATCH 15/29] fix: Use intersection of matching types in AssociatedTypes<> lookup (#14) Resolves #11. Reviewed-on: https://code.studioinfinity.org/glen/typocomath/pulls/14 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- src/interfaces/type.ts | 10 +++++++--- src/numbers/type.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 9e6870d..9fb1163 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -13,6 +13,10 @@ * but that's OK, the generic parameter doesn't hurt in those cases. ****/ +type ValueIntersectionByKeyUnion = { + [P in TKey]: (k: T[P])=>void +} [TKey] extends ((k: infer I)=>void) ? I : never + export interface AssociatedTypes { undefined: { type: undefined @@ -24,10 +28,10 @@ export interface AssociatedTypes { } type AssociatedTypeNames = keyof AssociatedTypes['undefined'] -type ALookup = { +type ALookup = ValueIntersectionByKeyUnion<{ [K in keyof AssociatedTypes]: - T extends AssociatedTypes[K]['type'] ? AssociatedTypes[K][Name] : never -}[keyof AssociatedTypes] + T extends AssociatedTypes[K]['type'] ? AssociatedTypes[K][Name] : unknown}, + keyof AssociatedTypes> // For everything to compile, zero and one must be subtypes of T: export type ZeroType = ALookup & T diff --git a/src/numbers/type.ts b/src/numbers/type.ts index 11e5d1c..48fb95e 100644 --- a/src/numbers/type.ts +++ b/src/numbers/type.ts @@ -8,7 +8,7 @@ export const number_type = { declare module "../interfaces/type" { interface AssociatedTypes { - numbers: { + number: { type: number zero: 0 one: 1 From 180e772dab3b01ca83609aab27232c4710e171e0 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 26 Aug 2023 02:15:59 +0000 Subject: [PATCH 16/29] fix: Add types first and distinguish them semantically. (#15) Rather than via some format on the name of the identifier, this commit changes the construction of Dispatcher to assume that functions are implementations and other objects are type specifiers. Also installs all types first, before any implementations. Resolves #3. Resolves #12. Reviewed-on: https://code.studioinfinity.org/glen/typocomath/pulls/15 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- src/Complex/type.ts | 1 + src/core/Dispatcher.ts | 24 +++++++++++++----------- src/numbers/type.ts | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Complex/type.ts b/src/Complex/type.ts index 50a1314..10f76b6 100644 --- a/src/Complex/type.ts +++ b/src/Complex/type.ts @@ -6,6 +6,7 @@ import type { export type Complex = { re: T; im: T; } export const Complex_type = { + name: 'Complex', // just until we have reflection to tell us test: (dep: { testT: (z: unknown) => z is T }) => (z: unknown): z is Complex => typeof z === 'object' && z != null && 're' in z && 'im' in z diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index def1bd3..ec5491f 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -25,6 +25,7 @@ export function joinTypes(a: TypeName, b: TypeName) { // Now types used in the Dispatcher class itself type TypeSpecification = { + name: string, // just until we get reflection, then we can remove this property before?: TypeName[], test: ((x: unknown) => boolean) | ((d: DependenciesType) => (x: unknown) => boolean), @@ -53,23 +54,24 @@ export class Dispatcher { //TODO: implement me } constructor(collection: SpecificationsGroup) { + const implementations = [] for (const key in collection) { console.log('Working on', key) for (const identifier in collection[key]) { - console.log('Handling', key, ':', identifier) - const parts = identifier.split('_') - if (parts[parts.length - 1] === 'type') { - parts.pop() - const name = parts.join('_') - this.installType( - name, collection[key][identifier] as TypeSpecification) + const item = collection[key][identifier] + if (typeof item === 'function') { + implementations.push([key, identifier, item]) } else { - const name = parts[0] - this.installSpecification( - name, ['dunno'], 'unsure', {}, - collection[key][identifier] as Function) + console.log('Handling type', key, ':', identifier) + this.installType( + item.name, collection[key][identifier] as TypeSpecification) } } } + for (const trio of implementations) { + const [k, id, imp] = trio + console.log('Handling implementation', id, 'from', k) + this.installSpecification(id, ['dunno'], 'unsure', {}, imp) + } } } diff --git a/src/numbers/type.ts b/src/numbers/type.ts index 48fb95e..f234a8f 100644 --- a/src/numbers/type.ts +++ b/src/numbers/type.ts @@ -1,6 +1,7 @@ import type { Signature } from '../interfaces/type.js' export const number_type = { + name: 'number', // just until we have reflection to tell us before: ['Complex'], test: (n: unknown): n is number => typeof n === 'number', from: { string: (s: string) => +s } From dea521029e821da7a11a82bbb7585a7162e55dc1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 17:52:44 +0200 Subject: [PATCH 17/29] Get a real TypeScript plugin working --- README.md | 6 +++ package.json5 | 2 + src/experiment/arithmeticInfer4.ts | 8 ++++ src/generic/infer.ts | 5 ++ src/plugins/infer4.ts | 75 ++++++++++++++++++++++++++++++ src/plugins/myFirstPlugin.ts | 58 +++++++++++++++++++++++ tsconfig.json | 9 ++-- 7 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 src/experiment/arithmeticInfer4.ts create mode 100644 src/plugins/infer4.ts create mode 100644 src/plugins/myFirstPlugin.ts diff --git a/README.md b/README.md index 6402234..897dd0b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful pnpm experiment:infer1-direct pnpm experiment:infer2 pnpm experiment:infer3 + pnpm experiment:infer4 ### Read more @@ -61,3 +62,8 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful - https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function - https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/ - https://github.com/itsdouges/typescript-transformer-handbook#transforms + +### Interesting libraries + +- https://github.com/GoogleFeud/ts-macros/ +- https://ts-morph.com diff --git a/package.json5 b/package.json5 index 3c214e5..1f3d6e0 100644 --- a/package.json5 +++ b/package.json5 @@ -9,6 +9,8 @@ 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', + 'experiment:infer4': 'ttsc -b && node build/plugins/infer4.js ./src/experiment/arithmeticInfer4.ts', + 'experiment:infer4:plugin': 'ttsc -b', test: 'echo "Error: no test specified" && exit 1', }, keywords: [ diff --git a/src/experiment/arithmeticInfer4.ts b/src/experiment/arithmeticInfer4.ts new file mode 100644 index 0000000..dc2f45a --- /dev/null +++ b/src/experiment/arithmeticInfer4.ts @@ -0,0 +1,8 @@ +import { typed } from '../generic/infer' + +export const square = typed('__infer__', (dep: { + multiply: (a: T, b: T) => T, + unaryMinus: (x: T) => T, // just for the experiment +}): (a: T) => T => + z => dep.multiply(z, z) +) diff --git a/src/generic/infer.ts b/src/generic/infer.ts index ac038dd..92db72d 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -2,3 +2,8 @@ export function infer(arg: T) : T { console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') return arg } + +export function typed(dep: string, arg: T) : T { + console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') + return arg +} diff --git a/src/plugins/infer4.ts b/src/plugins/infer4.ts new file mode 100644 index 0000000..c5e7834 --- /dev/null +++ b/src/plugins/infer4.ts @@ -0,0 +1,75 @@ +import { readFileSync } from "fs"; +import * as ts from "typescript"; +import { inspect } from 'util' + +export function infer(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + recurse(sourceFile); + + function recurse(node: ts.Node) { + if (ts.isCallExpression(node)) { + if (ts.isIdentifier(node.expression) && node.expression.escapedText === 'infer') { + const argNode = node.arguments[0] + + if (ts.isArrowFunction(argNode)) { + const text = sourceFile.text.slice(argNode.pos, argNode.end) + console.log('infer', text) + + console.log('AST') + console.log(node) + console.log() + + const returnType = argNode.type.getText(sourceFile) + console.log('returnType', returnType) + // (a: number) => number + + const paramNode = argNode.parameters[0] + + const paramType = paramNode.type.getText(sourceFile) + console.log('paramType', paramType) // (a: number) => number + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } + + const type = typeChecker.getTypeAtLocation(paramNode) + const typeStr = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('paramTypeString', typeStr) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + } + + } + } + + ts.forEachChild(node, recurse); + } +} + +const fileNames = process.argv.slice(2); +console.log('infer files', fileNames) + +const options = { + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.ES2022 +} +let program = ts.createProgram(fileNames, options); +const checker = program.getTypeChecker() + +for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + console.log('FILE') + console.log(sourceFile.fileName) + console.log() + + console.log('SOURCE') + console.log(sourceFile.text) + console.log() + + // console.log('AST') + // console.log(inspect(sourceFile, { depth: null, colors: true })) + // console.log() + + console.log('INFER') + infer(sourceFile, checker); + + } +} diff --git a/src/plugins/myFirstPlugin.ts b/src/plugins/myFirstPlugin.ts new file mode 100644 index 0000000..ba2feb6 --- /dev/null +++ b/src/plugins/myFirstPlugin.ts @@ -0,0 +1,58 @@ +import * as ts from 'typescript'; + +const transformer: ts.TransformerFactory = context => { + // TODO: get a reference to the program instance that the plugin is running in instead of creating a new program? + const program = ts.createProgram([], {}) + const checker = program.getTypeChecker() + + return sourceFile => { + // For the experiment we only want to influence a single file + if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) { + return sourceFile + } + + const visitor = (node: ts.Node): ts.Node => { + // @ts-ignore + if (ts.isStringLiteral(node) && node.text === '__infer__') { + console.log('STRING LITERAL', node.text) + console.log(node) + + const parentNode = node.parent + console.log('PARENT') + console.log(parentNode) + + // TODO: validate that the parent is indeed a mathjs typed function with deps + + // @ts-ignore + const argNode = parentNode.arguments[1] + const returnType = argNode.type.getText(sourceFile) + console.log('RETURN TYPE') + console.log(returnType) + // (a: number) => number + + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } + + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` + + return ts.factory.createStringLiteral(depsAndReturnType) + } + + return ts.visitEachChild(node, visitor, context); + }; + + return ts.visitNode(sourceFile, visitor); + }; +}; + +export default transformer; diff --git a/tsconfig.json b/tsconfig.json index c761525..50df804 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,10 +8,9 @@ "noImplicitAny": false, "moduleResolution": "Node", "module": "commonjs", - "plugins": [ - { - "transform": "typescript-rtti/dist/transformer" - } - ] + "plugins": [{ + "transform": "./src/plugins/myFirstPlugin.ts", + "type": "raw" + }] } } From cbb79d46fe07a5fd038423e890d0abba8b0bbac1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:21:45 +0200 Subject: [PATCH 18/29] cleanup old experiments and typescript-rtti, update readme --- README.md | 42 ++----- package.json5 | 14 +-- pnpm-lock.yaml | 44 ++----- src/core/Dispatcher.ts | 9 +- ...arithmeticInfer4.ts => arithmeticInfer.ts} | 2 +- src/experiment/arithmeticInfer1.ts | 8 -- src/experiment/arithmeticInfer2.ts | 15 --- src/experiment/arithmeticInfer3.js | 16 --- src/generic/infer.ts | 9 +- src/index.ts | 113 ++++-------------- src/plugins/infer1.ts | 73 ----------- src/plugins/infer2.ts | 74 ------------ src/plugins/infer3.ts | 65 ---------- src/plugins/infer4.ts | 75 ------------ .../{myFirstPlugin.ts => typeInferPlugin.ts} | 25 ++-- tsconfig.json | 2 +- 16 files changed, 68 insertions(+), 518 deletions(-) rename src/experiment/{arithmeticInfer4.ts => arithmeticInfer.ts} (73%) delete mode 100644 src/experiment/arithmeticInfer1.ts delete mode 100644 src/experiment/arithmeticInfer2.ts delete mode 100644 src/experiment/arithmeticInfer3.js delete mode 100644 src/plugins/infer1.ts delete mode 100644 src/plugins/infer2.ts delete mode 100644 src/plugins/infer3.ts delete mode 100644 src/plugins/infer4.ts rename src/plugins/{myFirstPlugin.ts => typeInferPlugin.ts} (79%) diff --git a/README.md b/README.md index 897dd0b..a7bf7e4 100644 --- a/README.md +++ b/README.md @@ -11,46 +11,28 @@ pnpm build-and-run ## experiment -See: the section under `/src/experiment` and `/src/plugins`. +Have a look at the section under `/src/experiment` and `/src/plugins`: + +- `src/plugins/typeInferPlugin.ts` is the actual plugin +- in `tsconfig.json` we configure TypeScript to run the plugin +- `src/experiment/arithmeticInfer.ts` with an example where we define `__infer__` +- `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replace with the actual types ### The idea -Create a TypeScript plugin which can replace structures like: +Create a TypeScript plugin which can replace a string literal like `__infer__` in a typed-function definition: - infer(factoryFunction) + typed('square', '__infer__', (dep: { ... } => { ... }) -where `factoryFunction` is a mathjs factory function in TypeScript, with something like: - - infer({ signature: factoryFunction }) - -where `signature` is a string containing the type of the factory function and its dependencies. - -Relevant methods of the TypeScript compiler are: +with the actual types, something like: -```ts -const program = ts.createProgram(fileNames, options) -const typeChecker = program.getTypeChecker() - -// relevant methods: -// -// typeChecker.getSymbolAtLocation -// typeChecker.getTypeOfSymbolAtLocation -// typeChecker.getResolvedSignature -// typeChecker.getSignaturesOfType -``` - -### Status - -None of the experiments (`infer1` and `infer2`) are outputting something useful yet. + typed('square', '{ deps: { multiply: (a: T, b: T) => T; }; return: (a: T) => T }', (dep: { ... } => { ... }) +(We can discuss what syntax we like most, this is just a POC) ### How to run - pnpm experiment:infer1 - pnpm experiment:infer1-direct - pnpm experiment:infer2 - pnpm experiment:infer3 - pnpm experiment:infer4 + pnpm build-and-run ### Read more diff --git a/package.json5 b/package.json5 index 1f3d6e0..02bb592 100644 --- a/package.json5 +++ b/package.json5 @@ -4,14 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && node build', - 'experiment:infer1': 'ttsc -b && node build/plugins/infer1.js ./src/generic/arithmetic.ts', - 'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts', - 'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts', - 'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js', - 'experiment:infer4': 'ttsc -b && node build/plugins/infer4.js ./src/experiment/arithmeticInfer4.ts', - 'experiment:infer4:plugin': 'ttsc -b', - test: 'echo "Error: no test specified" && exit 1', + 'build-and-run': 'ttsc -b && node build/index.js', }, keywords: [ 'math', @@ -25,12 +18,9 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, dependencies: { - 'reflect-metadata': '0.1.13', - 'source-map': '^0.7.4', - 'typescript-rtti': '0.8.3', + '@types/node': '20.5.7', }, devDependencies: { - '@types/node': '18.11.18', 'ts-node': '10.9.1', ttypescript: '1.5.15', typescript: '4.7.4', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47c4737..fdeb100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,22 +1,16 @@ lockfileVersion: 5.4 specifiers: - '@types/node': 18.11.18 - reflect-metadata: 0.1.13 - source-map: ^0.7.4 + '@types/node': 20.5.7 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 - typescript-rtti: 0.8.3 dependencies: - reflect-metadata: 0.1.13 - source-map: 0.7.4 - typescript-rtti: 0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu + '@types/node': 20.5.7 devDependencies: - '@types/node': 18.11.18 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm typescript: 4.7.4 @@ -61,9 +55,8 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node/18.11.18: - resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} - dev: true + /@types/node/20.5.7: + resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==} /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} @@ -114,10 +107,6 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /reflect-metadata/0.1.13: - resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} - dev: false - /resolve/1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -127,17 +116,12 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /source-map/0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - dev: false - /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_nv75g3i7xuh23du6z7qul3uiqi: + /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -156,7 +140,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.11.18 + '@types/node': 20.5.7 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -176,25 +160,15 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.4 - ts-node: 10.9.1_nv75g3i7xuh23du6z7qul3uiqi + ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha typescript: 4.7.4 dev: true - /typescript-rtti/0.8.3_qh5shpxvbkbt6m3jqtkkp2svgu: - resolution: {integrity: sha512-uX1A0JKs1o/ptLJqkubRCGgN7NOCYSTKRXyRIjG80exsLrPDq4jJWMfQxlHMAcv/zjoX0V6iIGU7bwjGWTzpLg==} - engines: {node: '>=10'} - peerDependencies: - reflect-metadata: ^0.1.13 - typescript: ^4.5 || ^4.6 || ^4.7 - dependencies: - reflect-metadata: 0.1.13 - typescript: 4.7.4 - dev: false - /typescript/4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index 1da89ff..4ba58fa 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -1,6 +1,3 @@ -import "reflect-metadata" -import { reflect, type CallSite } from 'typescript-rtti' - /* A Dispatcher is a collection of operations that do run-time * dispatch on the types of their arguments. Thus, every individual * method is like a typed-function (from the library by that name), @@ -51,15 +48,15 @@ export class Dispatcher { console.log('Pretending to install', name, signature, '=>', returns) // @ts-ignore - console.log(name, 'signature', reflect(signature)) - console.log(name, 'dependencies', reflect(dependencies)) + // console.log(name, 'signature', reflect(signature)) + // console.log(name, 'dependencies', reflect(dependencies)) //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) { for (const key in collection) { console.log('Working on', key) for (const identifier in collection[key]) { diff --git a/src/experiment/arithmeticInfer4.ts b/src/experiment/arithmeticInfer.ts similarity index 73% rename from src/experiment/arithmeticInfer4.ts rename to src/experiment/arithmeticInfer.ts index dc2f45a..e17ac6b 100644 --- a/src/experiment/arithmeticInfer4.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ import { typed } from '../generic/infer' -export const square = typed('__infer__', (dep: { +export const square = typed('square', '__infer__', (dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/experiment/arithmeticInfer1.ts b/src/experiment/arithmeticInfer1.ts deleted file mode 100644 index 92aeffd..0000000 --- a/src/experiment/arithmeticInfer1.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {infer} from '../generic/infer' - -export const square = infer((dep: { - multiply: (a: number, b: number) => number, - unaryMinus: (x: number) => number, // just for the experiment -}): (a: number) => number => - z => dep.multiply(z, z) -) diff --git a/src/experiment/arithmeticInfer2.ts b/src/experiment/arithmeticInfer2.ts deleted file mode 100644 index ab4eebf..0000000 --- a/src/experiment/arithmeticInfer2.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { infer } from '../generic/infer' -import { Dependencies, Signature } from '../interfaces/type' - -export type multiplyDep = Dependencies<'multiply', T> - -export const square1 = - (dep: Dependencies<'multiply', T>): Signature<'square', T> => - z => dep.multiply(z, z) - -export const square2 = infer((dep: { - multiply: (a: number, b: number) => number, - unaryMinus: (x: number) => number, // just for the experiment -}): (a: number) => number => - z => dep.multiply(z, z) -) diff --git a/src/experiment/arithmeticInfer3.js b/src/experiment/arithmeticInfer3.js deleted file mode 100644 index 2d0b6da..0000000 --- a/src/experiment/arithmeticInfer3.js +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-check - -/** - * Function square - * - * Description of function square bla bla bla - * - * @param {{ - * multiply: (a: number, b: number) => number, - * unaryMinus: (x: number) => number - * }} dep - * @return {(a: number) => number} - */ -export function square3 (dep) { - return z => dep.multiply(z, z) -} diff --git a/src/generic/infer.ts b/src/generic/infer.ts index 92db72d..4fa9279 100644 --- a/src/generic/infer.ts +++ b/src/generic/infer.ts @@ -3,7 +3,12 @@ export function infer(arg: T) : T { return arg } -export function typed(dep: string, arg: T) : T { - console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') +export function typed(name: string, types: string, arg: T) : T { + // TODO: implement typed-function for real + if (types === '__infer__') { + console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') + } + + console.log(`Creating typed-function "${name}" with types ${types}`) return arg } diff --git a/src/index.ts b/src/index.ts index ce04c21..9fd1e68 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,99 +1,28 @@ -import 'reflect-metadata' +import { Complex } from './Complex/type.js' +import { absquare as absquare_complex } from './Complex/arithmetic.js' +import { square } from './experiment/arithmeticInfer.js' -import {Dispatcher} from './core/Dispatcher.js' -import * as specifications from './all.js' -import {Complex} from './Complex/type.js' -import {absquare as absquare_complex} from './Complex/arithmetic.js' - -import { CallSite, ReflectedObjectRef, reflect } from 'typescript-rtti' -import { square } from './generic/arithmetic.js' - -// verify that typescript-rtti works (just as experiment) -const add = (a: number, b: number): number => a + b -console.log('reflect add') -console.log('parameterNames', reflect(add).parameterNames) -console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString())) -console.log('returnType', reflect(add).returnType.toString()) -console.log() -// output: -// reflect function add -// parameterNames [ 'a', 'b' ] -// parameterTypes [ 'class Number', 'class Number' ] -// returnType class Number - -// try out a very simple case (just as experiment) -function createSquare(deps: { - multiply: (a: number, b: number) => number, - subtract: (a: number, b: number) => number -}) { - return (a: number) => deps.multiply(a, a) -} -console.log('reflect createSquare') -console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name)) -// console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter)) -// @ts-ignore -console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m) -console.log('parameterTypes[0].ref.m[0]', - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n, - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0] -) -console.log('parameterTypes[0].ref.m[0].t.m', - // @ts-ignore - (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].t.m -) -// @ts-ignore -// console.log('parameters[0]', reflect(createSquare).parameters[0]) -// FIXME: where to find the information of the types of the dependencies multiply and subtract? - -// Test whether we loose the type information when casting to a generic interface -// Conclusion: we keep the information, that is good. -console.log() -console.log('reflect createFunction') -type MathjsDependencies = Record -type MathjsCreateFunction = (deps: MathjsDependencies) => Function -const createFunction: MathjsCreateFunction = createSquare as MathjsCreateFunction -console.log('parameter names', reflect(createFunction).parameters.map(parameter => parameter.name)) -// @ts-ignore -console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m) - -// TODO: more specific definition of Specifications -type Specifications = Record> - -console.log() -console.log('CallSite') -function reflectSpecifications(specifications: Specifications, callSite? : CallSite) { - console.log('specifications', reflect(callSite).parameters[0]) - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref) // shows 'numbers', 'Complex, 'complex', 'generic' - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref.m - .find(item => item.n === 'generic').t.m) // shows 'square', 'unequal' - // @ts-ignore - console.log('specifications', reflect(callSite).parameters[0]._ref.m - .find(item => item.n === 'generic').t.m - .find(item => item.n === 'square').t.p) // [ { n: 'dep', t: [Function: t], b: undefined, v: null } ] - // @ts-ignore - // FIXME: now, we should be able to get the signature of the multiply dependency of the function square, but how? -} -reflectSpecifications(specifications); - - -// TODO: import all specifications (turned off for debugging purposes) -// export default new Dispatcher(Specifications) - - -const mockRealAdd = (a: number, b: number) => a+b -const mockComplexAbsquare = (z: Complex) => z.re*z.re + z.im*z.im +const add = (a: number, b: number) => a + b +const multiply = (a: number, b: number) => a * b +const unaryMinus = (a: number) => -a +const absquare = (z: Complex) => z.re * z.re + z.im * z.im const quatAbsquare = absquare_complex({ - add: mockRealAdd, - absquare: mockComplexAbsquare + add, + absquare }) -const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) -const typeTest: typeof myabs = 7 // check myabs is just a number +const result = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}}) +const typeTest: typeof result = 7 // check myabs is just a number console.log() -console.log('Result is', myabs) +console.log('Result is', result) + + +const mySquare = square({ + multiply, + unaryMinus +}) + +console.log() +console.log('mySquare(4)=', mySquare(4)) diff --git a/src/plugins/infer1.ts b/src/plugins/infer1.ts deleted file mode 100644 index 9aea0da..0000000 --- a/src/plugins/infer1.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer(sourceFile: ts.SourceFile) { - recurse(sourceFile); - - function getType(kind: number) { - switch(kind) { - case ts.SyntaxKind.NumberKeyword: return 'number' - case ts.SyntaxKind.StringKeyword: return 'string' - case ts.SyntaxKind.BooleanKeyword: return 'boolean' - default: return String(ts.SyntaxKind[kind]) // TODO: work out all types - } - } - - function recurse(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { - console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) - } - - // recognize a structure like: - // - // export const square = infer((dep: { - // multiply: (a: number, b: number) => number - // }): (a: number) => number => - // z => dep.multiply(z, z) - // ) - if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - // console.log('dep', getType(node['type'].kind), node) - - node['type']?.members?.forEach(member => { - console.log('member', { - name: member.name.escapedText, - parameters: member.type.parameters.map(parameter => { - return parameter.name.escapedText + ': ' + getType(parameter.type.kind) - }), - returns: getType(member.type.type.kind) - }) - }) - } - - // recognize a structure like: - // - // export const square = - // (dep: Dependencies<'multiply' | 'unaryMinus', T>): Signature<'square', T> => - // z => dep.multiply(z, z) - if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') { - // TODO - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) -fileNames.forEach(fileName => { - // Parse a file - const sourceFile = ts.createSourceFile( - fileName, - readFileSync(fileName).toString(), - ts.ScriptTarget.ES2022, - /*setParentNodes */ true - ); - - console.log('AST', fileName, inspect(sourceFile, { depth: null, colors: true })) - - console.log(sourceFile.text) - console.log() - - infer(sourceFile); -}); diff --git a/src/plugins/infer2.ts b/src/plugins/infer2.ts deleted file mode 100644 index 43fc64b..0000000 --- a/src/plugins/infer2.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as ts from "typescript" - -// based on: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker - -infer2(process.argv.slice(2), { - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.CommonJS -}) - -function infer2( - fileNames: string[], - options: ts.CompilerOptions -): void { - const program = ts.createProgram(fileNames, options) - const typeChecker = program.getTypeChecker() - - for (const sourceFile of program.getSourceFiles()) { - if (!sourceFile.isDeclarationFile) { - ts.forEachChild(sourceFile, visit) - } - } - - return - - function visit(node: ts.Node) { - // // Only consider exported nodes - // if (!isNodeExported(node)) { - // return; - // } - - // console.log('Node', node.kind, node?.['name']?.escapedText) - - if (ts.isModuleDeclaration(node)) { - // This is a namespace, visit its children - console.log('check') - ts.forEachChild(node, visit); - } else if (ts.isTypeAliasDeclaration(node)) { - console.log('isTypeAliasDeclaration', node.name.escapedText) - - let symbol = typeChecker.getSymbolAtLocation(node.name); - if (symbol) { - const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) - const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) - - // checker.getResolvedSignature(symbol) - console.log('symbol', symbol.getName(), symbolSignature) - - // getTypeOfSymbolAtLocation - // getResolvedSignature - } - } else if (ts.isCallExpression(node)) { - console.log('isCallExpression', node.expression) - } else if (ts.isFunctionDeclaration(node)) { - console.log('isFunctionDeclaration', node.name.escapedText, { typeParameter0: node.typeParameters[0] }) - - if (node.name.escapedText === 'infer') { - const param0 = node.typeParameters[0] - if (ts.isPropertyDeclaration(param0)) { - const symbol = typeChecker.getSymbolAtLocation(param0) - - // TODO: get resolving - - // console.log('getResolvedSignature', typeChecker.getResolvedSignature(node) ) - - // const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) - // const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call) - // console.log('symbol', symbol.getName(), symbolSignature) - - // console.log('getSignaturesOfType', typeChecker.getSignaturesOfType(param0) - } - } - } - } -} diff --git a/src/plugins/infer3.ts b/src/plugins/infer3.ts deleted file mode 100644 index 5c599db..0000000 --- a/src/plugins/infer3.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer3(sourceFile: ts.SourceFile) { - recurse(sourceFile); - - function getType(kind: number) { - switch(kind) { - case ts.SyntaxKind.NumberKeyword: return 'number' - case ts.SyntaxKind.StringKeyword: return 'string' - case ts.SyntaxKind.BooleanKeyword: return 'boolean' - case ts.SyntaxKind.JSDoc: return 'jsdoc' - default: return String(ts.SyntaxKind[kind]) // TODO: work out all types - } - } - - function recurse(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { - console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind]) - } - - if (node['jsDoc']) { - console.log('Found a JSDoc comment:') - // console.log(inspect(node['jsDoc'], { depth: null, colors: true })) - - const fullComment = sourceFile.text.slice(node.pos, node.end) - console.log(fullComment) - // TODO: next steps: - // - either get the types from the TypeScript AST, - // or extract them ourselves with regex or anything from the comment text - // - After that, we have to transform the source file and insert the comments - // as string or object that is runtime accessible in JavaScript - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) -fileNames.forEach(fileName => { - // Parse a file - const sourceFile = ts.createSourceFile( - fileName, - readFileSync(fileName).toString(), - ts.ScriptTarget.ES2022, - /*setParentNodes */ true - ); - - console.log('FILE') - console.log(fileName) - console.log() - - console.log('SOURCE') - console.log(sourceFile.text) - console.log() - - console.log('AST') - console.log(inspect(sourceFile, { depth: null, colors: true })) - console.log() - - console.log('INFER') - infer3(sourceFile); -}); diff --git a/src/plugins/infer4.ts b/src/plugins/infer4.ts deleted file mode 100644 index c5e7834..0000000 --- a/src/plugins/infer4.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { readFileSync } from "fs"; -import * as ts from "typescript"; -import { inspect } from 'util' - -export function infer(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { - recurse(sourceFile); - - function recurse(node: ts.Node) { - if (ts.isCallExpression(node)) { - if (ts.isIdentifier(node.expression) && node.expression.escapedText === 'infer') { - const argNode = node.arguments[0] - - if (ts.isArrowFunction(argNode)) { - const text = sourceFile.text.slice(argNode.pos, argNode.end) - console.log('infer', text) - - console.log('AST') - console.log(node) - console.log() - - const returnType = argNode.type.getText(sourceFile) - console.log('returnType', returnType) - // (a: number) => number - - const paramNode = argNode.parameters[0] - - const paramType = paramNode.type.getText(sourceFile) - console.log('paramType', paramType) // (a: number) => number - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } - - const type = typeChecker.getTypeAtLocation(paramNode) - const typeStr = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('paramTypeString', typeStr) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - } - - } - } - - ts.forEachChild(node, recurse); - } -} - -const fileNames = process.argv.slice(2); -console.log('infer files', fileNames) - -const options = { - target: ts.ScriptTarget.ES2022, - module: ts.ModuleKind.ES2022 -} -let program = ts.createProgram(fileNames, options); -const checker = program.getTypeChecker() - -for (const sourceFile of program.getSourceFiles()) { - if (!sourceFile.isDeclarationFile) { - console.log('FILE') - console.log(sourceFile.fileName) - console.log() - - console.log('SOURCE') - console.log(sourceFile.text) - console.log() - - // console.log('AST') - // console.log(inspect(sourceFile, { depth: null, colors: true })) - // console.log() - - console.log('INFER') - infer(sourceFile, checker); - - } -} diff --git a/src/plugins/myFirstPlugin.ts b/src/plugins/typeInferPlugin.ts similarity index 79% rename from src/plugins/myFirstPlugin.ts rename to src/plugins/typeInferPlugin.ts index ba2feb6..0cf6d6f 100644 --- a/src/plugins/myFirstPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import ts from 'typescript' const transformer: ts.TransformerFactory = context => { // TODO: get a reference to the program instance that the plugin is running in instead of creating a new program? @@ -7,24 +7,23 @@ const transformer: ts.TransformerFactory = context => { return sourceFile => { // For the experiment we only want to influence a single file - if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) { + if (!sourceFile.fileName.endsWith('arithmeticInfer.ts')) { return sourceFile } const visitor = (node: ts.Node): ts.Node => { // @ts-ignore if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('STRING LITERAL', node.text) - console.log(node) + console.log('FOUND AN OCCURRENCE OF __infer__') const parentNode = node.parent - console.log('PARENT') - console.log(parentNode) + // console.log('PARENT') + // console.log(parentNode) // TODO: validate that the parent is indeed a mathjs typed function with deps // @ts-ignore - const argNode = parentNode.arguments[1] + const argNode = parentNode.arguments[2] const returnType = argNode.type.getText(sourceFile) console.log('RETURN TYPE') console.log(returnType) @@ -48,11 +47,11 @@ const transformer: ts.TransformerFactory = context => { return ts.factory.createStringLiteral(depsAndReturnType) } - return ts.visitEachChild(node, visitor, context); - }; + return ts.visitEachChild(node, visitor, context) + } - return ts.visitNode(sourceFile, visitor); - }; -}; + return ts.visitNode(sourceFile, visitor) + } +} -export default transformer; +export default transformer diff --git a/tsconfig.json b/tsconfig.json index 50df804..c6b3a50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "moduleResolution": "Node", "module": "commonjs", "plugins": [{ - "transform": "./src/plugins/myFirstPlugin.ts", + "transform": "./src/plugins/typeInferPlugin.ts", "type": "raw" }] } From d50c1a9ccf006ed5227bc8c8046b0a924cb8c7cf Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:36:50 +0200 Subject: [PATCH 19/29] some refinements in the plugin --- README.md | 2 +- src/{generic/infer.ts => core/typed.ts} | 7 +-- src/experiment/arithmeticInfer.ts | 2 +- src/plugins/typeInferPlugin.ts | 57 ++++++++++++------------- 4 files changed, 31 insertions(+), 37 deletions(-) rename src/{generic/infer.ts => core/typed.ts} (54%) diff --git a/README.md b/README.md index a7bf7e4..968cf51 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Have a look at the section under `/src/experiment` and `/src/plugins`: - `src/plugins/typeInferPlugin.ts` is the actual plugin - in `tsconfig.json` we configure TypeScript to run the plugin - `src/experiment/arithmeticInfer.ts` with an example where we define `__infer__` -- `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replace with the actual types +- after running TypeScript: look at `build/experiment/arithmeticInfer.ts` where the `__infer__` string literal is replaced with the actual types ### The idea diff --git a/src/generic/infer.ts b/src/core/typed.ts similarity index 54% rename from src/generic/infer.ts rename to src/core/typed.ts index 4fa9279..f66a4ad 100644 --- a/src/generic/infer.ts +++ b/src/core/typed.ts @@ -1,14 +1,9 @@ -export function infer(arg: T) : T { - console.error('infer should be replaced with runtime type information by a magic TypeScript plugin') - return arg -} - export function typed(name: string, types: string, arg: T) : T { // TODO: implement typed-function for real if (types === '__infer__') { console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') } - console.log(`Creating typed-function "${name}" with types ${types}`) + console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) return arg } diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index e17ac6b..be544a3 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,4 +1,4 @@ -import { typed } from '../generic/infer' +import { typed } from '../core/typed.js' export const square = typed('square', '__infer__', (dep: { multiply: (a: T, b: T) => T, diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 0cf6d6f..4ca6c21 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -6,45 +6,44 @@ const transformer: ts.TransformerFactory = context => { const checker = program.getTypeChecker() return sourceFile => { - // For the experiment we only want to influence a single file - if (!sourceFile.fileName.endsWith('arithmeticInfer.ts')) { - return sourceFile - } - const visitor = (node: ts.Node): ts.Node => { // @ts-ignore if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('FOUND AN OCCURRENCE OF __infer__') + console.log('PLUGIN: FOUND AN OCCURRENCE OF __infer__') + // we're looking for a function call like typed('name', '__infer__', deps => ...) const parentNode = node.parent - // console.log('PARENT') - // console.log(parentNode) + if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === 'typed') { + // console.log('PARENT') + // console.log(parentNode) - // TODO: validate that the parent is indeed a mathjs typed function with deps + // TODO: validate that argNode is an ArrowFunction + // @ts-ignore + const argNode = parentNode.arguments[2] + // @ts-ignore + const returnType = argNode.type.getText(sourceFile) + console.log('PLUGIN: RETURN TYPE') + console.log(returnType) + // (a: number) => number - // @ts-ignore - const argNode = parentNode.arguments[2] - const returnType = argNode.type.getText(sourceFile) - console.log('RETURN TYPE') - console.log(returnType) - // (a: number) => number + // @ts-ignore + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } - const paramNode = argNode.parameters[0] - const paramTypeSrc = paramNode.type.getText(sourceFile) - console.log('PARAM TYPE SRC', paramTypeSrc) - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('PARAM TYPE STRING', paramType) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - - return ts.factory.createStringLiteral(depsAndReturnType) + return ts.factory.createStringLiteral(depsAndReturnType) + } } return ts.visitEachChild(node, visitor, context) From 3967f5ce2b12717f42c3b5845d14deab91942ab7 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:43:03 +0200 Subject: [PATCH 20/29] let TypeScript output ES modules --- package.json5 | 2 +- tsconfig.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json5 b/package.json5 index 02bb592..4e03b9b 100644 --- a/package.json5 +++ b/package.json5 @@ -4,7 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && node build/index.js', + 'build-and-run': 'ttsc -b && echo {"type":"module"} > build/package.json && node build/index.js', }, keywords: [ 'math', diff --git a/tsconfig.json b/tsconfig.json index c6b3a50..eed8f17 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "allowJs": false, "noImplicitAny": false, "moduleResolution": "Node", - "module": "commonjs", "plugins": [{ "transform": "./src/plugins/typeInferPlugin.ts", "type": "raw" From 26631febf905cca9a257131c0a8f5589143ad641 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 20:54:39 +0200 Subject: [PATCH 21/29] add a TODO explaining the plan --- src/core/typed.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/typed.ts b/src/core/typed.ts index f66a4ad..3306776 100644 --- a/src/core/typed.ts +++ b/src/core/typed.ts @@ -5,5 +5,7 @@ export function typed(name: string, types: string, arg: T) : T { } console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) + + // TODO: here we can now turn the inputs into a real typed-function with a signature return arg } From 7fc9d2a2f360742c262934ed0443d08a54502d16 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 8 Sep 2023 14:10:03 +0200 Subject: [PATCH 22/29] remove name argument from the reflect function --- src/core/{typed.ts => $reflect.ts} | 4 ++-- src/experiment/arithmeticInfer.ts | 4 ++-- src/plugins/typeInferPlugin.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/core/{typed.ts => $reflect.ts} (65%) diff --git a/src/core/typed.ts b/src/core/$reflect.ts similarity index 65% rename from src/core/typed.ts rename to src/core/$reflect.ts index 3306776..abd069d 100644 --- a/src/core/typed.ts +++ b/src/core/$reflect.ts @@ -1,10 +1,10 @@ -export function typed(name: string, types: string, arg: T) : T { +export function $reflect(types: string, arg: T) : T { // TODO: implement typed-function for real if (types === '__infer__') { console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') } - console.log(`TYPED-FUNCTION: Creating function "${name}" with types ${types}`) + console.log(`INFER: Creating function with types ${types}`) // TODO: here we can now turn the inputs into a real typed-function with a signature return arg diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index be544a3..c36ed92 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ -import { typed } from '../core/typed.js' +import { $reflect } from '../core/$reflect.js' -export const square = typed('square', '__infer__', (dep: { +export const square = $reflect('__infer__', (dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 4ca6c21..08a19ae 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -13,13 +13,13 @@ const transformer: ts.TransformerFactory = context => { // we're looking for a function call like typed('name', '__infer__', deps => ...) const parentNode = node.parent - if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === 'typed') { + if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === '$reflect') { // console.log('PARENT') // console.log(parentNode) // TODO: validate that argNode is an ArrowFunction // @ts-ignore - const argNode = parentNode.arguments[2] + const argNode = parentNode.arguments[1] // @ts-ignore const returnType = argNode.type.getText(sourceFile) console.log('PLUGIN: RETURN TYPE') From 232d5d4a96b3d9face1ba428224d631d28a44330 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 8 Sep 2023 14:25:14 +0200 Subject: [PATCH 23/29] make defining `__infer__` redundant --- src/core/$reflect.ts | 6 ++-- src/experiment/arithmeticInfer.ts | 2 +- src/plugins/typeInferPlugin.ts | 60 +++++++++++++++---------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/core/$reflect.ts b/src/core/$reflect.ts index abd069d..ca4f920 100644 --- a/src/core/$reflect.ts +++ b/src/core/$reflect.ts @@ -1,7 +1,7 @@ -export function $reflect(types: string, arg: T) : T { +export function $reflect(arg: T, types?: string) : T { // TODO: implement typed-function for real - if (types === '__infer__') { - console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin') + if (!types) { + console.error('types should be resolved with runtime type information by the TypeScript plugin') } console.log(`INFER: Creating function with types ${types}`) diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index c36ed92..4a4a6e6 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,6 +1,6 @@ import { $reflect } from '../core/$reflect.js' -export const square = $reflect('__infer__', (dep: { +export const square = $reflect((dep: { multiply: (a: T, b: T) => T, unaryMinus: (x: T) => T, // just for the experiment }): (a: T) => T => diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 08a19ae..84c0dcf 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -7,43 +7,43 @@ const transformer: ts.TransformerFactory = context => { return sourceFile => { const visitor = (node: ts.Node): ts.Node => { + // we're looking for a function call like $reflect(deps => ...) // @ts-ignore - if (ts.isStringLiteral(node) && node.text === '__infer__') { - console.log('PLUGIN: FOUND AN OCCURRENCE OF __infer__') + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.escapedText === '$reflect') { + console.log('PLUGIN: FOUND AN OCCURRENCE OF $reflect') + // console.log('PARENT') + // console.log(node) - // we're looking for a function call like typed('name', '__infer__', deps => ...) - const parentNode = node.parent - if (ts.isCallExpression(parentNode) && ts.isIdentifier(parentNode.expression) && parentNode.expression.escapedText === '$reflect') { - // console.log('PARENT') - // console.log(parentNode) + // TODO: validate that argNode is an ArrowFunction + // @ts-ignore + const argNode = node.arguments[0] + // @ts-ignore + const returnType = argNode.type.getText(sourceFile) + console.log('PLUGIN: RETURN TYPE') + console.log(returnType) + // (a: number) => number - // TODO: validate that argNode is an ArrowFunction - // @ts-ignore - const argNode = parentNode.arguments[1] - // @ts-ignore - const returnType = argNode.type.getText(sourceFile) - console.log('PLUGIN: RETURN TYPE') - console.log(returnType) - // (a: number) => number + // @ts-ignore + const paramNode = argNode.parameters[0] + const paramTypeSrc = paramNode.type.getText(sourceFile) + console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) + // { + // multiply: (a: number, b: number) => number, + // unaryMinus: (x: number) => number, // just for the experiment + // } - // @ts-ignore - const paramNode = argNode.parameters[0] - const paramTypeSrc = paramNode.type.getText(sourceFile) - console.log('PLUGIN: PARAM TYPE SRC', paramTypeSrc) - // { - // multiply: (a: number, b: number) => number, - // unaryMinus: (x: number) => number, // just for the experiment - // } + const type = checker.getTypeAtLocation(paramNode) + const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: PARAM TYPE STRING', paramType) + // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } - const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) - console.log('PLUGIN: PARAM TYPE STRING', paramType) - // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` - const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` + // Now we insert a second argument to the $reflect function call: a string with the types + // @ts-ignore + node.arguments.push(ts.factory.createStringLiteral(depsAndReturnType)) - return ts.factory.createStringLiteral(depsAndReturnType) - } + return node } return ts.visitEachChild(node, visitor, context) From 24bcf34b6095e8237666661536158503599fa765 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 16:07:52 +0200 Subject: [PATCH 24/29] try getting resolved type arguments (WIP) --- pnpm-lock.yaml | 11 +++++- src/experiment/arithmeticInfer.ts | 19 +++++++--- src/plugins/typeInferPlugin.ts | 58 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdeb100..533a215 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,12 +2,14 @@ lockfileVersion: 5.4 specifiers: '@types/node': 20.5.7 + ts-macros: 2.4.1 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 dependencies: '@types/node': 20.5.7 + ts-macros: 2.4.1_typescript@4.7.4 devDependencies: ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha @@ -121,6 +123,14 @@ packages: engines: {node: '>= 0.4'} dev: true + /ts-macros/2.4.1_typescript@4.7.4: + 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: 4.7.4 + dev: false + /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -168,7 +178,6 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} diff --git a/src/experiment/arithmeticInfer.ts b/src/experiment/arithmeticInfer.ts index 4a4a6e6..771d37c 100644 --- a/src/experiment/arithmeticInfer.ts +++ b/src/experiment/arithmeticInfer.ts @@ -1,8 +1,19 @@ import { $reflect } from '../core/$reflect.js' +import { Dependencies } from '../interfaces/type.js' -export const square = $reflect((dep: { - multiply: (a: T, b: T) => T, - unaryMinus: (x: T) => T, // just for the experiment -}): (a: T) => T => +// unaryMinus dep is just for the experiment +// FIXME: the typescript plugin should resolve Dependencies<'multiply' | 'unaryMinus', T> +export const square = $reflect((dep: Dependencies<'multiply' | 'unaryMinus', T>): (a: T) => T => z => dep.multiply(z, z) ) + +// export const square2 = function $reflect(dep: Dependencies<'multiply' | 'unaryMinus', T>) { +// return (z: T) => dep.multiply(z, z) +// } + +// export const square = $reflect((dep: { +// multiply: (a: T, b: T) => T, +// unaryMinus: (x: T) => T, // just for the experiment +// }): (a: T) => T => +// z => dep.multiply(z, z) +// ) diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 84c0dcf..5e91f07 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -32,11 +32,39 @@ const transformer: ts.TransformerFactory = context => { // unaryMinus: (x: number) => number, // just for the experiment // } + // WIP + // @ts-ignore const type = checker.getTypeAtLocation(paramNode) const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + // const paramType = checker.typeToString(type) + // TDOO: get checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) console.log('PLUGIN: PARAM TYPE STRING', paramType) // { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; } + + // WIP + // For a function definition + // const signature = checker.getResolvedSignature(node); + // if (signature != null) { + // // outputs -- (Ctor: Ctor): void + // console.log(signature) + // console.log('TEST 1', checker.signatureToString(signature)); + // // @ts-ignore + // // console.log('TEST 2', checker.getResolvedSignatureForStringLiteralCompletions(node)); + // const params = signature.getParameters(); + // for (const param of params) { + // const type = checker.getTypeOfSymbolAtLocation(param, node); + // // outputs -- Ctor + // console.log('TEST 3', checker.typeToString(type)); + // } + // } + + // WIP + const type0 = resolveTypeArgumentOfCall(checker, node, 0) + // @ts-ignore + const typeStr = checker.typeToString(type0); + console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) + const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` // Now we insert a second argument to the $reflect function call: a string with the types @@ -53,4 +81,34 @@ const transformer: ts.TransformerFactory = context => { } } +// WIP +function resolveTypeArgumentOfCall(checker: ts.TypeChecker, macroCall: ts.CallExpression, typeIndex: number) : ts.Type | undefined { + + // @ts-ignore + console.log( 'TEST D', macroCall.arguments[0].typeParameters) + + if (!macroCall.typeArguments || !macroCall.typeArguments[typeIndex]) return; + const type = checker.getTypeAtLocation(macroCall.typeArguments[typeIndex]); + + console.log( 'TEST A', type, macroCall.typeArguments ) + + return type + + // const lastMacroCall = this.getLastMacro(); + // if (!lastMacroCall) return type; + // if (type.isTypeParameter()) { + // const resolvedTypeParameterIndex = lastMacroCall.macro.typeParams.findIndex(arg => this.checker.getTypeAtLocation(arg) === type); + // if (resolvedTypeParameterIndex === -1) return; + // if (lastMacroCall.call && ts.isCallExpression(lastMacroCall.call)) { + // const resolvedTypeParam = lastMacroCall.call.typeArguments?.[resolvedTypeParameterIndex]; + // if (!resolvedTypeParam) return resolveTypeArguments(this.checker, lastMacroCall.call)[resolvedTypeParameterIndex]; + // return this.checker.getTypeAtLocation(resolvedTypeParam); + // } else return; + // } else { + // const allParams = lastMacroCall.macro.typeParams.map(p => this.checker.getTypeAtLocation(p)); + // const replacementTypes = resolveTypeArguments(this.checker, lastMacroCall.call as ts.CallExpression); + // return resolveTypeWithTypeParams(type, allParams, replacementTypes); + // } +} + export default transformer From 94a0737555ab9a8cfe3846673fbee78fe87421ec Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 16:09:50 +0200 Subject: [PATCH 25/29] update pnpm lock file --- pnpm-lock.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 533a215..fdeb100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,14 +2,12 @@ lockfileVersion: 5.4 specifiers: '@types/node': 20.5.7 - ts-macros: 2.4.1 ts-node: 10.9.1 ttypescript: 1.5.15 typescript: 4.7.4 dependencies: '@types/node': 20.5.7 - ts-macros: 2.4.1_typescript@4.7.4 devDependencies: ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha @@ -123,14 +121,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /ts-macros/2.4.1_typescript@4.7.4: - 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: 4.7.4 - dev: false - /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -178,6 +168,7 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} From ab7fc4450ea0ef28efa51808dfae043f595b083a Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Mon, 11 Sep 2023 17:50:10 +0200 Subject: [PATCH 26/29] add another try --- package.json5 | 2 +- pnpm-lock.yaml | 80 ++++++++++++++++++---------------- src/plugins/typeInferPlugin.ts | 40 +++-------------- 3 files changed, 49 insertions(+), 73 deletions(-) diff --git a/package.json5 b/package.json5 index 4e03b9b..6c2691d 100644 --- a/package.json5 +++ b/package.json5 @@ -4,7 +4,7 @@ description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { - 'build-and-run': 'ttsc -b && echo {"type":"module"} > build/package.json && node build/index.js', + 'go': 'ttsc && echo {"type":"module"} > build/package.json && node build/index.js', }, keywords: [ 'math', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdeb100..e931f27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,113 +1,119 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - '@types/node': 20.5.7 - ts-node: 10.9.1 - ttypescript: 1.5.15 - typescript: 4.7.4 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: - '@types/node': 20.5.7 + '@types/node': + specifier: 20.5.7 + version: 20.5.7 devDependencies: - ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha - ttypescript: 1.5.15_6oasmw356qmm23djlsjgkwvrtm - typescript: 4.7.4 + ts-node: + specifier: 10.9.1 + version: 10.9.1(@types/node@20.5.7)(typescript@4.7.4) + ttypescript: + specifier: 1.5.15 + version: 1.5.15(ts-node@10.9.1)(typescript@4.7.4) + typescript: + specifier: 4.7.4 + version: 4.7.4 packages: - /@cspotcode/source-map-support/0.8.1: + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@jridgewell/resolve-uri/3.1.1: + /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.15: + /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@tsconfig/node10/1.0.9: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.4: + /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/node/20.5.7: + /@types/node@20.5.7: resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==} - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.10.0: + /acorn@8.10.0: resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /is-core-module/2.13.0: + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /resolve/1.22.4: + /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true dependencies: @@ -116,12 +122,12 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /ts-node/10.9.1_l7whiu4appksmcywzzf5ucsgha: + /ts-node@10.9.1(@types/node@20.5.7)(typescript@4.7.4): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -152,7 +158,7 @@ packages: yn: 3.1.1 dev: true - /ttypescript/1.5.15_6oasmw356qmm23djlsjgkwvrtm: + /ttypescript@1.5.15(ts-node@10.9.1)(typescript@4.7.4): resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==} hasBin: true peerDependencies: @@ -160,21 +166,21 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.4 - ts-node: 10.9.1_l7whiu4appksmcywzzf5ucsgha + ts-node: 10.9.1(@types/node@20.5.7)(typescript@4.7.4) typescript: 4.7.4 dev: true - /typescript/4.7.4: + /typescript@4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true diff --git a/src/plugins/typeInferPlugin.ts b/src/plugins/typeInferPlugin.ts index 5e91f07..d4fbe94 100644 --- a/src/plugins/typeInferPlugin.ts +++ b/src/plugins/typeInferPlugin.ts @@ -35,7 +35,7 @@ const transformer: ts.TransformerFactory = context => { // WIP // @ts-ignore const type = checker.getTypeAtLocation(paramNode) - const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias) + const paramType = checker.typeToString(type, undefined, ts.TypeFormatFlags.InTypeAlias) // const paramType = checker.typeToString(type) // TDOO: get checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration) console.log('PLUGIN: PARAM TYPE STRING', paramType) @@ -60,10 +60,10 @@ const transformer: ts.TransformerFactory = context => { // } // WIP - const type0 = resolveTypeArgumentOfCall(checker, node, 0) - // @ts-ignore - const typeStr = checker.typeToString(type0); - console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) + const type1 = checker.getTypeAtLocation(paramNode) + const type2 = checker.getApparentType(type1) + const typeStr = checker.typeToString(type2, undefined, ts.TypeFormatFlags.InTypeAlias) + console.log('PLUGIN: RESOLVED TYPE ARGUMENT', typeStr) // TODO: not yet working const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }` @@ -81,34 +81,4 @@ const transformer: ts.TransformerFactory = context => { } } -// WIP -function resolveTypeArgumentOfCall(checker: ts.TypeChecker, macroCall: ts.CallExpression, typeIndex: number) : ts.Type | undefined { - - // @ts-ignore - console.log( 'TEST D', macroCall.arguments[0].typeParameters) - - if (!macroCall.typeArguments || !macroCall.typeArguments[typeIndex]) return; - const type = checker.getTypeAtLocation(macroCall.typeArguments[typeIndex]); - - console.log( 'TEST A', type, macroCall.typeArguments ) - - return type - - // const lastMacroCall = this.getLastMacro(); - // if (!lastMacroCall) return type; - // if (type.isTypeParameter()) { - // const resolvedTypeParameterIndex = lastMacroCall.macro.typeParams.findIndex(arg => this.checker.getTypeAtLocation(arg) === type); - // if (resolvedTypeParameterIndex === -1) return; - // if (lastMacroCall.call && ts.isCallExpression(lastMacroCall.call)) { - // const resolvedTypeParam = lastMacroCall.call.typeArguments?.[resolvedTypeParameterIndex]; - // if (!resolvedTypeParam) return resolveTypeArguments(this.checker, lastMacroCall.call)[resolvedTypeParameterIndex]; - // return this.checker.getTypeAtLocation(resolvedTypeParam); - // } else return; - // } else { - // const allParams = lastMacroCall.macro.typeParams.map(p => this.checker.getTypeAtLocation(p)); - // const replacementTypes = resolveTypeArguments(this.checker, lastMacroCall.call as ts.CallExpression); - // return resolveTypeWithTypeParams(type, allParams, replacementTypes); - // } -} - export default transformer From 40146c2f48c23d8e41e905a885338b44ee7ebca2 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Tue, 17 Oct 2023 22:02:18 +0000 Subject: [PATCH 27/29] feat: Runtime type reflection (#17) Now each behavior specification "knows" its type information. Also bumps version number and sets up so that the scripts will run on Windows as well as Unix (thanks to Jos). Resolves #5. Resolves #16. Co-authored-by: Jos de Jong Reviewed-on: https://code.studioinfinity.org/glen/typocomath/pulls/17 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- README.md | 4 + package.json5 | 20 +- pnpm-lock.yaml | 899 ++++++++++++++++++++++++++++++++- src/Complex/arithmetic.ts | 11 +- src/Complex/predicate.ts | 6 +- src/Complex/relational.ts | 2 + src/Complex/type.ts | 3 + src/core/Dispatcher.ts | 19 +- src/core/parseReflectedType.ts | 273 ++++++++++ src/generic/arithmetic.ts | 4 +- src/generic/relational.ts | 2 + src/index.ts | 39 +- src/interfaces/type.ts | 15 +- src/numbers/arithmetic.ts | 46 +- src/numbers/native.ts | 2 + src/numbers/predicate.ts | 7 +- src/numbers/relational.ts | 2 + src/numbers/type.ts | 11 +- tsconfig.json | 8 +- 19 files changed, 1317 insertions(+), 56 deletions(-) create mode 100644 src/core/parseReflectedType.ts 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..02e46f3 100644 --- a/package.json5 +++ b/package.json5 @@ -1,13 +1,14 @@ { name: 'typocomath', - version: '0.0.1', + version: '0.0.2', description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept', main: 'index.ts', scripts: { test: 'echo "Error: no test specified" && exit 1', - build: 'tsc && cp etc/package.json build', - exec: 'node build', - go: 'pnpm --sequential "/build|exec/"', + build: 'mkdirp build && cpy etc/package.json build --flat && tsc', + start: 'node build', + go: 'pnpm clean && pnpm build && pnpm start', + clean: 'del-cli build', }, packageManager: 'pnpm', keywords: [ @@ -22,8 +23,13 @@ url: 'https://code.studioinfinity.org/glen/typocomath.git', }, devDependencies: { - '@types/node': '^20.5.0', - 'source-map': '^0.7.4', - typescript: '^5.1.6', + '@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.6.0', + 'ts-patch': '3.0.2', + typescript: '5.1.6', }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 682e112..6791304 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,19 +6,771 @@ settings: devDependencies: '@types/node': - specifier: ^20.5.0 - version: 20.5.0 + specifier: 20.8.4 + version: 20.8.4 + 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 + specifier: 0.7.4 version: 0.7.4 + ts-macros: + specifier: 2.6.0 + version: 2.6.0(typescript@5.1.6) + ts-patch: + specifier: 3.0.2 + version: 3.0.2 typescript: - specifier: ^5.1.6 + specifier: 5.1.6 version: 5.1.6 packages: - /@types/node@20.5.0: - resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} + /@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.8.4: + resolution: {integrity: sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==} + dependencies: + undici-types: 5.25.3 + 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'} + dependencies: + 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'} + dependencies: + ansi-styles: 4.3.0 + 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'} + dependencies: + 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'} + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + 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'} + 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 + + /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'} + dependencies: + 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 + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + 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'} + hasBin: true + dependencies: + 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: @@ -26,8 +778,143 @@ packages: 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'} + dependencies: + 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'} + 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 + + /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.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 + 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 + 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 + + /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 + + /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: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + 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 + + /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 diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index 90c8eab..0f11f2e 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 {$reflect} from '../interfaces/type.js' declare module "../interfaces/type" { interface Signatures { @@ -78,11 +79,10 @@ export const 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> - }): - Signature<'sqrt', Complex> => + }): Signature<'sqrt', Complex> => z => { const myabs = dep.conservativeSqrt(dep.absquare(z)) const r = dep.re(z) @@ -98,4 +98,7 @@ export const sqrt = return dep.divideReal(num, denom) } -export const conservativeSqrt = sqrt +$reflect!([ + add, addReal, unaryMinus, conj, subtract, multiply, absquare, divideReal, + reciprocal, divide, sqrt +]) 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 ec5491f..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,21 +41,25 @@ 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) + 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 } 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) @@ -71,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 new file mode 100644 index 0000000..e4b72a3 --- /dev/null +++ b/src/core/parseReflectedType.ts @@ -0,0 +1,273 @@ +export type FunctionDef { + name: string, + aliasOf?: string, + signatures: Array<{ + args: Array<{ name: string, type: string }> + returns: string + }> +} + + +export type ImplementationDef = { + fn: FunctionDef, + dependencies: Record +} + +/** + * 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): 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: string[] = depArg ? split(depArg, '&').map(trim) : [] + + const deps = depArgBlocks + .filter(depArgBlock => { + if (depArgBlock.startsWith('{') || depArgBlock === 'configDependency') { + return true + } else { + throw new SyntaxError(`Cannot parse dependency "${depArgBlock}"`) + } + }) + .flatMap(parseDependencies) + + const dependencies: Record = groupBy(deps, 'name') + + 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) +} + +// 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) + + return parseAlias(name, def) +} + +// Parse a possibly aliased function +function parseAlias(name: string, alias: string): FunctionDef { + const { aliasOf, innerSignature } = parseAliasOf(alias) + + return { name, signatures: parseSignatures(innerSignature), aliasOf } +} + +// 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) + + if (!returns) { + throw new SyntaxError(`Failed to find return type in '${signature}'`) + } + + 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: 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) + } +} + +/** + * 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 | 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 && (pieces === 0 || n < pieces)) { + i = skipBrackets(text, i) + + delim.lastIndex = i + const result = delim.exec(text) + if (result) { + parts.push(text.substring(start, i)) + n += 1 + i += result[0].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/generic/arithmetic.ts b/src/generic/arithmetic.ts index 6f4e949..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 = (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 +$reflect!([square]) 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/index.ts b/src/index.ts index 297b271..e3377e5 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,16 +6,48 @@ export default new Dispatcher(Specifications) import {Complex} from './Complex/type.js' import {absquare as absquare_complex} from './Complex/arithmetic.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 +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)) + +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(`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(`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 })) diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts index 9fb1163..3ab1b9a 100644 --- a/src/interfaces/type.ts +++ b/src/interfaces/type.ts @@ -1,3 +1,5 @@ +import {$$typeToString} from 'ts-macros' + /***** * Every typocomath type has some associated types; they need * to be published in the following interface. The key is the @@ -73,7 +75,16 @@ export interface Signatures { type SignatureKey = keyof Signatures export type Signature, T> = Signatures[Name] -export type Returns, T> = ReturnType[Name]> -export type Dependencies, T> = {[K in Name]: Signature} +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} + +// For defining implementations with type reflection +export function $reflect(tup: ImplTuple) { + +[[tup], (elt: T) => + elt.reflectedType = $$typeToString!(true, false, true)] +} diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 11da5c2..51d2ce5 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,25 +1,35 @@ import type {configDependency} from '../core/Config.js' import type {Dependencies, Signature} 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 -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 +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 => isNaN(a) ? NaN : Math.sqrt(a) -export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt +const basicSqrt = (a: number) => 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))) - } + (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(unaMinus(a))) + } } + +$reflect!([ + add, unaryMinus, conj, subtract, multiply, absquare, reciprocal, divide, + conservativeSqrt, sqrt +]) 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]) diff --git a/tsconfig.json b/tsconfig.json index 13b7b6c..63c0b04 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,12 @@ { "compilerOptions": { - "target": "ES2022", + "target": "esnext", "rootDir": "./src", "outDir": "./build", - "moduleResolution": "nodenext" + "moduleResolution": "nodenext", + "plugins": [ { + "transform": "ts-macros/dist/type-resolve", + "transformProgram": true + } ] } } From 0cdc9aba7809558dfa2fe2b181a22fb4d2f377bb Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 18 Oct 2023 15:11:54 +0200 Subject: [PATCH 28/29] chore: fix typo in type definition of `FunctionDef` --- src/core/parseReflectedType.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/parseReflectedType.ts b/src/core/parseReflectedType.ts index e4b72a3..a2e9866 100644 --- a/src/core/parseReflectedType.ts +++ b/src/core/parseReflectedType.ts @@ -1,4 +1,4 @@ -export type FunctionDef { +export type FunctionDef = { name: string, aliasOf?: string, signatures: Array<{ From 6bfd06cafb29749542f835a08ccc54fbbba049ba Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Wed, 18 Oct 2023 15:22:21 +0200 Subject: [PATCH 29/29] feat: extract generic parameter from the reflectedType (see #18) --- src/core/parseReflectedType.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/parseReflectedType.ts b/src/core/parseReflectedType.ts index a2e9866..6212320 100644 --- a/src/core/parseReflectedType.ts +++ b/src/core/parseReflectedType.ts @@ -11,6 +11,7 @@ export type FunctionDef = { export type ImplementationDef = { fn: FunctionDef, dependencies: Record + genericParameter: string | null } /** @@ -23,6 +24,11 @@ export function parseReflectedType(name: string, reflectedType: string): Impleme const [factoryArgs, fnsClause] = split(reflectedType, '=>', 2).map(trim) const fn = parseAlias(name, fnsClause) + // extract the generic parameter like '' at the start of the type + const genericParameter = factoryArgs.trim().startsWith('<') + ? findBlockContents(factoryArgs, '<', '>')?.innerText || null + : null + const factoryArgsInner = findBlockContents(factoryArgs, '(', ')') const depArg = split(factoryArgsInner.innerText, ':').map(trim)[1] const depArgBlocks: string[] = depArg ? split(depArg, '&').map(trim) : [] @@ -39,7 +45,7 @@ export function parseReflectedType(name: string, reflectedType: string): Impleme const dependencies: Record = groupBy(deps, 'name') - return {fn, dependencies} + return {fn, dependencies, genericParameter } } function parseDependencies(deps: string): FunctionDef[] {