@gi-tcg/gts-transpiler 0.1.1 → 0.3.0

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.
@@ -2,14 +2,12 @@ import type {
2
2
  ArrayExpression,
3
3
  ArrowFunctionExpression,
4
4
  BlockStatement,
5
- CallExpression,
6
5
  Declaration,
7
- ExportNamedDeclaration,
8
6
  Expression,
9
- ExpressionStatement,
10
7
  Identifier,
11
8
  Literal,
12
9
  MemberExpression,
10
+ ModuleDeclaration,
13
11
  Node,
14
12
  ObjectExpression,
15
13
  ObjectPattern,
@@ -27,57 +25,59 @@ import {
27
25
  export interface ExternalizedBinding {
28
26
  bindingName: Identifier;
29
27
  export: boolean;
30
- internalId: Identifier;
31
- value: Expression;
32
- path: string[];
33
28
  }
34
29
 
35
30
  export interface TranspileState {
36
31
  readonly createDefineFnId: Identifier;
37
- readonly ActionId: Identifier;
38
- readonly preludeSymbolId: Identifier;
32
+ readonly createBindingFnId: Identifier;
33
+ readonly ActionLit: Literal;
34
+ readonly PreludeLit: Literal;
39
35
  readonly fnArgId: Identifier;
40
36
  readonly shortcutFunctionParameters: Pattern[];
41
37
  readonly rootVmId: Identifier;
42
- readonly binderFnId: Identifier;
43
38
  readonly queryFnId: Identifier;
44
39
  readonly queryParameters: Pattern[];
45
40
 
46
41
  readonly runtimeImportSource: string;
47
42
  readonly providerImportSource: string;
48
43
  readonly queryArg: ObjectPattern;
49
-
50
- readonly attributeNames: (Identifier | Literal)[];
51
- readonly externalizedBindings: ExternalizedBinding[];
44
+
52
45
  hasQueryExpressions: boolean;
46
+
47
+ externalizedBindings: ExternalizedBinding[];
48
+ /** Internal counters / state for emitting per-define nodes & bindings */
49
+ defineIdCounter: number;
50
+
51
+ /** Binding statements to be inserted before all define statement */
52
+ bindingStatements: (Statement | ModuleDeclaration)[];
53
53
  }
54
54
 
55
55
  export const commonGtsVisitor: Visitors<Node, TranspileState> = {
56
- GTSDirectFunction(node, { visit, state }): ArrowFunctionExpression {
56
+ GTSDirectFunction(node, { visit, state }): ObjectExpression {
57
57
  return {
58
- type: "ArrowFunctionExpression",
59
- params: [],
60
- body: {
61
- type: "ObjectExpression",
62
- properties: [
63
- {
64
- type: "Property",
65
- key: { type: "Identifier", name: "name" },
66
- computed: false,
67
- kind: "init",
68
- method: false,
69
- shorthand: false,
70
- value: state.ActionId,
71
- loc: node.loc,
72
- },
73
- {
74
- type: "Property",
75
- key: { type: "Identifier", name: "positionals" },
76
- computed: false,
77
- kind: "init",
78
- method: false,
79
- shorthand: false,
80
- value: {
58
+ type: "ObjectExpression",
59
+ properties: [
60
+ {
61
+ type: "Property",
62
+ key: { type: "Identifier", name: "name" },
63
+ computed: false,
64
+ kind: "init",
65
+ method: false,
66
+ shorthand: false,
67
+ value: state.ActionLit,
68
+ loc: node.loc,
69
+ },
70
+ {
71
+ type: "Property",
72
+ key: { type: "Identifier", name: "positionals" },
73
+ computed: false,
74
+ kind: "init",
75
+ method: false,
76
+ shorthand: false,
77
+ value: {
78
+ type: "ArrowFunctionExpression",
79
+ params: [],
80
+ body: {
81
81
  type: "ArrayExpression",
82
82
  elements: [
83
83
  {
@@ -91,22 +91,22 @@ export const commonGtsVisitor: Visitors<Node, TranspileState> = {
91
91
  },
92
92
  ],
93
93
  },
94
+ expression: true,
94
95
  },
95
- {
96
- type: "Property",
97
- key: { type: "Identifier", name: "named" },
98
- computed: false,
99
- kind: "init",
100
- method: false,
101
- shorthand: false,
102
- value: {
103
- type: "Literal",
104
- value: null,
105
- },
96
+ },
97
+ {
98
+ type: "Property",
99
+ key: { type: "Identifier", name: "named" },
100
+ computed: false,
101
+ kind: "init",
102
+ method: false,
103
+ shorthand: false,
104
+ value: {
105
+ type: "Literal",
106
+ value: null,
106
107
  },
107
- ],
108
- },
109
- expression: true,
108
+ },
109
+ ],
110
110
  loc: node.loc,
111
111
  };
112
112
  },
@@ -178,91 +178,16 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
178
178
  // }
179
179
  // return next();
180
180
  // },
181
- Program(node, { state, next }) {
182
- node = (next() as Program) ?? node;
183
- const body = [...node.body];
184
- if (state.externalizedBindings.length > 0) {
185
- body.unshift(
186
- {
187
- type: "ImportDeclaration",
188
- specifiers: [
189
- {
190
- type: "ImportDefaultSpecifier",
191
- local: state.binderFnId,
192
- },
193
- ],
194
- source: {
195
- type: "Literal",
196
- value: `${state.providerImportSource}/binder`,
197
- },
198
- attributes: [],
199
- },
200
- ...state.externalizedBindings.flatMap(
201
- (binding): (Declaration | ExportNamedDeclaration)[] => {
202
- const internalDecl: Declaration = {
203
- type: "VariableDeclaration",
204
- kind: "const",
205
- declarations: [
206
- {
207
- type: "VariableDeclarator",
208
- id: binding.internalId,
209
- init: binding.value,
210
- },
211
- ],
212
- };
213
- const externalDecl: Declaration = {
214
- type: "VariableDeclaration",
215
- kind: "const",
216
- declarations: [
217
- {
218
- type: "VariableDeclarator",
219
- id: binding.bindingName,
220
- init: {
221
- type: "CallExpression",
222
- optional: false,
223
- callee: state.binderFnId,
224
- arguments: [
225
- binding.internalId,
226
- {
227
- type: "ObjectExpression",
228
- properties: [
229
- {
230
- type: "Property",
231
- key: { type: "Identifier", name: "path" },
232
- computed: false,
233
- kind: "init",
234
- method: false,
235
- shorthand: false,
236
- value: {
237
- type: "ArrayExpression",
238
- elements: binding.path.map((segment) => ({
239
- type: "Literal",
240
- value: segment,
241
- })),
242
- },
243
- },
244
- ],
245
- },
246
- ],
247
- },
248
- },
249
- ],
250
- };
251
- return binding.export
252
- ? [
253
- internalDecl,
254
- {
255
- type: "ExportNamedDeclaration",
256
- declaration: externalDecl,
257
- specifiers: [],
258
- attributes: [],
259
- },
260
- ]
261
- : [internalDecl, externalDecl];
262
- }
263
- )
264
- );
181
+ Program(node, { state, visit }) {
182
+ const body: Program["body"] = [];
183
+ for (const stmt of node.body) {
184
+ const visited = visit(stmt) as Statement;
185
+ body.push(visited);
265
186
  }
187
+
188
+ body.unshift(...state.bindingStatements);
189
+ state.bindingStatements = [];
190
+
266
191
  if (state.hasQueryExpressions) {
267
192
  body.unshift({
268
193
  type: "ImportDeclaration",
@@ -279,70 +204,135 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
279
204
  attributes: [],
280
205
  });
281
206
  }
282
- body.unshift({
283
- type: "ImportDeclaration",
284
- specifiers: [
285
- {
286
- type: "ImportSpecifier",
287
- imported: { type: "Identifier", name: "createDefine" },
288
- local: state.createDefineFnId,
289
- },
290
- {
291
- type: "ImportSpecifier",
292
- imported: { type: "Identifier", name: "Action" },
293
- local: state.ActionId,
294
- },
295
- {
296
- type: "ImportSpecifier",
297
- imported: { type: "Identifier", name: "Prelude" },
298
- local: state.preludeSymbolId,
207
+ body.unshift(
208
+ {
209
+ type: "ImportDeclaration",
210
+ specifiers: [
211
+ {
212
+ type: "ImportSpecifier",
213
+ imported: { type: "Identifier", name: "createDefine" },
214
+ local: state.createDefineFnId,
215
+ },
216
+ {
217
+ type: "ImportSpecifier",
218
+ imported: { type: "Identifier", name: "createBinding" },
219
+ local: state.createBindingFnId,
220
+ },
221
+ ],
222
+ source: { type: "Literal", value: state.runtimeImportSource },
223
+ attributes: [],
224
+ },
225
+ {
226
+ type: "ImportDeclaration",
227
+ specifiers: [
228
+ {
229
+ type: "ImportDefaultSpecifier",
230
+ local: state.rootVmId,
231
+ },
232
+ ],
233
+ source: {
234
+ type: "Literal",
235
+ value: `${state.providerImportSource}/vm`,
299
236
  },
300
- ],
301
- source: { type: "Literal", value: state.runtimeImportSource },
302
- attributes: [],
303
- });
237
+ attributes: [],
238
+ }
239
+ );
304
240
  return {
305
241
  ...node,
306
242
  body,
307
243
  };
308
244
  },
309
- GTSDefineStatement(node, { state, visit }): ExpressionStatement {
310
- const body = visit(node.body) as Expression;
311
- const wrapper: ObjectExpression = {
312
- type: "ObjectExpression",
313
- properties: [
245
+ GTSDefineStatement(node, { state, visit }): Statement {
246
+ const defineId = state.defineIdCounter++;
247
+
248
+ const rootAttr = visit(node.body) as Expression;
249
+
250
+ const nodeVarId: Identifier = {
251
+ type: "Identifier",
252
+ name: `__gts_node_${defineId}`,
253
+ };
254
+ const bindingsVarId: Identifier = {
255
+ type: "Identifier",
256
+ name: `__gts_bindings_${defineId}`,
257
+ };
258
+
259
+ const newBindings = state.externalizedBindings;
260
+ state.externalizedBindings = [];
261
+
262
+ state.bindingStatements.push({
263
+ type: "VariableDeclaration",
264
+ kind: "const",
265
+ declarations: [
314
266
  {
315
- type: "Property",
316
- key: { type: "Identifier", name: "attributes" },
317
- computed: false,
318
- kind: "init",
319
- method: false,
320
- shorthand: false,
321
- value: {
322
- type: "ArrayExpression",
323
- elements: [body],
267
+ type: "VariableDeclarator",
268
+ id: nodeVarId,
269
+ init: rootAttr,
270
+ },
271
+ ],
272
+ loc: node.loc,
273
+ });
274
+
275
+ state.bindingStatements.push({
276
+ type: "VariableDeclaration",
277
+ kind: "const",
278
+ declarations: [
279
+ {
280
+ type: "VariableDeclarator",
281
+ id: bindingsVarId,
282
+ init: {
283
+ type: "CallExpression",
284
+ optional: false,
285
+ callee: state.createBindingFnId,
286
+ arguments: [state.rootVmId, nodeVarId],
324
287
  },
325
288
  },
326
289
  ],
327
- };
290
+ loc: node.loc,
291
+ });
292
+
293
+ for (let i = 0; i < newBindings.length; i++) {
294
+ const binding = newBindings[i];
295
+ const decl: Declaration = {
296
+ type: "VariableDeclaration",
297
+ kind: "const",
298
+ declarations: [
299
+ {
300
+ type: "VariableDeclarator",
301
+ id: binding.bindingName,
302
+ init: {
303
+ type: "MemberExpression",
304
+ object: bindingsVarId,
305
+ property: { type: "Literal", value: i },
306
+ computed: true,
307
+ optional: false,
308
+ },
309
+ },
310
+ ],
311
+ };
312
+ if (binding.export) {
313
+ state.bindingStatements.push({
314
+ type: "ExportNamedDeclaration",
315
+ declaration: decl,
316
+ specifiers: [],
317
+ attributes: [],
318
+ });
319
+ } else {
320
+ state.bindingStatements.push(decl);
321
+ }
322
+ }
328
323
  return {
329
324
  type: "ExpressionStatement",
330
325
  expression: {
331
326
  type: "CallExpression",
332
327
  optional: false,
333
- callee: {
334
- ...state.createDefineFnId,
335
- loc: node.loc,
336
- },
337
- arguments: [state.rootVmId, wrapper],
328
+ callee: state.createDefineFnId,
329
+ arguments: [state.rootVmId, nodeVarId],
338
330
  },
339
331
  loc: node.loc,
340
332
  };
341
333
  },
342
334
  GTSNamedAttributeDefinition(node, { visit, state }) {
343
- state.attributeNames.push(node.name);
344
335
  const namedBody = visit(node.body) as ObjectExpression;
345
- state.attributeNames.pop();
346
336
  const properties = [...namedBody.properties];
347
337
  const nameValue: Literal =
348
338
  node.name.type === "Literal"
@@ -366,13 +356,6 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
366
356
  loc: node.loc,
367
357
  });
368
358
  const body = { ...namedBody, properties };
369
- const arrow: ArrowFunctionExpression = {
370
- type: "ArrowFunctionExpression",
371
- params: [],
372
- expression: true,
373
- body,
374
- loc: node.loc,
375
- };
376
359
  if (node.bindingName) {
377
360
  if (node.bindingAccessModifier === "protected") {
378
361
  throw new GtsTranspilerError(
@@ -381,27 +364,25 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
381
364
  );
382
365
  }
383
366
  const export_ = node.bindingAccessModifier !== "private";
384
- const internalId: Identifier = {
385
- type: "Identifier",
386
- name: `__gts_internal_binding_${state.externalizedBindings.length}`,
387
- };
388
367
  state.externalizedBindings.push({
389
368
  bindingName: node.bindingName,
390
369
  export: export_,
391
- internalId,
392
- value: arrow,
393
- path: [...state.attributeNames, node.name].map((n) => {
394
- if (n.type === "Literal") {
395
- return String(n.value);
396
- } else {
397
- return n.name;
398
- }
399
- }),
400
370
  });
401
- return internalId;
402
- } else {
403
- return arrow;
371
+
372
+ body.properties.push({
373
+ type: "Property",
374
+ key: { type: "Identifier", name: "binding" },
375
+ computed: false,
376
+ kind: "init",
377
+ method: false,
378
+ shorthand: false,
379
+ value: {
380
+ type: "Literal",
381
+ value: export_ ? "public" : "private",
382
+ },
383
+ });
404
384
  }
385
+ return body;
405
386
  },
406
387
  GTSAttributeBody(node, { visit }) {
407
388
  const positionals = visit(node.positionalAttributes) as ArrayExpression;
@@ -418,7 +399,13 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
418
399
  kind: "init",
419
400
  method: false,
420
401
  shorthand: false,
421
- value: positionals,
402
+ value: {
403
+ type: "ArrowFunctionExpression",
404
+ params: [],
405
+ body: positionals,
406
+ expression: true,
407
+ loc: positionals.loc,
408
+ },
422
409
  loc: positionals.loc,
423
410
  },
424
411
  {
@@ -494,9 +481,9 @@ export const initialTranspileState = (
494
481
  option.shortcutFunctionPreludes ?? DEFAULT_SHORTCUT_FUNCTION_PRELUDES;
495
482
  const queryBindings = option.queryBindings ?? DEFAULT_QUERY_BINDINGS;
496
483
  const fnArgId: Identifier = { type: "Identifier", name: "__gts_fnArg" };
497
- const preludeSymbolId: Identifier = {
498
- type: "Identifier",
499
- name: "__gts_Prelude",
484
+ const PreludeLit: Literal = {
485
+ type: "Literal",
486
+ value: "~prelude",
500
487
  };
501
488
  const shortcutFunctionParameters: Pattern[] = [
502
489
  fnArgId,
@@ -517,7 +504,7 @@ export const initialTranspileState = (
517
504
  right: {
518
505
  type: "MemberExpression",
519
506
  object: fnArgId,
520
- property: preludeSymbolId,
507
+ property: PreludeLit,
521
508
  computed: true,
522
509
  optional: false,
523
510
  },
@@ -539,12 +526,12 @@ export const initialTranspileState = (
539
526
  ];
540
527
  return {
541
528
  createDefineFnId: { type: "Identifier", name: "__gts_createDefine" },
542
- ActionId: { type: "Identifier", name: "__gts_Action" },
543
- preludeSymbolId,
529
+ createBindingFnId: { type: "Identifier", name: "__gts_createBinding" },
530
+ ActionLit: { type: "Literal", value: "~action" },
531
+ PreludeLit,
544
532
  fnArgId,
545
533
  shortcutFunctionParameters,
546
534
  rootVmId: { type: "Identifier", name: "__gts_rootVm" },
547
- binderFnId: { type: "Identifier", name: "__gts_Binder" },
548
535
  queryFnId: { type: "Identifier", name: "__gts_query" },
549
536
  queryParameters,
550
537
 
@@ -563,9 +550,11 @@ export const initialTranspileState = (
563
550
  })),
564
551
  },
565
552
 
566
- attributeNames: [],
567
553
  externalizedBindings: [],
568
554
  hasQueryExpressions: false,
555
+ defineIdCounter: 0,
556
+
557
+ bindingStatements: [],
569
558
  };
570
559
  };
571
560
 
@@ -1,4 +1,4 @@
1
- import type { SourceLocation } from "estree";
1
+ import type { Node, SourceLocation } from "estree";
2
2
  import { walk } from "zimmerframe";
3
3
 
4
4
  function isLeafNode(node: any): boolean {
@@ -28,43 +28,69 @@ function isLeafNode(node: any): boolean {
28
28
  return true;
29
29
  }
30
30
 
31
- export interface LocationAdjustment {
31
+ export interface LeafToken {
32
+ loc: SourceLocation;
33
+ isDummy?: boolean;
34
+ /**
35
+ * Override source length (instead of loc.end - loc.start)
36
+ */
37
+ sourceLength?: number;
38
+ /**
39
+ * Adjust the start position of source code
40
+ */
41
+ sourceStartOffset?: number;
32
42
  /**
33
43
  * Adjust the start position of generated code
34
44
  */
35
- startOffset: number;
45
+ generatedStartOffset?: number;
46
+ /**
47
+ * Make the source length longer
48
+ */
49
+ sourceLengthOffset?: number;
36
50
  /**
37
51
  * The original length of generated code, used for mapping diagnostics
38
52
  */
39
- generatedLength: number;
40
- }
41
-
42
- export interface LeafToken {
43
- loc: SourceLocation;
44
- isDummy?: boolean;
45
- sourceLength?: number;
46
53
  generatedLength?: number;
47
- locationAdjustment?: LocationAdjustment;
48
54
  }
49
55
 
50
56
  export function collectLeafTokens(ast: any): LeafToken[] {
51
- const tokens: LeafToken[] = [];
52
- walk(ast, tokens, {
57
+ const state = {
58
+ tokens: [] as LeafToken[],
59
+ };
60
+ walk(ast as Node, state, {
53
61
  _(node, { state, next }) {
54
62
  if (isLeafNode(node) && node.loc) {
55
63
  const token: LeafToken = {
56
- loc: node.loc
64
+ loc: node.loc,
57
65
  };
58
- if (node.isDummy) {
66
+ if ("isDummy" in node && node.isDummy) {
59
67
  token.isDummy = true;
60
68
  token.sourceLength = 0;
61
- // include next character for diagnostic
69
+ // add 1 for squiggle on next character
62
70
  token.generatedLength = 1;
63
71
  }
64
- state.push(token);
72
+ state.tokens.push(token);
73
+ }
74
+ next();
75
+ },
76
+ NewExpression(node, { state, next }) {
77
+ const lParenLoc = node.lParenLoc;
78
+ if (lParenLoc) {
79
+ state.tokens.push({
80
+ loc: lParenLoc,
81
+ });
82
+ }
83
+ next();
84
+ },
85
+ CallExpression(node, { state, next }) {
86
+ const lParenLoc = node.lParenLoc;
87
+ if (lParenLoc) {
88
+ state.tokens.push({
89
+ loc: lParenLoc,
90
+ });
65
91
  }
66
92
  next();
67
93
  },
68
94
  });
69
- return tokens;
95
+ return state.tokens;
70
96
  }