@jpoly1219/context-extractor 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +27 -0
- package/dist/app.js +285 -0
- package/dist/codeql.d.ts +17 -0
- package/dist/codeql.js +1341 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +62 -0
- package/dist/core.d.ts +20 -0
- package/dist/core.js +576 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/main.d.ts +13 -0
- package/dist/main.js +255 -0
- package/dist/ocaml-driver.d.ts +30 -0
- package/dist/ocaml-driver.js +394 -0
- package/dist/ocaml-type-checker.d.ts +52 -0
- package/dist/ocaml-type-checker.js +286 -0
- package/dist/runner.d.ts +1 -0
- package/dist/runner.js +52 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.js +14 -0
- package/dist/typescript-driver.d.ts +27 -0
- package/dist/typescript-driver.js +542 -0
- package/dist/typescript-type-checker.d.ts +64 -0
- package/dist/typescript-type-checker.js +512 -0
- package/dist/utils.d.ts +40 -0
- package/dist/utils.js +350 -0
- package/package.json +1 -1
@@ -0,0 +1,542 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
26
|
+
exports.TypeScriptDriver = void 0;
|
27
|
+
const fs = __importStar(require("fs"));
|
28
|
+
const path = __importStar(require("path"));
|
29
|
+
const child_process_1 = require("child_process");
|
30
|
+
const typescript_type_checker_1 = require("./typescript-type-checker");
|
31
|
+
const utils_1 = require("./utils");
|
32
|
+
class TypeScriptDriver {
|
33
|
+
constructor() {
|
34
|
+
this.typeChecker = new typescript_type_checker_1.TypeScriptTypeChecker();
|
35
|
+
}
|
36
|
+
async init(lspClient, sketchPath) {
|
37
|
+
const capabilities = {
|
38
|
+
'textDocument': {
|
39
|
+
'codeAction': { 'dynamicRegistration': true },
|
40
|
+
'codeLens': { 'dynamicRegistration': true },
|
41
|
+
'colorProvider': { 'dynamicRegistration': true },
|
42
|
+
'completion': {
|
43
|
+
'completionItem': {
|
44
|
+
'commitCharactersSupport': true,
|
45
|
+
'documentationFormat': ['markdown', 'plaintext'],
|
46
|
+
'snippetSupport': true
|
47
|
+
},
|
48
|
+
'completionItemKind': {
|
49
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
|
50
|
+
},
|
51
|
+
'contextSupport': true,
|
52
|
+
'dynamicRegistration': true
|
53
|
+
},
|
54
|
+
'definition': { 'dynamicRegistration': true },
|
55
|
+
'documentHighlight': { 'dynamicRegistration': true },
|
56
|
+
'documentLink': { 'dynamicRegistration': true },
|
57
|
+
'documentSymbol': {
|
58
|
+
'dynamicRegistration': true,
|
59
|
+
'symbolKind': {
|
60
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
61
|
+
}
|
62
|
+
},
|
63
|
+
'formatting': { 'dynamicRegistration': true },
|
64
|
+
'hover': {
|
65
|
+
'contentFormat': ['markdown', 'plaintext'],
|
66
|
+
'dynamicRegistration': true
|
67
|
+
},
|
68
|
+
'implementation': { 'dynamicRegistration': true },
|
69
|
+
// 'inlayhint': { 'dynamicRegistration': true },
|
70
|
+
'onTypeFormatting': { 'dynamicRegistration': true },
|
71
|
+
'publishDiagnostics': { 'relatedInformation': true },
|
72
|
+
'rangeFormatting': { 'dynamicRegistration': true },
|
73
|
+
'references': { 'dynamicRegistration': true },
|
74
|
+
'rename': { 'dynamicRegistration': true },
|
75
|
+
'signatureHelp': {
|
76
|
+
'dynamicRegistration': true,
|
77
|
+
'signatureInformation': { 'documentationFormat': ['markdown', 'plaintext'] }
|
78
|
+
},
|
79
|
+
'synchronization': {
|
80
|
+
'didSave': true,
|
81
|
+
'dynamicRegistration': true,
|
82
|
+
'willSave': true,
|
83
|
+
'willSaveWaitUntil': true
|
84
|
+
},
|
85
|
+
'typeDefinition': { 'dynamicRegistration': true, 'linkSupport': true },
|
86
|
+
// 'typeHierarchy': { 'dynamicRegistration': true }
|
87
|
+
},
|
88
|
+
'workspace': {
|
89
|
+
'applyEdit': true,
|
90
|
+
'configuration': true,
|
91
|
+
'didChangeConfiguration': { 'dynamicRegistration': true },
|
92
|
+
'didChangeWatchedFiles': { 'dynamicRegistration': true },
|
93
|
+
'executeCommand': { 'dynamicRegistration': true },
|
94
|
+
'symbol': {
|
95
|
+
'dynamicRegistration': true,
|
96
|
+
'symbolKind': {
|
97
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
98
|
+
}
|
99
|
+
}, 'workspaceEdit': { 'documentChanges': true },
|
100
|
+
'workspaceFolders': true
|
101
|
+
},
|
102
|
+
'general': {
|
103
|
+
'positionEncodings': ['utf-8']
|
104
|
+
},
|
105
|
+
};
|
106
|
+
const rootPath = path.dirname(sketchPath);
|
107
|
+
const rootUri = `file://${rootPath}`;
|
108
|
+
const workspaceFolders = [{ 'name': 'context-extractor', 'uri': rootUri }];
|
109
|
+
await lspClient.initialize({
|
110
|
+
processId: process.pid,
|
111
|
+
capabilities: capabilities,
|
112
|
+
trace: 'off',
|
113
|
+
rootUri: null,
|
114
|
+
workspaceFolders: workspaceFolders,
|
115
|
+
initializationOptions: {
|
116
|
+
preferences: {
|
117
|
+
includeInlayVariableTypeHints: true
|
118
|
+
}
|
119
|
+
}
|
120
|
+
});
|
121
|
+
}
|
122
|
+
async getHoleContext(lspClient, sketchFilePath) {
|
123
|
+
// For TypeScript programs, we need to inject the hole function before getting its context.
|
124
|
+
// NOTE: this can be abstracted to its own method?
|
125
|
+
const sketchDir = path.dirname(sketchFilePath);
|
126
|
+
const injectedSketchFilePath = path.join(sketchDir, "injected_sketch.ts");
|
127
|
+
const sketchFileContent = fs.readFileSync(sketchFilePath, "utf8");
|
128
|
+
const injectedSketchFileContent = `declare function _<T>(): T\n${sketchFileContent}`;
|
129
|
+
fs.writeFileSync(injectedSketchFilePath, injectedSketchFileContent);
|
130
|
+
// Sync client and server by notifying that the client has opened all the files inside the target directory.
|
131
|
+
fs.readdirSync(sketchDir).map(fileName => {
|
132
|
+
if (fs.lstatSync(path.join(sketchDir, fileName)).isFile()) {
|
133
|
+
lspClient.didOpen({
|
134
|
+
textDocument: {
|
135
|
+
uri: `file://${sketchDir}/${fileName}`,
|
136
|
+
languageId: "typescript",
|
137
|
+
text: fs.readFileSync(`${sketchDir}/${fileName}`).toString("ascii"),
|
138
|
+
version: 1
|
139
|
+
}
|
140
|
+
});
|
141
|
+
}
|
142
|
+
});
|
143
|
+
// Get hole context.
|
144
|
+
const holePattern = /_\(\)/;
|
145
|
+
const firstPatternIndex = injectedSketchFileContent.search(holePattern);
|
146
|
+
const linePosition = (injectedSketchFileContent.substring(0, firstPatternIndex).match(/\n/g)).length;
|
147
|
+
const characterPosition = firstPatternIndex - injectedSketchFileContent.split("\n", linePosition).join("\n").length - 1;
|
148
|
+
const holeHoverResult = await lspClient.hover({
|
149
|
+
textDocument: {
|
150
|
+
uri: injectedSketchFilePath
|
151
|
+
},
|
152
|
+
position: {
|
153
|
+
character: characterPosition,
|
154
|
+
line: linePosition
|
155
|
+
}
|
156
|
+
});
|
157
|
+
const formattedHoverResult = holeHoverResult.contents.value.split("\n").reduce((acc, curr) => {
|
158
|
+
if (curr != "" && curr != "```typescript" && curr != "```") {
|
159
|
+
return acc + curr;
|
160
|
+
}
|
161
|
+
else {
|
162
|
+
return acc;
|
163
|
+
}
|
164
|
+
}, "");
|
165
|
+
// function _<(a: Apple, c: Cherry, b: Banana) => Cherry > (): (a: Apple, c: Cherry, b: Banana) => Cherry
|
166
|
+
const holeFunctionPattern = /(function _)(\<.+\>)(\(\): )(.+)/;
|
167
|
+
const match = formattedHoverResult.match(holeFunctionPattern);
|
168
|
+
const functionName = "_()";
|
169
|
+
const functionTypeSpan = match[4];
|
170
|
+
// Clean up and inject the true hole function without the generic type signature.
|
171
|
+
// NOTE: this can be abstracted to its own method?
|
172
|
+
const trueHoleFunction = `declare function _(): ${functionTypeSpan}`;
|
173
|
+
const trueInjectedSketchFileContent = `${trueHoleFunction}\n${sketchFileContent}`;
|
174
|
+
fs.writeFileSync(injectedSketchFilePath, trueInjectedSketchFileContent);
|
175
|
+
lspClient.didChange({
|
176
|
+
textDocument: {
|
177
|
+
uri: `file://${injectedSketchFilePath}`,
|
178
|
+
version: 2
|
179
|
+
},
|
180
|
+
contentChanges: [{
|
181
|
+
text: trueInjectedSketchFileContent
|
182
|
+
}]
|
183
|
+
});
|
184
|
+
const sketchSymbol = await lspClient.documentSymbol({
|
185
|
+
textDocument: {
|
186
|
+
uri: `file://${injectedSketchFilePath}`,
|
187
|
+
}
|
188
|
+
});
|
189
|
+
return {
|
190
|
+
fullHoverResult: formattedHoverResult,
|
191
|
+
functionName: functionName,
|
192
|
+
functionTypeSpan: functionTypeSpan,
|
193
|
+
linePosition: linePosition,
|
194
|
+
characterPosition: characterPosition,
|
195
|
+
holeTypeDefLinePos: 0,
|
196
|
+
holeTypeDefCharPos: "declare function _(): ".length,
|
197
|
+
// range: { start: { line: 0, character: 0 }, end: { line: 0, character: 52 } }
|
198
|
+
range: sketchSymbol[0].location.range,
|
199
|
+
source: `file://${injectedSketchFilePath}`
|
200
|
+
};
|
201
|
+
}
|
202
|
+
async extractRelevantTypes(lspClient, fullHoverResult, typeName, startLine, endLine, foundSoFar, // identifier -> [full hover result, source]
|
203
|
+
currentFile) {
|
204
|
+
if (!foundSoFar.has(typeName)) {
|
205
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
206
|
+
// outputFile.write(`${fullHoverResult};\n`);
|
207
|
+
const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
208
|
+
for (let linePos = startLine; linePos <= endLine; ++linePos) {
|
209
|
+
const numOfCharsInLine = parseInt((0, child_process_1.execSync)(`wc -m <<< "${content.split("\n")[linePos]}"`, { shell: "/bin/bash" }).toString());
|
210
|
+
for (let charPos = 0; charPos < numOfCharsInLine; ++charPos) {
|
211
|
+
try {
|
212
|
+
const typeDefinitionResult = await lspClient.typeDefinition({
|
213
|
+
textDocument: {
|
214
|
+
uri: currentFile
|
215
|
+
},
|
216
|
+
position: {
|
217
|
+
character: charPos,
|
218
|
+
line: linePos
|
219
|
+
}
|
220
|
+
});
|
221
|
+
if (typeDefinitionResult && typeDefinitionResult instanceof Array && typeDefinitionResult.length != 0) {
|
222
|
+
// Use documentSymbol instead of hover.
|
223
|
+
// This prevents type alias "squashing" done by tsserver.
|
224
|
+
// This also allows for grabbing the entire definition range and not just the symbol range.
|
225
|
+
// PERF: feels like this could be memoized to improve performance.
|
226
|
+
const documentSymbolResult = await lspClient.documentSymbol({
|
227
|
+
textDocument: {
|
228
|
+
uri: typeDefinitionResult[0].uri
|
229
|
+
}
|
230
|
+
});
|
231
|
+
// grab if the line number of typeDefinitionResult and documentSymbolResult matches
|
232
|
+
const dsMap = documentSymbolResult.reduce((m, obj) => {
|
233
|
+
m.set(obj.location.range.start.line, obj.location.range);
|
234
|
+
return m;
|
235
|
+
}, new Map());
|
236
|
+
const matchingSymbolRange = dsMap.get(typeDefinitionResult[0].range.start.line);
|
237
|
+
if (matchingSymbolRange) {
|
238
|
+
const snippetInRange = (0, utils_1.extractSnippet)(fs.readFileSync(typeDefinitionResult[0].uri.slice(7)).toString("utf8"), matchingSymbolRange.start, matchingSymbolRange.end);
|
239
|
+
// TODO: this can potentially be its own method. the driver would require some way to get type context.
|
240
|
+
// potentially, this type checker can be its own class.
|
241
|
+
const identifier = this.typeChecker.getIdentifierFromDecl(snippetInRange);
|
242
|
+
await this.extractRelevantTypes(lspClient, snippetInRange, identifier, matchingSymbolRange.start.line, matchingSymbolRange.end.line, foundSoFar, typeDefinitionResult[0].uri);
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
246
|
+
catch (err) {
|
247
|
+
console.log(`${err}`);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return foundSoFar;
|
253
|
+
}
|
254
|
+
async extractRelevantHeaders(_, sources, relevantTypes, holeType) {
|
255
|
+
const relevantContext = new Set();
|
256
|
+
const targetTypes = this.generateTargetTypes(relevantTypes, holeType);
|
257
|
+
// only consider lines that start with let or const
|
258
|
+
for (const source of sources) {
|
259
|
+
const sourceContent = fs.readFileSync(source).toString("utf8");
|
260
|
+
const filteredLines = sourceContent.split("\n").filter((line) => {
|
261
|
+
return line.slice(0, 3) === "let" || line.slice(0, 5) === "const";
|
262
|
+
});
|
263
|
+
// check for relationship between each line and relevant types
|
264
|
+
filteredLines.forEach(line => {
|
265
|
+
// TODO: Use the compiler API to split this.
|
266
|
+
const splittedLine = line.split(" = ")[0];
|
267
|
+
const typeSpanPattern = /(^[^:]*: )(.+)/;
|
268
|
+
const regexMatch = splittedLine.match(typeSpanPattern);
|
269
|
+
if (regexMatch) {
|
270
|
+
const returnTypeSpan = regexMatch[2];
|
271
|
+
if (!this.typeChecker.isPrimitive(returnTypeSpan.split(" => ")[1])) {
|
272
|
+
this.extractRelevantHeadersHelper(returnTypeSpan, targetTypes, relevantTypes, relevantContext, splittedLine, source);
|
273
|
+
}
|
274
|
+
}
|
275
|
+
});
|
276
|
+
}
|
277
|
+
return relevantContext;
|
278
|
+
}
|
279
|
+
generateTargetTypes(relevantTypes, holeType) {
|
280
|
+
const targetTypes = new Set();
|
281
|
+
targetTypes.add(holeType);
|
282
|
+
this.generateTargetTypesHelper(relevantTypes, holeType, targetTypes);
|
283
|
+
return targetTypes;
|
284
|
+
}
|
285
|
+
// generateTargetTypesHelper(
|
286
|
+
// relevantTypes: Map<string, TypeSpanAndSourceFile>,
|
287
|
+
// currType: string,
|
288
|
+
// targetTypes: Set<string>
|
289
|
+
// ) {
|
290
|
+
// // console.log("===Helper===")
|
291
|
+
// if (this.typeChecker.isFunction(currType)) {
|
292
|
+
// const functionPattern = /(\(.+\))( => )(.+)(;*)/;
|
293
|
+
// const rettype = currType.match(functionPattern)![3];
|
294
|
+
// targetTypes.add(rettype);
|
295
|
+
// this.generateTargetTypesHelper(relevantTypes, rettype, targetTypes);
|
296
|
+
//
|
297
|
+
// } else if (this.typeChecker.isTuple(currType)) {
|
298
|
+
// const elements = this.typeChecker.parseTypeArrayString(currType)
|
299
|
+
//
|
300
|
+
// elements.forEach(element => {
|
301
|
+
// targetTypes.add(element)
|
302
|
+
// this.generateTargetTypesHelper(relevantTypes, element, targetTypes);
|
303
|
+
// });
|
304
|
+
// } else {
|
305
|
+
// if (relevantTypes.has(currType)) {
|
306
|
+
// const definition = relevantTypes.get(currType)!.typeSpan.split(" = ")[1];
|
307
|
+
// this.generateTargetTypesHelper(relevantTypes, definition, targetTypes);
|
308
|
+
// }
|
309
|
+
// }
|
310
|
+
// }
|
311
|
+
generateTargetTypesHelper(relevantTypes, currType, targetTypes) {
|
312
|
+
// Run analysis on currType.
|
313
|
+
const typeAnalysisResult = this.typeChecker.analyzeTypeString(currType);
|
314
|
+
// Match on its kind.
|
315
|
+
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
316
|
+
const rettype = typeAnalysisResult.returnType;
|
317
|
+
targetTypes.add(rettype.text);
|
318
|
+
this.generateTargetTypesHelper(relevantTypes, rettype.text, targetTypes);
|
319
|
+
}
|
320
|
+
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
321
|
+
typeAnalysisResult.constituents.forEach(constituent => {
|
322
|
+
targetTypes.add(constituent.text);
|
323
|
+
this.generateTargetTypesHelper(relevantTypes, constituent.text, targetTypes);
|
324
|
+
});
|
325
|
+
}
|
326
|
+
else {
|
327
|
+
if (relevantTypes.has(currType)) {
|
328
|
+
const definition = relevantTypes.get(currType).typeSpan.split(" = ")[1];
|
329
|
+
this.generateTargetTypesHelper(relevantTypes, definition, targetTypes);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
}
|
333
|
+
// resursive helper for extractRelevantContext
|
334
|
+
// checks for nested type equivalence
|
335
|
+
// extractRelevantHeadersHelper(typeSpan: string, targetTypes: Set<string>, relevantTypes: Map<string, TypeSpanAndSourceFile>, relevantContext: Set<TypeSpanAndSourceFile>, line: string, source: string) {
|
336
|
+
// // NOTE: BUGFIX
|
337
|
+
// // console.log(`typeSpan: ${typeSpan}`)
|
338
|
+
// // console.log(`targetTypes: ${targetTypes}`)
|
339
|
+
// targetTypes.forEach(typ => {
|
340
|
+
// if (this.isTypeEquivalent(typeSpan, typ, relevantTypes)) {
|
341
|
+
// relevantContext.add({ typeSpan: line, sourceFile: source });
|
342
|
+
// }
|
343
|
+
//
|
344
|
+
// if (this.typeChecker.isFunction(typeSpan)) {
|
345
|
+
// const functionPattern = /(\(.+\))( => )(.+)/;
|
346
|
+
// const rettype = typeSpan.match(functionPattern)![3];
|
347
|
+
//
|
348
|
+
// this.extractRelevantHeadersHelper(rettype, targetTypes, relevantTypes, relevantContext, line, source);
|
349
|
+
//
|
350
|
+
// } else if (this.typeChecker.isTuple(typeSpan)) {
|
351
|
+
// const elements = this.typeChecker.parseTypeArrayString(typeSpan)
|
352
|
+
// // const elements = typeSpan.slice(1, typeSpan.length - 1).split(", ");
|
353
|
+
//
|
354
|
+
// elements.forEach(element => {
|
355
|
+
// this.extractRelevantHeadersHelper(element, targetTypes, relevantTypes, relevantContext, line, source);
|
356
|
+
// });
|
357
|
+
//
|
358
|
+
// }
|
359
|
+
// });
|
360
|
+
// }
|
361
|
+
extractRelevantHeadersHelper(typeSpan, targetTypes, relevantTypes, relevantContext, line, source) {
|
362
|
+
const typeAnalysisResult = this.typeChecker.analyzeTypeString(typeSpan);
|
363
|
+
targetTypes.forEach(typ => {
|
364
|
+
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes)) {
|
365
|
+
relevantContext.add({ typeSpan: line, sourceFile: source });
|
366
|
+
}
|
367
|
+
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
368
|
+
const rettype = typeAnalysisResult.returnType;
|
369
|
+
this.extractRelevantHeadersHelper(rettype.text, targetTypes, relevantTypes, relevantContext, line, source);
|
370
|
+
}
|
371
|
+
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
372
|
+
typeAnalysisResult.constituents.forEach(constituent => {
|
373
|
+
this.extractRelevantHeadersHelper(constituent.text, targetTypes, relevantTypes, relevantContext, line, source);
|
374
|
+
});
|
375
|
+
}
|
376
|
+
});
|
377
|
+
}
|
378
|
+
// two types are equivalent if they have the same normal forms
|
379
|
+
isTypeEquivalent(t1, t2, relevantTypes) {
|
380
|
+
// NOTE: BUGFIX
|
381
|
+
// console.log(`isTypeEquivalent: ${t1}, ${t2}`)
|
382
|
+
// console.log(t1 == undefined)
|
383
|
+
// console.log(t2 == undefined)
|
384
|
+
const normT1 = this.normalize(t1, relevantTypes);
|
385
|
+
const normT2 = this.normalize(t2, relevantTypes);
|
386
|
+
return normT1 === normT2;
|
387
|
+
}
|
388
|
+
// return the normal form given a type span and a set of relevant types
|
389
|
+
// TODO: replace type checking with information from the AST?
|
390
|
+
normalize(typeSpan, relevantTypes) {
|
391
|
+
// NOTE: BUGFIX
|
392
|
+
// console.log(`normalize: ${typeSpan}`)
|
393
|
+
if (typeSpan.slice(typeSpan.length - 2) == " =") {
|
394
|
+
typeSpan = typeSpan.slice(typeSpan.length - 2);
|
395
|
+
}
|
396
|
+
let normalForm = "";
|
397
|
+
// pattern matching for typeSpan
|
398
|
+
if (this.typeChecker.isPrimitive(typeSpan)) {
|
399
|
+
return typeSpan;
|
400
|
+
}
|
401
|
+
else if (this.typeChecker.isObject(typeSpan)) {
|
402
|
+
// console.log(`isObject: ${typeSpan}`)
|
403
|
+
const elements = typeSpan.slice(1, typeSpan.length - 2).split(";");
|
404
|
+
normalForm += "{";
|
405
|
+
elements.forEach(element => {
|
406
|
+
if (element !== "") {
|
407
|
+
const kv = element.split(": ");
|
408
|
+
normalForm += kv[0].slice(1, kv[0].length), ": ", this.normalize(kv[1], relevantTypes);
|
409
|
+
normalForm += "; ";
|
410
|
+
}
|
411
|
+
});
|
412
|
+
normalForm += "}";
|
413
|
+
return normalForm;
|
414
|
+
}
|
415
|
+
else if (this.typeChecker.isTuple(typeSpan)) {
|
416
|
+
// console.log(`isTuple: ${typeSpan}`)
|
417
|
+
// const elements = typeSpan.slice(1, typeSpan.length - 1).split(", ");
|
418
|
+
const elements = this.typeChecker.parseTypeArrayString(typeSpan);
|
419
|
+
normalForm += "[";
|
420
|
+
elements.forEach((element, i) => {
|
421
|
+
normalForm += this.normalize(element, relevantTypes);
|
422
|
+
if (i < elements.length - 1) {
|
423
|
+
normalForm += ", ";
|
424
|
+
}
|
425
|
+
});
|
426
|
+
normalForm += "]";
|
427
|
+
return normalForm;
|
428
|
+
}
|
429
|
+
else if (this.typeChecker.isUnion(typeSpan)) {
|
430
|
+
// console.log(`isUnion: ${typeSpan}`)
|
431
|
+
const elements = typeSpan.split(" | ");
|
432
|
+
elements.forEach((element, i) => {
|
433
|
+
normalForm += "(";
|
434
|
+
normalForm += this.normalize(element, relevantTypes);
|
435
|
+
normalForm += ")";
|
436
|
+
if (i < elements.length - 1) {
|
437
|
+
normalForm += " | ";
|
438
|
+
}
|
439
|
+
});
|
440
|
+
return normalForm;
|
441
|
+
}
|
442
|
+
else if (this.typeChecker.isArray(typeSpan)) {
|
443
|
+
// console.log(`isArray: ${typeSpan}`)
|
444
|
+
const element = typeSpan.split("[]")[0];
|
445
|
+
normalForm += this.normalize(element, relevantTypes);
|
446
|
+
normalForm += "[]";
|
447
|
+
return normalForm;
|
448
|
+
}
|
449
|
+
else if (this.typeChecker.isTypeAlias(typeSpan)) {
|
450
|
+
const typ = relevantTypes.get(typeSpan).typeSpan.split(" = ")[1];
|
451
|
+
if (typ === undefined) {
|
452
|
+
return typeSpan;
|
453
|
+
}
|
454
|
+
normalForm += this.normalize(typ, relevantTypes);
|
455
|
+
return normalForm;
|
456
|
+
}
|
457
|
+
else {
|
458
|
+
// console.log(`else: ${typeSpan}`)
|
459
|
+
return typeSpan;
|
460
|
+
}
|
461
|
+
}
|
462
|
+
normalize2(typeSpan, relevantTypes) {
|
463
|
+
// NOTE: BUGFIX
|
464
|
+
// console.log(`normalize: ${typeSpan}`)
|
465
|
+
if (typeSpan.slice(typeSpan.length - 2) == " =") {
|
466
|
+
typeSpan = typeSpan.slice(typeSpan.length - 2);
|
467
|
+
}
|
468
|
+
let normalForm = "";
|
469
|
+
const analysisResult = this.typeChecker.analyzeTypeString(typeSpan);
|
470
|
+
// pattern matching for typeSpan
|
471
|
+
// if (this.typeChecker.isPrimitive(typeSpan)) {
|
472
|
+
if (this.typeChecker.isPrimitive2(analysisResult)) {
|
473
|
+
return typeSpan;
|
474
|
+
// } else if (this.typeChecker.isObject(typeSpan)) {
|
475
|
+
}
|
476
|
+
else if (this.typeChecker.isObject2(analysisResult)) {
|
477
|
+
// console.log(`isObject: ${typeSpan}`)
|
478
|
+
const elements = typeSpan.slice(1, typeSpan.length - 2).split(";");
|
479
|
+
normalForm += "{";
|
480
|
+
elements.forEach(element => {
|
481
|
+
if (element !== "") {
|
482
|
+
const kv = element.split(": ");
|
483
|
+
normalForm += kv[0].slice(1, kv[0].length), ": ", this.normalize(kv[1], relevantTypes);
|
484
|
+
normalForm += "; ";
|
485
|
+
}
|
486
|
+
});
|
487
|
+
normalForm += "}";
|
488
|
+
return normalForm;
|
489
|
+
// } else if (this.typeChecker.isTuple(typeSpan)) {
|
490
|
+
}
|
491
|
+
else if (this.typeChecker.isTuple2(analysisResult)) {
|
492
|
+
// console.log(`isTuple: ${typeSpan}`)
|
493
|
+
// const elements = typeSpan.slice(1, typeSpan.length - 1).split(", ");
|
494
|
+
const elements = this.typeChecker.parseTypeArrayString(typeSpan);
|
495
|
+
normalForm += "[";
|
496
|
+
elements.forEach((element, i) => {
|
497
|
+
normalForm += this.normalize(element, relevantTypes);
|
498
|
+
if (i < elements.length - 1) {
|
499
|
+
normalForm += ", ";
|
500
|
+
}
|
501
|
+
});
|
502
|
+
normalForm += "]";
|
503
|
+
return normalForm;
|
504
|
+
// } else if (this.typeChecker.isUnion(typeSpan)) {
|
505
|
+
}
|
506
|
+
else if (this.typeChecker.isUnion2(analysisResult)) {
|
507
|
+
// console.log(`isUnion: ${typeSpan}`)
|
508
|
+
const elements = typeSpan.split(" | ");
|
509
|
+
elements.forEach((element, i) => {
|
510
|
+
normalForm += "(";
|
511
|
+
normalForm += this.normalize(element, relevantTypes);
|
512
|
+
normalForm += ")";
|
513
|
+
if (i < elements.length - 1) {
|
514
|
+
normalForm += " | ";
|
515
|
+
}
|
516
|
+
});
|
517
|
+
return normalForm;
|
518
|
+
// } else if (this.typeChecker.isArray(typeSpan)) {
|
519
|
+
}
|
520
|
+
else if (this.typeChecker.isArray2(analysisResult)) {
|
521
|
+
// console.log(`isArray: ${typeSpan}`)
|
522
|
+
const element = typeSpan.split("[]")[0];
|
523
|
+
normalForm += this.normalize(element, relevantTypes);
|
524
|
+
normalForm += "[]";
|
525
|
+
return normalForm;
|
526
|
+
// } else if (this.typeChecker.isTypeAlias(typeSpan)) {
|
527
|
+
}
|
528
|
+
else if (this.typeChecker.isTypeAlias2(analysisResult)) {
|
529
|
+
const typ = relevantTypes.get(typeSpan).typeSpan.split(" = ")[1];
|
530
|
+
if (typ === undefined) {
|
531
|
+
return typeSpan;
|
532
|
+
}
|
533
|
+
normalForm += this.normalize(typ, relevantTypes);
|
534
|
+
return normalForm;
|
535
|
+
}
|
536
|
+
else {
|
537
|
+
// console.log(`else: ${typeSpan}`)
|
538
|
+
return typeSpan;
|
539
|
+
}
|
540
|
+
}
|
541
|
+
}
|
542
|
+
exports.TypeScriptDriver = TypeScriptDriver;
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import * as ts from 'typescript';
|
2
|
+
import { TypeChecker, TypeAnalysis } from "./types";
|
3
|
+
export declare class TypeScriptTypeChecker implements TypeChecker {
|
4
|
+
getIdentifierFromDecl(typeDecl: string): string;
|
5
|
+
getTypeContextFromDecl(typeDecl: string): {
|
6
|
+
identifier: string;
|
7
|
+
span: string;
|
8
|
+
} | null;
|
9
|
+
checkPrimitive(typeDecl: string): {
|
10
|
+
identifier: string;
|
11
|
+
span: string;
|
12
|
+
interestingIndex: number;
|
13
|
+
} | null;
|
14
|
+
checkImports(typeDecl: string): {
|
15
|
+
identifier: string;
|
16
|
+
span: string;
|
17
|
+
interestingIndex: number;
|
18
|
+
} | null;
|
19
|
+
checkModule(typeDecl: string): {
|
20
|
+
identifier: string;
|
21
|
+
span: string;
|
22
|
+
interestingIndex: number;
|
23
|
+
} | null;
|
24
|
+
checkObject(typeDecl: string): {
|
25
|
+
identifier: string;
|
26
|
+
span: string;
|
27
|
+
interestingIndex: number;
|
28
|
+
} | null;
|
29
|
+
checkUnion(typeDecl: string): {
|
30
|
+
identifier: string;
|
31
|
+
span: string;
|
32
|
+
interestingIndex: number;
|
33
|
+
} | null;
|
34
|
+
checkFunction(typeDecl: string): {
|
35
|
+
identifier: string;
|
36
|
+
span: string;
|
37
|
+
interestingIndex: number;
|
38
|
+
} | null;
|
39
|
+
checkHole(typeDecl: string): {
|
40
|
+
identifier: string;
|
41
|
+
span: string;
|
42
|
+
} | null;
|
43
|
+
checkParameter(typeDecl: string): null;
|
44
|
+
isTuple(typeSpan: string): boolean;
|
45
|
+
isUnion(typeSpan: string): boolean;
|
46
|
+
isArray(typeSpan: string): boolean;
|
47
|
+
isObject(typeSpan: string): boolean;
|
48
|
+
isFunction(typeSpan: string): boolean;
|
49
|
+
isPrimitive(typeSpan: string): boolean;
|
50
|
+
isTypeAlias(typeSpan: string): boolean;
|
51
|
+
escapeQuotes(typeSpan: string): string;
|
52
|
+
parseTypeArrayString(typeStr: string): string[];
|
53
|
+
handleMembers(members: ts.NodeArray<ts.TypeElement> | ts.NodeArray<ts.ClassElement>, checker: ts.TypeChecker): TypeAnalysis[];
|
54
|
+
analyzeTypeNode(typeNode: ts.TypeNode, checker: ts.TypeChecker): TypeAnalysis;
|
55
|
+
analyzeTypeString(typeString: string, program?: ts.Program): TypeAnalysis;
|
56
|
+
createProgramFromSource(content: string): ts.Program;
|
57
|
+
isPrimitive2(typeAnalysisResult: TypeAnalysis): RegExpMatchArray | null;
|
58
|
+
isFunction2(typeAnalysisResult: TypeAnalysis): boolean;
|
59
|
+
isTuple2(typeAnalysisResult: TypeAnalysis): boolean;
|
60
|
+
isObject2(typeAnalysisResult: TypeAnalysis): boolean;
|
61
|
+
isUnion2(typeAnalysisResult: TypeAnalysis): boolean;
|
62
|
+
isArray2(typeAnalysisResult: TypeAnalysis): boolean;
|
63
|
+
isTypeAlias2(typeAnalysisResult: TypeAnalysis): boolean;
|
64
|
+
}
|