From cbb79d46fe07a5fd038423e890d0abba8b0bbac1 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Fri, 1 Sep 2023 18:21:45 +0200 Subject: [PATCH] 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" }] }