@bikky/compiler 0.0.1

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.
Files changed (42) hide show
  1. package/ASTHelper.d.ts +74 -0
  2. package/ASTHelper.d.ts.map +1 -0
  3. package/BiscuitCompiler.njsproj +89 -0
  4. package/BiscuitCompiler.njsproj.user +7 -0
  5. package/Libraries/BiscuitLibraries.d.ts +3 -0
  6. package/Libraries/BiscuitLibraries.d.ts.map +1 -0
  7. package/Libraries/BiscuitLibraries.js +3 -0
  8. package/Libraries/BiscuitLibraries.js.map +1 -0
  9. package/Libraries/BiscuitLibraries.ts +2 -0
  10. package/Libraries/GlobalTypes.d.ts +57 -0
  11. package/Libraries/MixinCode.d.ts +14 -0
  12. package/Libraries/MixinCode.d.ts.map +1 -0
  13. package/Libraries/MixinCode.js +109 -0
  14. package/Libraries/MixinCode.js.map +1 -0
  15. package/Libraries/MixinCode.ts +125 -0
  16. package/Libraries/package-lock.json +5 -0
  17. package/Source/ASTHelper.js +344 -0
  18. package/Source/ASTHelper.js.map +1 -0
  19. package/Source/ASTHelper.ts +373 -0
  20. package/Source/TSPatchTypes.d.ts +17 -0
  21. package/Transformers/CompilerInsertions.ts +21 -0
  22. package/Transformers/MacroTransformer.js +273 -0
  23. package/Transformers/MacroTransformer.js.map +1 -0
  24. package/Transformers/MacroTransformer.ts +304 -0
  25. package/Transformers/Macros.d.ts +3 -0
  26. package/Transformers/Macros.d.ts.map +1 -0
  27. package/Transformers/MixinTransformer.js +124 -0
  28. package/Transformers/MixinTransformer.js.map +1 -0
  29. package/Transformers/MixinTransformer.ts +151 -0
  30. package/Transformers/SuperTransform.d.ts +3 -0
  31. package/Transformers/SuperTransform.d.ts.map +1 -0
  32. package/Transformers/SuperTransform.js +117 -0
  33. package/Transformers/SuperTransform.js.map +1 -0
  34. package/Transformers/SuperTransform.ts +129 -0
  35. package/bin/Microsoft.NodejsTools.WebRole.dll +0 -0
  36. package/obj/Debug/BiscuitCompiler.njsproj.AssemblyReference.cache +0 -0
  37. package/obj/Debug/BiscuitCompiler.njsproj.CoreCompileInputs.cache +1 -0
  38. package/obj/Debug/BiscuitCompiler.njsproj.FileListAbsolute.txt +3 -0
  39. package/package.json +23 -0
  40. package/tsconfig.build.json +20 -0
  41. package/tsconfig.json +26 -0
  42. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,373 @@
1
+ 
2
+ import * as ts from "typescript";
3
+
4
+ export function Identifier(context: ts.TransformationContext, name: string) {
5
+ return context.factory.createIdentifier(name);
6
+ }
7
+
8
+ export function StringLiteral(context: ts.TransformationContext, name: string) {
9
+ return context.factory.createStringLiteral(name);
10
+ }
11
+
12
+ export function StaticModifier(context: ts.TransformationContext) {
13
+ return context.factory.createModifiersFromModifierFlags(ts.ModifierFlags.Static)[0];
14
+ }
15
+
16
+ export interface fromable {
17
+ from: (param: any)=> any;
18
+ }
19
+
20
+ export class NamedExpression {
21
+ name: ts.Identifier;
22
+
23
+ constructor(name: ts.Identifier) {
24
+ this.name = name;
25
+ }
26
+
27
+ getName() {
28
+ return this.name;
29
+ }
30
+
31
+ rename(name: ts.Identifier) {
32
+ this.name = name;
33
+ return this;
34
+ }
35
+ }
36
+
37
+ export class VariableDeclaration extends NamedExpression {
38
+ modifiers?: ts.Modifier[] = [];
39
+ value?: ts.Expression;
40
+
41
+
42
+ setValue(value: ts.Expression) {
43
+ this.value = value;
44
+ return this;
45
+ }
46
+
47
+ addModifier(modifier: ts.Modifier) {
48
+ if (!this.modifiers) {
49
+ this.modifiers = [];
50
+ }
51
+ this.modifiers.push(modifier);
52
+ return this;
53
+ }
54
+
55
+ make(context: ts.TransformationContext): ts.Node {
56
+ return context.factory.createVariableStatement(this.modifiers,
57
+ context.factory.createVariableDeclarationList(
58
+ [context.factory.createVariableDeclaration(this.name, undefined, undefined, this.value)]));
59
+ }
60
+ }
61
+
62
+ export module VariableDeclaration {
63
+ export function from(variable: ts.VariableDeclaration): VariableDeclaration {
64
+ var result = new VariableDeclaration(<ts.Identifier>variable.name);
65
+ result.value = variable.initializer;
66
+ result.modifiers = variable.modifiers ? Array.from(variable.modifiers) : undefined;
67
+ return result;
68
+ }
69
+ }
70
+
71
+ export class PropertyDeclaration extends VariableDeclaration {
72
+
73
+ //Property declarations can support a few more name types than normal variables.
74
+ constructor(name: ts.Identifier | ts.ComputedPropertyName) {
75
+ super(<ts.Identifier>name);
76
+ }
77
+
78
+ make(context: ts.TransformationContext) {
79
+ return context.factory.createPropertyDeclaration([], this.modifiers, this.name, undefined, undefined, this.value);
80
+ }
81
+ }
82
+
83
+ export module PropertyDeclaration {
84
+ export function from(variable: ts.PropertyDeclaration | ts.VariableDeclaration): PropertyDeclaration {
85
+ var result = new PropertyDeclaration(<ts.Identifier>variable.name);
86
+ result.value = variable.initializer;
87
+ result.modifiers = variable.modifiers ? Array.from(variable.modifiers) : undefined;
88
+ return result;
89
+ }
90
+ }
91
+
92
+ export class CallExpression extends NamedExpression {
93
+ arguments: ts.Expression[] = [];
94
+ addArgument(...arg: ts.Expression[]) {
95
+ this.arguments.push(...arg);
96
+ return this;
97
+ }
98
+ make(context: ts.TransformationContext) {
99
+ return context.factory.createCallExpression(this.name, undefined, this.arguments);
100
+ }
101
+ }
102
+
103
+ //this[super].function.call(this, arguments);
104
+ export class AccessExpression {
105
+ start!: ts.Expression | ts.Identifier;
106
+ chain!: ts.AccessExpression;
107
+ context: ts.TransformationContext;
108
+ constructor(context: ts.TransformationContext) {
109
+ this.context = context;
110
+ }
111
+ this() {
112
+ if (!this.start) {
113
+ this.start = this.context.factory.createThis();
114
+ }
115
+ else {
116
+ throw new Error("This can only be the first expression in an access chain");
117
+ }
118
+ return this;
119
+ }
120
+ access(node: ts.Expression | ts.Identifier | ts.ComputedPropertyName) {
121
+ if (!this.start) {
122
+ this.start = <ts.Identifier>node;
123
+ }
124
+ else {
125
+ if (!this.chain) {
126
+ if (this.start.kind === ts.SyntaxKind.ThisKeyword) {
127
+ this.chain = this.context.factory.createElementAccessExpression(this.start, <ts.Identifier>node);
128
+ }
129
+ else {
130
+ this.chain = this.context.factory.createPropertyAccessExpression(this.start, <ts.Identifier>node);
131
+ }
132
+ }
133
+ else {
134
+ this.chain = this.context.factory.createPropertyAccessExpression(this.chain, <ts.Identifier>node);
135
+ }
136
+ }
137
+ return this;
138
+ }
139
+ make() {
140
+ return this.chain;
141
+ }
142
+ }
143
+
144
+ export class ClassDeclaration extends NamedExpression {
145
+ original?: ts.ClassDeclaration;
146
+ decorators: ts.Decorator[] = [];
147
+ extends: ts.Expression | undefined;
148
+ members: (ts.ClassElement)[] = [];
149
+
150
+ static from(oldClass: ts.ClassDeclaration) {
151
+ var Class = new ClassDeclaration(oldClass.name!);
152
+ Class.original = oldClass;
153
+ if (oldClass.heritageClauses) {
154
+ for (var clause of oldClass.heritageClauses) {
155
+ if ((<ts.HeritageClause>clause).token == ts.SyntaxKind.ExtendsKeyword) {
156
+ Class.extends = clause.types[0].expression;
157
+ }
158
+ }
159
+ }
160
+ Class.members = Array.from(oldClass.members);
161
+ if (oldClass.decorators) {
162
+ Class.decorators = Array.from(oldClass.decorators);
163
+ }
164
+ return Class;
165
+ }
166
+
167
+ addMemberAtStart(member: ts.ClassElement) {
168
+ this.members.unshift(member);
169
+ return this;
170
+ }
171
+
172
+ addMember(member: ts.ClassElement) {
173
+ this.members.push(member);
174
+ return this;
175
+ }
176
+
177
+ hasDecorator(identifier: string | ts.Identifier) {
178
+ var name = (typeof identifier === "string") ? identifier : identifier.text;
179
+ for (var i = 0; i < this.decorators.length; i++) {
180
+ var decorator = this.decorators[i];
181
+ if (ts.isIdentifier(decorator.expression) && decorator.expression.text == name) {
182
+ return true;
183
+ }
184
+ else if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() == name) {
185
+ return true;
186
+ }
187
+ }
188
+ return false;
189
+ }
190
+
191
+ removeDecorator(identifier: string | ts.Identifier) {
192
+ var name = (typeof identifier === "string") ? identifier : identifier.text;
193
+ for (var i = 0; i < this.decorators.length; i++) {
194
+ var decorator = this.decorators[i];
195
+ if (ts.isIdentifier(decorator.expression) && decorator.expression.text == name) {
196
+ this.decorators.splice(i, 1);
197
+ return this;
198
+ }
199
+ else if (ts.isCallExpression(decorator.expression) && decorator.expression.expression.getText() == name) {
200
+ this.decorators.splice(i, 1);
201
+ return this;
202
+ }
203
+ }
204
+ return this;
205
+ }
206
+
207
+ hasExtends() {
208
+ return !!this.extends;
209
+ }
210
+
211
+ getExtends() {
212
+ return this.extends;
213
+ }
214
+
215
+ setExtends(Extends: ts.Expression | undefined) {
216
+ this.extends = Extends;
217
+ return this;
218
+ }
219
+
220
+ static eachContextualNode(Class: ts.ClassDeclaration, foo: ts.Visitor, context: ts.TransformationContext) {
221
+ var statement = (node: ts.Node): ts.VisitResult<ts.Node> => {
222
+ if (ts.isBlock(node)) {
223
+ return blocks(node);
224
+ }
225
+ else if (ts.isArrowFunction(node)) {
226
+ return members(node);
227
+ }
228
+ else {
229
+ var result = foo(node);
230
+ if (!result) {
231
+ return result;
232
+ }
233
+ if (Array.isArray(result)) {
234
+ //TODO: Should probably do something smart here?
235
+ return result;
236
+ }
237
+ return ts.visitEachChild(result, statement, context);
238
+ }
239
+ };
240
+
241
+ var blocks = (node: ts.Node): ts.VisitResult<ts.Node> => {
242
+ if (ts.isBlock(node)) {
243
+ return ts.visitEachChild(node, statement, context);
244
+ }
245
+ return node;
246
+ };
247
+ var members = (node: ts.Node): ts.VisitResult<ts.Node> => {
248
+ if (ts.isMethodDeclaration(node)) {
249
+ return ts.visitEachChild(node, blocks, context);
250
+ }
251
+ else {
252
+ return node;
253
+ }
254
+ };
255
+ return ts.visitEachChild(Class, members, context);
256
+ }
257
+
258
+ update(context: ts.TransformationContext) {
259
+ if (!this.original) {
260
+ throw new Error("Cannot update class that doesn't exist!");
261
+ }
262
+
263
+ var replaceNodeArrays = (nodes: ts.NodeArray<any> | undefined, visitor: ts.Visitor | undefined, test?: (node: ts.Node) => boolean, start?: number, count?: number): ts.NodeArray<ts.Node> => {
264
+ if (nodes == this.original!.decorators) {
265
+ return context.factory.createNodeArray(this.decorators);
266
+ }
267
+ if (nodes == this.original!.members) {
268
+ return context.factory.createNodeArray(this.members);
269
+ }
270
+ if (nodes) {
271
+ var changedArray = false;
272
+ var newArray = [];
273
+ for (var node of nodes) {
274
+ var newNode = ts.visitNode(node, visitor, test);
275
+ newArray.push(newNode);
276
+ if (node != newNode) {
277
+ changedArray = true;
278
+ }
279
+ }
280
+ if (changedArray) {
281
+ return context.factory.createNodeArray(newArray);
282
+ }
283
+ }
284
+ return nodes!;
285
+ };
286
+
287
+ var replaceExports: ts.Visitor = (node: ts.Node) => {
288
+ if (node.kind == ts.SyntaxKind.HeritageClause && (<ts.HeritageClause>node).token == ts.SyntaxKind.ExtendsKeyword) {
289
+ //I hope this works...
290
+ var result = context.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.createExpressionWithTypeArguments(undefined, this.extends!)]);
291
+ return result;
292
+ }
293
+ return node;
294
+ }
295
+
296
+ //first parameter is the class, second is the thing to edit individual nodes, third is the context
297
+ // last parameter is the thing to edit entire node arrays.
298
+ return ts.visitEachChild(this.original, replaceExports, context, replaceNodeArrays);
299
+ }
300
+ }
301
+
302
+ export class SourceFile {
303
+ original?: ts.SourceFile;
304
+ statements: ts.Statement[] = [];
305
+
306
+ static from(oldfile: ts.SourceFile) {
307
+ var file = new SourceFile;
308
+ file.statements = Array.from(oldfile.statements);
309
+ file.original = oldfile;
310
+ return file;
311
+ }
312
+
313
+ replaceNode(oldNode: ts.Statement, newNode: ts.Statement) {
314
+ var index = this.statements.indexOf(oldNode);
315
+ if (index < 0) {
316
+ throw new Error("Tried to replace a node that doesn't exist in file");
317
+ }
318
+ this.statements[index] = newNode;
319
+ return this;
320
+ }
321
+
322
+ insertUnderImports(node: ts.Statement) {
323
+ for (var i = 0; i < this.statements.length; i++) {
324
+ if (this.statements[i].kind !== ts.SyntaxKind.ImportDeclaration) {
325
+ this.statements.splice(i, 0, node);
326
+ break;
327
+ }
328
+ }
329
+ return this;
330
+ }
331
+
332
+ update(context: ts.TransformationContext) {
333
+ if (!this.original) {
334
+ throw new Error("Cannot update file that doesn't exist!");
335
+ }
336
+ var newSource = ts.getMutableClone(this.original);
337
+ //Cast to any because mutable clones use the same immutable typing as their original node.
338
+ (<any>newSource).statements = context.factory.createNodeArray(this.statements);
339
+ return newSource;
340
+ }
341
+ }
342
+
343
+
344
+ export function logError(node: ts.Node, error: string): undefined {
345
+ if (!node || !node.getSourceFile()) {
346
+ console.error("Why are we here?");
347
+ return;
348
+ }
349
+ var source = ts.isSourceFile(node) ? node : node.getSourceFile();
350
+ var pos = source.getLineAndCharacterOfPosition(node.pos);
351
+ console.error(`${source.fileName}(${pos.line},${pos.character}):`, error);
352
+ // This can cause the compiler to stop, but we're not using it because the above will allow
353
+ // us to go to the right location in the file. And we probably want to return what we can if
354
+ // there's an error since that's how the rest of typescript works.
355
+ //ts.createThrow(ts.createStringLiteral("Compiler Error."));
356
+ return;
357
+ }
358
+
359
+ export module Printer {
360
+ var printer = ts.createPrinter();
361
+
362
+ export function printNode(node: ts.Node, sourceFile: ts.SourceFile) {
363
+ return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
364
+ }
365
+
366
+ export function printNodeFirstLine(node: ts.Node, sourceFile: ts.SourceFile) {
367
+ var code = printNode(node, node.getSourceFile());
368
+ //space matches tab.
369
+ code = code.replace(/^ *{? *\r\n?/, "");
370
+ return code.replace(/\r\n.*/gi, "");
371
+
372
+ }
373
+ }
@@ -0,0 +1,17 @@
1
+ import * as ts from "typescript";
2
+ import { ProgramTransformerExtras, PluginConfig } from 'ts-patch';
3
+
4
+
5
+ type ProgramPatchableTS = typeof ts & {
6
+ createProgram(rootNames: string[], compilerOptions: PluginConfig, host: ts.CompilerHost | undefined, oldProgram: ts.Program): ts.Program;
7
+ }
8
+
9
+
10
+
11
+ type ProgramTransformer = (program: ts.Program, host: ts.CompilerHost | undefined, options: PluginConfig, pte: ProgramTransformerExtras) => ts.Program;
12
+
13
+
14
+ type SourceTransformer = (program: ts.Program, pluginOptions: {}) =>
15
+ (context: ts.TransformationContext) =>
16
+ (sourceFile: ts.SourceFile) => ts.SourceFile;
17
+
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Adds BikkyCompiler type files and supporting code into the program at compile-time.
3
+ */
4
+
5
+ import * as ts from 'typescript';
6
+ import * as path from 'path';
7
+ import { ProgramTransformerExtras, PluginConfig } from 'ts-patch';
8
+ import {ProgramPatchableTS} from "../Source/TSPatchTypes.js";
9
+
10
+ export const newFile = path.resolve(__dirname, '../Libraries/GlobalTypes.d.ts');
11
+
12
+
13
+ export default function (program: ts.Program, host: ts.CompilerHost | undefined, options: PluginConfig, pte: ProgramTransformerExtras) {
14
+ const tsInstance : ProgramPatchableTS = pte.ts;
15
+ return tsInstance.createProgram(
16
+ /* rootNames */ [newFile, ...program.getRootFileNames()],
17
+ program.getCompilerOptions(),
18
+ host,
19
+ /* oldProgram */ program
20
+ );
21
+ }
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // transformer1-module
4
+ const ts = require("typescript");
5
+ //This class should be able to do a few things:
6
+ // Import macros from other files.
7
+ // Copy macro AST
8
+ // Insert macro AST to the destination location (inlining the macro).
9
+ // Resolve variable names to the name of the provided variable.
10
+ // Not require special (complex) syntax in the macro creation.
11
+ class MacroTransformer {
12
+ constructor() {
13
+ this.RegisteredMacros = new Map();
14
+ this.DeclarationName = "MACRO";
15
+ this.printer = ts.createPrinter();
16
+ }
17
+ logError(node, error) {
18
+ if (!node || !node.getSourceFile()) {
19
+ console.error("Why are we here?");
20
+ return;
21
+ }
22
+ var source = ts.isSourceFile(node) ? node : node.getSourceFile();
23
+ var pos = source.getLineAndCharacterOfPosition(node.pos);
24
+ console.error(`${source.fileName}(${pos.line},${pos.character}):`, error);
25
+ // This can cause the compiler to stop, but we're not using it because the above will allow
26
+ // us to go to the right location in the file. And we probably want to return what we can if
27
+ // there's an error since that's how the rest of typescript works.
28
+ //ts.createThrow(ts.createStringLiteral("Compiler Error."));
29
+ return;
30
+ }
31
+ printNode(node, sourceFile) {
32
+ return this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
33
+ }
34
+ printNodeFirstLine(node, sourceFile) {
35
+ var code = this.printNode(node, node.getSourceFile());
36
+ //space matches tab.
37
+ code = code.replace(/^ *{? *\r\n?/, "");
38
+ return code.replace(/\r\n.*/gi, "");
39
+ }
40
+ CrawlExpressionTree(node) {
41
+ //Run twice, we hoist macro declarations.
42
+ node = ts.visitNode(node, this.FindMacroDeclaration.bind(this));
43
+ return ts.visitNode(node, this.SearchForMacroUseStatement.bind(this));
44
+ }
45
+ FindMacroDeclaration(node) {
46
+ if (ts.isVariableStatement(node)) {
47
+ //This statement has potential to contain a MACRO declaration.
48
+ //Support for comma separated declarations.
49
+ for (var declaration of node.declarationList.declarations) {
50
+ //If this variable is initialised, and it's initialised with a function call check to see
51
+ // if the function's name is MACRO (which is our custom buzzword).
52
+ if (declaration.initializer
53
+ && ts.isCallExpression(declaration.initializer)
54
+ && ts.isIdentifier(declaration.initializer.expression)
55
+ && declaration.initializer.expression.text === this.DeclarationName) {
56
+ //This is the name of the variable that the macro's being assigned to.
57
+ var name = declaration.name;
58
+ //Macros must be identifiers (normal variables, not symbols and not array unwraps).
59
+ if (!ts.isIdentifier(name)) {
60
+ this.logError(declaration, "Macros must be declared as using normal identifiers, symbols and array unwraps are unsupported.");
61
+ return node;
62
+ }
63
+ if (declaration.initializer.arguments.length == 0) {
64
+ this.logError(declaration, "When declaring a macro a function must be supplied as the first parameter.");
65
+ return node;
66
+ }
67
+ if (declaration.initializer.arguments.length > 1) {
68
+ this.logError(declaration, "Macro declaration only takes one parameter.");
69
+ return node;
70
+ }
71
+ var value = declaration.initializer.arguments[0];
72
+ this.RegisteredMacros.set(name.text, value);
73
+ console.log("Registering macro", name.text);
74
+ //Now that we've stored the macro code for later use, we need to delete it from the file.
75
+ //Todo: this creates an unnecessary semicolon.
76
+ return ts.createEmptyStatement();
77
+ }
78
+ }
79
+ }
80
+ node = ts.visitEachChild(node, this.FindMacroDeclaration.bind(this), this.context) || node;
81
+ return node;
82
+ }
83
+ SearchForMacroUseStatement(node) {
84
+ if (ts.isBlock(node) || ts.isSourceFile(node) || ts.isCaseBlock(node)) {
85
+ //console.log("Block:", this.printNodeFirstLine(node, node.getSourceFile()));
86
+ return ts.visitEachChild(node, this.FindMacroUsage.bind(this), this.context);
87
+ }
88
+ else if (ts.isFunctionDeclaration(node)) {
89
+ var body = ts.visitEachChild(node.body, this.FindMacroUsage.bind(this), this.context);
90
+ let foo = ts.updateFunctionDeclaration(node, node.decorators, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, body);
91
+ //if (foo.name) {
92
+ // console.log("Function:", this.printNode(foo.name, node.getSourceFile()));
93
+ //}
94
+ return foo;
95
+ }
96
+ else if (ts.isMethodDeclaration(node)) {
97
+ var body = ts.visitEachChild(node.body, this.FindMacroUsage.bind(this), this.context);
98
+ let foo = ts.updateMethod(node, node.decorators, node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, body);
99
+ //console.log("Method:", this.printNode(foo.name, node.getSourceFile()));
100
+ return foo;
101
+ }
102
+ else if (ts.isIfStatement(node)) {
103
+ //console.log("IF:", this.printNode(node, node.getSourceFile()));
104
+ var thenS = ts.visitNode(node.thenStatement, this.SearchForMacroUseStatement.bind(this));
105
+ var elseS = node.elseStatement ? ts.visitNode(node.elseStatement, this.SearchForMacroUseStatement.bind(this)) : undefined;
106
+ return ts.updateIf(node, node.expression, thenS, elseS);
107
+ }
108
+ else {
109
+ return ts.visitEachChild(node, this.SearchForMacroUseStatement.bind(this), this.context);
110
+ }
111
+ }
112
+ isBlocky(node) {
113
+ return ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isIfStatement(node);
114
+ }
115
+ FindMacroUsage(node) {
116
+ if (this.isBlocky(node)) {
117
+ return ts.visitNode(node, this.SearchForMacroUseStatement.bind(this));
118
+ }
119
+ var MACARONY = false;
120
+ var context = undefined;
121
+ var call = undefined;
122
+ //How to do this:
123
+ //Recurse through statement to see if it has a macro expression.
124
+ function SpyMacro(node) {
125
+ //If there's a new block in here we bail, otherwise we'd put the macro scoping in the wrong scoping level.
126
+ if (ts.isBlock(node) || ts.isCaseBlock(node) || this.isBlocky(node)) {
127
+ return ts.visitNode(node, this.SearchForMacroUseStatement.bind(this));
128
+ }
129
+ if (ts.isCallExpression(node)
130
+ && ts.isPropertyAccessExpression(node.expression)
131
+ && this.RegisteredMacros.has(node.expression.name.text)) {
132
+ MACARONY = true;
133
+ context = node.expression.expression;
134
+ call = node;
135
+ //console.log("FOUND MACRO USAGE!", node.expression.name.text);
136
+ }
137
+ if (ts.isCallExpression(node) &&
138
+ ts.isIdentifier(node.expression) &&
139
+ this.RegisteredMacros.has(node.expression.text)) {
140
+ MACARONY = true;
141
+ call = node;
142
+ }
143
+ return ts.visitEachChild(node, SpyMacro.bind(this), this.context);
144
+ }
145
+ ts.visitEachChild(node, SpyMacro.bind(this), this.context);
146
+ if (MACARONY) {
147
+ return this.InsertMacro(node, context, call);
148
+ }
149
+ else {
150
+ return node;
151
+ }
152
+ }
153
+ //"context" is the thing before the dot when the macro is called, might be undefined.
154
+ //"context" is present if you call the macro like so: context.MACRO().
155
+ //Call expression is the node that represents the MACRO() part of the statement.
156
+ InsertMacro(statement, context, macroCall) {
157
+ var block = [];
158
+ var macro;
159
+ var macroName;
160
+ if (ts.isIdentifier(macroCall.expression)) {
161
+ macroName = macroCall.expression.text;
162
+ }
163
+ else if (ts.isPropertyAccessExpression(macroCall.expression)) {
164
+ macroName = macroCall.expression.name.text;
165
+ }
166
+ if (macroName) {
167
+ macro = this.RegisteredMacros.get(macroName);
168
+ }
169
+ if (!macroName || !macro) {
170
+ this.logError(statement, "Unspecified error in macro expansion.");
171
+ return statement;
172
+ }
173
+ //console.log("Putting", macroName, "in to place:", statement.getText());
174
+ //Map macro parameters to the arguments (replace all instances of the parameter with the argument's expression).
175
+ //Replacing identifiers with nodes because it can be an unnamed expression (lambda or 3 + 5).
176
+ var parameters = new Map();
177
+ var parameterOffset = 0;
178
+ for (var i = 0; i + parameterOffset < macro.parameters.length; i++) {
179
+ var parameter = macro.parameters[i + parameterOffset];
180
+ //Detect and skip "this" parameter
181
+ if (ts.isParameter(parameter) && parameter.name.getText() == "this") {
182
+ parameterOffset = 1;
183
+ //If "this" is the only parameter then don't do the rest of the loop.
184
+ if (!(i + parameterOffset < macro.parameters.length)) {
185
+ break;
186
+ }
187
+ parameter = macro.parameters[i + parameterOffset];
188
+ }
189
+ var argument = macroCall.arguments[i];
190
+ if (typeof argument === "undefined") {
191
+ if (typeof parameter.questionToken === "undefined") {
192
+ this.logError(parameter, `Failure to specify non-optional argument to macro ${macroName}.`);
193
+ return statement;
194
+ //throw new Error("Failure to specify non-optional argument to macro.");
195
+ }
196
+ else {
197
+ //Probably use this instead, because it's garenteed to be unique.
198
+ //ts.getGeneratedNameForNode
199
+ var name = parameter.name.getText();
200
+ //This is "let param;" for optional variables that weren't done.
201
+ var ver = ts.createVariableStatement([], ts.createVariableDeclarationList([ts.createVariableDeclaration(name)], ts.NodeFlags.Let));
202
+ block.push(ver);
203
+ parameters.set(name, ts.createIdentifier(name));
204
+ }
205
+ }
206
+ else {
207
+ parameters.set(parameter.name.getText(), argument);
208
+ }
209
+ }
210
+ //Macros can be multiline!
211
+ //So we need to insert the macro into the appropriate place in the block, and then duplicate expression
212
+ // wherever return shows up in the macro (or on the next line if it doesn't? or maybe error...).
213
+ var body = (macro.body);
214
+ //Put the macro's result in the calling expression in place of the macro call.
215
+ var PutResultInExpression = (result) => {
216
+ return function IterateResult(node) {
217
+ if (ts.isCallExpression(node)
218
+ && ts.isPropertyAccessExpression(node.expression)
219
+ && node.expression.name.text == macroName
220
+ || ts.isCallExpression(node)
221
+ && ts.isIdentifier(node.expression)
222
+ && node.expression.text == macroName) {
223
+ //replace with stuff after return statement.
224
+ return ts.visitNode(result.expression, ReplaceStuffInMacro.bind(this));
225
+ }
226
+ return ts.visitEachChild(node, IterateResult.bind(this), this.context);
227
+ }.bind(this);
228
+ };
229
+ function CopyAsIs(node) {
230
+ return ts.visitEachChild(node, CopyAsIs.bind(this), this.context);
231
+ }
232
+ function ReplaceStuffInMacro(node) {
233
+ if (ts.isReturnStatement(node)) {
234
+ return ts.visitEachChild(statement, PutResultInExpression(node), this.context);
235
+ }
236
+ if (ts.isPropertyAccessExpression(node) && (node.expression.kind == ts.SyntaxKind.ThisType || node.expression.kind == ts.SyntaxKind.ThisKeyword)) {
237
+ if (!context) {
238
+ this.logError(node, "Macro can only be executed as a property accessor (value.MACRO) but here is not, this causes an error. MACRO.bind is not supported use value.MACRO instead.");
239
+ return node;
240
+ }
241
+ //If this is referenced then replace it with the stuff before the . when the macro is called.
242
+ return ts.createPropertyAccess(context, node.name.text);
243
+ //return ts.visitEachChild(context, Inplace.bind(this), this.context);
244
+ }
245
+ if (ts.isIdentifier(node) && parameters.has(node.text)) {
246
+ //If a parameter is referenced replace it with the argument provided in macro use.
247
+ return ts.visitNode(parameters.get(node.text), CopyAsIs.bind(this));
248
+ }
249
+ return ts.visitEachChild(node, ReplaceStuffInMacro.bind(this), this.context);
250
+ }
251
+ //unroll macro and then replace all return statements with expression.
252
+ for (var expression of body.statements) {
253
+ block.push(ts.visitNode(expression, ReplaceStuffInMacro.bind(this)));
254
+ }
255
+ var result = ts.createBlock(block);
256
+ return result;
257
+ }
258
+ }
259
+ //Initiating the transformer here is a hodge-podge solution to resolving transforms supplied in other files.
260
+ // We probably need to do a proper solution that involves aliasing the macro's name to whatever the import is called, and removing
261
+ // the import statement if the only thing that's imported is macros. On second thought that last part is really hard so let's not
262
+ // do that.
263
+ var transformer = new MacroTransformer();
264
+ function default_1(program, pluginOptions) {
265
+ return (context) => {
266
+ transformer.context = context;
267
+ return (sourceFile) => {
268
+ return transformer.CrawlExpressionTree(sourceFile);
269
+ };
270
+ };
271
+ }
272
+ exports.default = default_1;
273
+ //# sourceMappingURL=Macros.js.map