@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.
@@ -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
+ }