Compare commits
No commits in common. "f8553aa748fff0e843661fe9b71a09583a035aac" and "946b4a495f7c64190e7ac763e05df7a2304e583f" have entirely different histories.
f8553aa748
...
946b4a495f
7 changed files with 0 additions and 227 deletions
50
README.md
50
README.md
|
@ -8,53 +8,3 @@ To build and run the prototype, run:
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm build-and-run
|
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
|
|
|
@ -5,9 +5,6 @@
|
||||||
main: 'index.ts',
|
main: 'index.ts',
|
||||||
scripts: {
|
scripts: {
|
||||||
'build-and-run': 'ttsc -b && node build',
|
'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:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts',
|
|
||||||
test: 'echo "Error: no test specified" && exit 1',
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
},
|
},
|
||||||
keywords: [
|
keywords: [
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import {infer} from '../generic/infer'
|
|
||||||
|
|
||||||
export const square = infer(<T>(dep: {
|
|
||||||
multiply: (a: number, b: number) => number,
|
|
||||||
unaryMinus: (x: number) => number, // just for the experiment
|
|
||||||
}): (a: number) => number =>
|
|
||||||
z => dep.multiply(z, z)
|
|
||||||
)
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { infer } from '../generic/infer'
|
|
||||||
import { Dependencies, Signature } from '../interfaces/type'
|
|
||||||
|
|
||||||
export type multiplyDep<T> = Dependencies<'multiply', T>
|
|
||||||
|
|
||||||
export const square1 =
|
|
||||||
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
|
||||||
z => dep.multiply(z, z)
|
|
||||||
|
|
||||||
export const square2 = infer(<T>(dep: {
|
|
||||||
multiply: (a: number, b: number) => number,
|
|
||||||
unaryMinus: (x: number) => number, // just for the experiment
|
|
||||||
}): (a: number) => number =>
|
|
||||||
z => dep.multiply(z, z)
|
|
||||||
)
|
|
|
@ -1,4 +0,0 @@
|
||||||
export function infer<T>(arg: T) : T {
|
|
||||||
console.error('infer should be replace with runtime type information by a magic TypeScript plugin')
|
|
||||||
return arg
|
|
||||||
}
|
|
|
@ -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(<T>(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 =
|
|
||||||
// <T>(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);
|
|
||||||
});
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue