novac 2.0.1 → 2.2.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.
Files changed (161) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1574 -597
  3. package/bin/novac +468 -171
  4. package/bin/nvc +522 -0
  5. package/bin/nvml +78 -17
  6. package/demo.nv +0 -0
  7. package/demo_builtins.nv +0 -0
  8. package/demo_http.nv +0 -0
  9. package/examples/bf.nv +69 -0
  10. package/examples/math.nv +21 -0
  11. package/kits/birdAPI/kitdef.js +954 -0
  12. package/kits/kitRNG/kitdef.js +740 -0
  13. package/kits/kitSSH/kitdef.js +1272 -0
  14. package/kits/kitadb/kitdef.js +606 -0
  15. package/kits/kitai/kitdef.js +2185 -0
  16. package/kits/kitansi/kitdef.js +1402 -0
  17. package/kits/kitcanvas/kitdef.js +914 -0
  18. package/kits/kitclippy/kitdef.js +925 -0
  19. package/kits/kitformat/kitdef.js +1485 -0
  20. package/kits/kitgps/kitdef.js +1862 -0
  21. package/kits/kitlibproc/kitdef.js +3 -2
  22. package/kits/kitmatrix/ex.js +19 -0
  23. package/kits/kitmatrix/kitdef.js +960 -0
  24. package/kits/kitmorse/kitdef.js +229 -0
  25. package/kits/kitmpatch/kitdef.js +906 -0
  26. package/kits/kitnet/kitdef.js +1401 -0
  27. package/kits/kitnovacweb/README.md +1416 -143
  28. package/kits/kitnovacweb/kitdef.js +92 -2
  29. package/kits/kitnovacweb/nvml/executor.js +578 -176
  30. package/kits/kitnovacweb/nvml/index.js +2 -2
  31. package/kits/kitnovacweb/nvml/lexer.js +72 -69
  32. package/kits/kitnovacweb/nvml/parser.js +328 -159
  33. package/kits/kitnovacweb/nvml/renderer.js +770 -270
  34. package/kits/kitparse/kitdef.js +1688 -0
  35. package/kits/kitproto/kitdef.js +613 -0
  36. package/kits/kitqr/kitdef.js +637 -0
  37. package/kits/kitregex++/kitdef.js +1353 -0
  38. package/kits/kitrequire/kitdef.js +1599 -0
  39. package/kits/kitx11/kitdef.js +1 -0
  40. package/kits/kitx11/kitx11.js +2472 -0
  41. package/kits/kitx11/kitx11_conn.js +948 -0
  42. package/kits/kitx11/kitx11_worker.js +121 -0
  43. package/kits/libtea/kitdef.js +2691 -0
  44. package/kits/libterm/ex.js +285 -0
  45. package/kits/libterm/kitdef.js +1927 -0
  46. package/novac/LICENSE +21 -0
  47. package/novac/README.md +1823 -0
  48. package/novac/bin/novac +950 -0
  49. package/novac/bin/nvc +522 -0
  50. package/novac/bin/nvml +542 -0
  51. package/novac/demo.nv +245 -0
  52. package/novac/demo_builtins.nv +209 -0
  53. package/novac/demo_http.nv +62 -0
  54. package/novac/examples/bf.nv +69 -0
  55. package/novac/examples/math.nv +21 -0
  56. package/novac/kits/kitai/kitdef.js +2185 -0
  57. package/novac/kits/kitansi/kitdef.js +1402 -0
  58. package/novac/kits/kitformat/kitdef.js +1485 -0
  59. package/novac/kits/kitgps/kitdef.js +1862 -0
  60. package/novac/kits/kitlibfs/kitdef.js +231 -0
  61. package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
  62. package/novac/kits/kitmatrix/ex.js +19 -0
  63. package/novac/kits/kitmatrix/kitdef.js +960 -0
  64. package/novac/kits/kitmpatch/kitdef.js +906 -0
  65. package/novac/kits/kitnovacweb/README.md +1572 -0
  66. package/novac/kits/kitnovacweb/demo.nv +12 -0
  67. package/novac/kits/kitnovacweb/demo.nvml +71 -0
  68. package/novac/kits/kitnovacweb/index.nova +12 -0
  69. package/novac/kits/kitnovacweb/kitdef.js +692 -0
  70. package/novac/kits/kitnovacweb/nova.kit.json +8 -0
  71. package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
  72. package/novac/kits/kitnovacweb/nvml/index.js +67 -0
  73. package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
  74. package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
  75. package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
  76. package/novac/kits/kitparse/kitdef.js +1688 -0
  77. package/novac/kits/kitregex++/kitdef.js +1353 -0
  78. package/novac/kits/kitrequire/kitdef.js +1599 -0
  79. package/novac/kits/kitx11/kitdef.js +1 -0
  80. package/novac/kits/kitx11/kitx11.js +2472 -0
  81. package/novac/kits/kitx11/kitx11_conn.js +948 -0
  82. package/novac/kits/kitx11/kitx11_worker.js +121 -0
  83. package/novac/kits/libtea/tf.js +2691 -0
  84. package/novac/kits/libterm/ex.js +285 -0
  85. package/novac/kits/libterm/kitdef.js +1927 -0
  86. package/novac/node_modules/chalk/license +9 -0
  87. package/novac/node_modules/chalk/package.json +83 -0
  88. package/novac/node_modules/chalk/readme.md +297 -0
  89. package/novac/node_modules/chalk/source/index.d.ts +325 -0
  90. package/novac/node_modules/chalk/source/index.js +225 -0
  91. package/novac/node_modules/chalk/source/utilities.js +33 -0
  92. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  93. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  94. package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  95. package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  96. package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  97. package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  98. package/novac/node_modules/commander/LICENSE +22 -0
  99. package/novac/node_modules/commander/Readme.md +1176 -0
  100. package/novac/node_modules/commander/esm.mjs +16 -0
  101. package/novac/node_modules/commander/index.js +24 -0
  102. package/novac/node_modules/commander/lib/argument.js +150 -0
  103. package/novac/node_modules/commander/lib/command.js +2777 -0
  104. package/novac/node_modules/commander/lib/error.js +39 -0
  105. package/novac/node_modules/commander/lib/help.js +747 -0
  106. package/novac/node_modules/commander/lib/option.js +380 -0
  107. package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
  108. package/novac/node_modules/commander/package-support.json +19 -0
  109. package/novac/node_modules/commander/package.json +82 -0
  110. package/novac/node_modules/commander/typings/esm.d.mts +3 -0
  111. package/novac/node_modules/commander/typings/index.d.ts +1113 -0
  112. package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
  113. package/novac/node_modules/node-addon-api/README.md +95 -0
  114. package/novac/node_modules/node-addon-api/common.gypi +21 -0
  115. package/novac/node_modules/node-addon-api/except.gypi +25 -0
  116. package/novac/node_modules/node-addon-api/index.js +14 -0
  117. package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  118. package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
  119. package/novac/node_modules/node-addon-api/napi.h +3364 -0
  120. package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  121. package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
  122. package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
  123. package/novac/node_modules/node-addon-api/package-support.json +21 -0
  124. package/novac/node_modules/node-addon-api/package.json +480 -0
  125. package/novac/node_modules/node-addon-api/tools/README.md +73 -0
  126. package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
  127. package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
  128. package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
  129. package/novac/node_modules/serialize-javascript/LICENSE +27 -0
  130. package/novac/node_modules/serialize-javascript/README.md +149 -0
  131. package/novac/node_modules/serialize-javascript/index.js +297 -0
  132. package/novac/node_modules/serialize-javascript/package.json +33 -0
  133. package/novac/package.json +27 -0
  134. package/novac/scripts/update-bin.js +24 -0
  135. package/novac/src/core/bstd.js +1035 -0
  136. package/novac/src/core/config.js +155 -0
  137. package/novac/src/core/describe.js +187 -0
  138. package/novac/src/core/emitter.js +499 -0
  139. package/novac/src/core/error.js +86 -0
  140. package/novac/src/core/executor.js +5606 -0
  141. package/novac/src/core/formatter.js +686 -0
  142. package/novac/src/core/lexer.js +1026 -0
  143. package/novac/src/core/nova_builtins.js +717 -0
  144. package/novac/src/core/nova_thread_worker.js +166 -0
  145. package/novac/src/core/parser.js +2181 -0
  146. package/novac/src/core/types.js +112 -0
  147. package/novac/src/index.js +28 -0
  148. package/novac/src/runtime/stdlib.js +244 -0
  149. package/package.json +6 -3
  150. package/scripts/update-bin.js +0 -0
  151. package/src/core/bstd.js +838 -362
  152. package/src/core/executor.js +2578 -170
  153. package/src/core/lexer.js +502 -54
  154. package/src/core/nova_builtins.js +21 -3
  155. package/src/core/parser.js +413 -72
  156. package/src/core/types.js +30 -2
  157. package/src/index.js +0 -0
  158. package/examples/example-project/README.md +0 -3
  159. package/examples/example-project/src/main.nova +0 -3
  160. package/src/core/environment.js +0 -0
  161. /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
@@ -0,0 +1,906 @@
1
+ // ============================================
2
+ // novac monkey patching + live AST toolkit
3
+ // ============================================
4
+
5
+ // --------------------------------------------
6
+ // utilities
7
+ // --------------------------------------------
8
+
9
+ function deepClone(obj) {
10
+ return structuredClone
11
+ ? structuredClone(obj)
12
+ : JSON.parse(JSON.stringify(obj));
13
+ }
14
+
15
+ function deepEqual(a, b) {
16
+ if (a === b) return true;
17
+
18
+ if (typeof a !== typeof b)
19
+ return false;
20
+
21
+ if (typeof a !== 'object' || a === null || b === null)
22
+ return false;
23
+
24
+ let aKeys = Object.keys(a);
25
+ let bKeys = Object.keys(b);
26
+
27
+ if (aKeys.length !== bKeys.length)
28
+ return false;
29
+
30
+ for (let key of aKeys) {
31
+ if (!deepEqual(a[key], b[key]))
32
+ return false;
33
+ }
34
+
35
+ return true;
36
+ }
37
+
38
+ function stripMeta(obj) {
39
+ if (typeof obj !== 'object' || obj === null)
40
+ return obj;
41
+
42
+ let out = Array.isArray(obj) ? [] : {};
43
+
44
+ for (let key in obj) {
45
+ if (
46
+ key === 'line' ||
47
+ key === 'column' ||
48
+ key === 'collumn' ||
49
+ key === 'loc'
50
+ ) continue;
51
+
52
+ out[key] = stripMeta(obj[key]);
53
+ }
54
+
55
+ return out;
56
+ }
57
+
58
+ function randomID() {
59
+ return Math.random()
60
+ .toString(36)
61
+ .slice(2);
62
+ }
63
+
64
+ // ============================================
65
+ // AST factory helpers
66
+ // ============================================
67
+
68
+ class AST {
69
+ static node(kind, props = {}) {
70
+ return {
71
+ kind,
72
+ ...props
73
+ };
74
+ }
75
+
76
+ static return(value) {
77
+ return {
78
+ kind: 'return',
79
+ value
80
+ };
81
+ }
82
+
83
+ static log(msg) {
84
+ return AST.call('log', [msg]);
85
+ }
86
+
87
+ static throw(err) {
88
+ return {
89
+ kind: 'throw',
90
+ value: err
91
+ };
92
+ }
93
+
94
+ static raw(ast) {
95
+ return ast;
96
+ }
97
+ }
98
+
99
+ // ============================================
100
+ // AST walker
101
+ // ============================================
102
+
103
+ class Walker {
104
+ static walk(node, visitor) {
105
+ function visit(n, parent = null) {
106
+ if (!n || typeof n !== 'object')
107
+ return;
108
+
109
+ visitor(n, parent);
110
+
111
+ for (let key in n) {
112
+ visit(n[key], n);
113
+ }
114
+ }
115
+
116
+ visit(node);
117
+ }
118
+
119
+ static map(node, mapper) {
120
+ function transform(n) {
121
+ if (!n || typeof n !== 'object')
122
+ return n;
123
+
124
+ n = mapper(n) || n;
125
+
126
+ for (let key in n) {
127
+ n[key] = transform(n[key]);
128
+ }
129
+
130
+ return n;
131
+ }
132
+
133
+ return transform(node);
134
+ }
135
+
136
+ static find(node, predicate) {
137
+ let matches = [];
138
+
139
+ Walker.walk(node, (n) => {
140
+ if (predicate(n))
141
+ matches.push(n);
142
+ });
143
+
144
+ return matches;
145
+ }
146
+ }
147
+
148
+ // ============================================
149
+ // AST diffing engine
150
+ // ============================================
151
+
152
+ class ASTDiff {
153
+ static compare(a, b) {
154
+ return deepEqual(
155
+ stripMeta(a),
156
+ stripMeta(b)
157
+ );
158
+ }
159
+
160
+ static changed(a, b) {
161
+ return !ASTDiff.compare(a, b);
162
+ }
163
+
164
+ static diff(a, b) {
165
+ let diffs = [];
166
+
167
+ function scan(x, y, path = '') {
168
+ if (!deepEqual(x, y)) {
169
+ diffs.push({
170
+ path,
171
+ old: x,
172
+ new: y
173
+ });
174
+ }
175
+
176
+ if (
177
+ typeof x === 'object' &&
178
+ typeof y === 'object'
179
+ ) {
180
+ for (let k in x) {
181
+ scan(
182
+ x[k],
183
+ y[k],
184
+ path + '.' + k
185
+ );
186
+ }
187
+ }
188
+ }
189
+
190
+ scan(a, b);
191
+
192
+ return diffs;
193
+ }
194
+ }
195
+
196
+ // ============================================
197
+ // patch transaction system
198
+ // ============================================
199
+
200
+ class PatchTransaction {
201
+ constructor(patcher) {
202
+ this.patcher = patcher;
203
+ this.snapshot = deepClone(
204
+ patcher.ast
205
+ );
206
+ }
207
+
208
+ rollback() {
209
+ this.patcher.ast =
210
+ deepClone(this.snapshot);
211
+
212
+ this.patcher.fn.body =
213
+ this.patcher.ast;
214
+
215
+ return this.patcher;
216
+ }
217
+
218
+ commit() {
219
+ return this.patcher.patch();
220
+ }
221
+ }
222
+
223
+ // ============================================
224
+ // hook system
225
+ // ============================================
226
+
227
+ class HookManager {
228
+ constructor() {
229
+ this.hooks = {};
230
+ }
231
+
232
+ on(event, fn) {
233
+ if (!this.hooks[event])
234
+ this.hooks[event] = [];
235
+
236
+ this.hooks[event].push(fn);
237
+ }
238
+
239
+ emit(event, data) {
240
+ if (!this.hooks[event])
241
+ return;
242
+
243
+ this.hooks[event]
244
+ .forEach(fn => fn(data));
245
+ }
246
+
247
+ off(event, fn) {
248
+ if (!this.hooks[event])
249
+ return;
250
+
251
+ this.hooks[event] =
252
+ this.hooks[event]
253
+ .filter(x => x !== fn);
254
+ }
255
+ }
256
+
257
+ // ============================================
258
+ // live function registry
259
+ // ============================================
260
+
261
+ class LiveRegistry {
262
+ constructor() {
263
+ this.functions = new Map();
264
+ }
265
+
266
+ register(name, fn) {
267
+ this.functions.set(name, fn);
268
+ }
269
+
270
+ get(name) {
271
+ return this.functions.get(name);
272
+ }
273
+
274
+ patch(name) {
275
+ return new Patcher(
276
+ this.get(name)
277
+ );
278
+ }
279
+
280
+ remove(name) {
281
+ this.functions.delete(name);
282
+ }
283
+
284
+ list() {
285
+ return [
286
+ ...this.functions.keys()
287
+ ];
288
+ }
289
+ }
290
+
291
+ // ============================================
292
+ // main patcher
293
+ // ============================================
294
+
295
+ class Patcher {
296
+ static hooks = new HookManager();
297
+
298
+ constructor(fn) {
299
+ this.id = randomID();
300
+
301
+ this.fn = fn;
302
+
303
+ this.ast = fn.body;
304
+
305
+ this.original =
306
+ deepClone(fn.body);
307
+
308
+ this.history = [];
309
+
310
+ Patcher.hooks.emit(
311
+ 'patcher:create',
312
+ this
313
+ );
314
+ }
315
+
316
+ // ----------------------------------------
317
+ // internals
318
+ // ----------------------------------------
319
+
320
+ _save(op, data) {
321
+ this.history.push({
322
+ op,
323
+ data,
324
+ time: Date.now()
325
+ });
326
+
327
+ Patcher.hooks.emit(
328
+ 'patch',
329
+ {
330
+ patcher: this,
331
+ op,
332
+ data
333
+ }
334
+ );
335
+ }
336
+
337
+ _normalize(ast) {
338
+ return ast?.ast ?? ast;
339
+ }
340
+
341
+ // ----------------------------------------
342
+ // transactions
343
+ // ----------------------------------------
344
+
345
+ transaction() {
346
+ return new PatchTransaction(
347
+ this
348
+ );
349
+ }
350
+
351
+ // ----------------------------------------
352
+ // restore
353
+ // ----------------------------------------
354
+
355
+ old() {
356
+ this.fn.body =
357
+ deepClone(this.original);
358
+
359
+ this.ast = this.fn.body;
360
+
361
+ return this;
362
+ }
363
+
364
+ rollback(steps = 1) {
365
+ while (
366
+ steps-- > 0 &&
367
+ this.history.length > 0
368
+ ) {
369
+ let h = this.history.pop();
370
+
371
+ switch (h.op) {
372
+ case 'insert':
373
+ this.ast.splice(
374
+ h.data.idx,
375
+ 1
376
+ );
377
+ break;
378
+
379
+ case 'remove':
380
+ this.ast.splice(
381
+ h.data.idx,
382
+ 0,
383
+ h.data.oldNode
384
+ );
385
+ break;
386
+
387
+ case 'replace':
388
+ this.ast[h.data.idx] =
389
+ h.data.oldNode;
390
+ break;
391
+ }
392
+ }
393
+
394
+ return this;
395
+ }
396
+
397
+ // ----------------------------------------
398
+ // mutations
399
+ // ----------------------------------------
400
+
401
+ injectBefore(idx, ast) {
402
+ this._save('insert', { idx });
403
+
404
+ this.ast.splice(
405
+ idx,
406
+ 0,
407
+ this._normalize(ast)
408
+ );
409
+
410
+ return this;
411
+ }
412
+
413
+ injectAfter(idx, ast) {
414
+ this._save('insert', {
415
+ idx: idx + 1
416
+ });
417
+
418
+ this.ast.splice(
419
+ idx + 1,
420
+ 0,
421
+ this._normalize(ast)
422
+ );
423
+
424
+ return this;
425
+ }
426
+
427
+ replace(idx, ast) {
428
+ this._save('replace', {
429
+ idx,
430
+ oldNode: deepClone(
431
+ this.ast[idx]
432
+ )
433
+ });
434
+
435
+ this.ast[idx] =
436
+ this._normalize(ast);
437
+
438
+ return this;
439
+ }
440
+
441
+ remove(idx) {
442
+ this._save('remove', {
443
+ idx,
444
+ oldNode: deepClone(
445
+ this.ast[idx]
446
+ )
447
+ });
448
+
449
+ this.ast.splice(idx, 1);
450
+
451
+ return this;
452
+ }
453
+
454
+ append(ast) {
455
+ this.ast.push(
456
+ this._normalize(ast)
457
+ );
458
+
459
+ return this;
460
+ }
461
+
462
+ // inside the Patcher class
463
+
464
+ /**
465
+ * Match AST nodes by a predicate function or pattern object.
466
+ *
467
+ * Usage:
468
+ * patcher.match(node => node.kind === "call")
469
+ * patcher.match({ kind: "return" })
470
+ *
471
+ * Returns: array of { idx, node } objects
472
+ */
473
+ match(pattern) {
474
+ const matches = [];
475
+
476
+ function isMatch(node, pattern) {
477
+ // If pattern is a function
478
+ if (typeof pattern === 'function') {
479
+ return pattern(node);
480
+ }
481
+
482
+ // If pattern is an object
483
+ if (typeof pattern === 'object' && pattern !== null) {
484
+ // Strip metadata for comparison
485
+ const cleanNode = stripMeta(node);
486
+ const cleanPattern = stripMeta(pattern);
487
+
488
+ // Check every property in the pattern
489
+ for (let key in cleanPattern) {
490
+ if (typeof cleanPattern[key] === 'object' && cleanPattern[key] !== null) {
491
+ if (!isMatch(cleanNode[key], cleanPattern[key])) return false;
492
+ } else {
493
+ if (cleanNode[key] !== cleanPattern[key]) return false;
494
+ }
495
+ }
496
+ return true;
497
+ }
498
+
499
+ return false;
500
+ }
501
+
502
+ this.ast.forEach((node, idx) => {
503
+ if (isMatch(node, pattern)) {
504
+ matches.push({ idx, node });
505
+ }
506
+ });
507
+
508
+ return matches;
509
+ }
510
+
511
+ prepend(ast) {
512
+ this.ast.unshift(
513
+ this._normalize(ast)
514
+ );
515
+
516
+ return this;
517
+ }
518
+
519
+ clear() {
520
+ this.ast.length = 0;
521
+ return this;
522
+ }
523
+
524
+ reverse() {
525
+ this.ast.reverse();
526
+ return this;
527
+ }
528
+
529
+ shuffle() {
530
+ this.ast.sort(
531
+ () => Math.random() - 0.5
532
+ );
533
+
534
+ return this;
535
+ }
536
+
537
+ duplicate(idx) {
538
+ this.ast.splice(
539
+ idx,
540
+ 0,
541
+ deepClone(this.ast[idx])
542
+ );
543
+
544
+ return this;
545
+ }
546
+
547
+ // ----------------------------------------
548
+ // querying
549
+ // ----------------------------------------
550
+
551
+ find(kind) {
552
+ let out = [];
553
+
554
+ this.ast.forEach((n, i) => {
555
+ if (n.kind === kind)
556
+ out.push(i);
557
+ });
558
+
559
+ return out;
560
+ }
561
+
562
+ where(node) {
563
+ let out = [];
564
+
565
+ this.ast.forEach((n, i) => {
566
+ if (
567
+ deepEqual(
568
+ stripMeta(n),
569
+ stripMeta(node)
570
+ )
571
+ ) {
572
+ out.push(i);
573
+ }
574
+ });
575
+
576
+ return out;
577
+ }
578
+
579
+ select(predicate) {
580
+ let out = [];
581
+
582
+ this.ast.forEach((n, i) => {
583
+ if (predicate(n, i))
584
+ out.push(i);
585
+ });
586
+
587
+ return out;
588
+ }
589
+
590
+ // ----------------------------------------
591
+ // high level patching
592
+ // ----------------------------------------
593
+
594
+ before(kind, ast) {
595
+ this.find(kind)
596
+ .reverse()
597
+ .forEach(idx => {
598
+ this.injectBefore(
599
+ idx,
600
+ ast
601
+ );
602
+ });
603
+
604
+ return this;
605
+ }
606
+
607
+ after(kind, ast) {
608
+ this.find(kind)
609
+ .reverse()
610
+ .forEach(idx => {
611
+ this.injectAfter(
612
+ idx,
613
+ ast
614
+ );
615
+ });
616
+
617
+ return this;
618
+ }
619
+
620
+ replaceKind(kind, ast) {
621
+ this.find(kind)
622
+ .reverse()
623
+ .forEach(idx => {
624
+ this.replace(
625
+ idx,
626
+ ast
627
+ );
628
+ });
629
+
630
+ return this;
631
+ }
632
+
633
+ interceptCall(
634
+ name,
635
+ transformer
636
+ ) {
637
+ this.select(n =>
638
+ n.kind === 'call' &&
639
+ n.name === name
640
+ )
641
+ .reverse()
642
+ .forEach(idx => {
643
+ this.ast[idx] =
644
+ transformer(
645
+ this.ast[idx]
646
+ );
647
+ });
648
+
649
+ return this;
650
+ }
651
+
652
+ renameCall(from, to) {
653
+ return this.interceptCall(
654
+ from,
655
+ call => {
656
+ call.name = to;
657
+ return call;
658
+ }
659
+ );
660
+ }
661
+
662
+ // ----------------------------------------
663
+ // instrumentation
664
+ // ----------------------------------------
665
+
666
+ trace() {
667
+ this.ast = this.ast.flatMap(
668
+ node => [
669
+ AST.log(
670
+ `[TRACE] ${node.kind}`
671
+ ),
672
+ node
673
+ ]
674
+ );
675
+
676
+ return this;
677
+ }
678
+
679
+ profile() {
680
+ this.prepend(
681
+ AST.call(
682
+ '__profilerStart'
683
+ )
684
+ );
685
+
686
+ this.after(
687
+ 'return',
688
+ AST.call(
689
+ '__profilerEnd'
690
+ )
691
+ );
692
+
693
+ return this;
694
+ }
695
+
696
+ freeze() {
697
+ Object.freeze(this.ast);
698
+ return this;
699
+ }
700
+
701
+ // ----------------------------------------
702
+ // compiler transforms
703
+ // ----------------------------------------
704
+
705
+ constantFold() {
706
+ this.walk(node => {
707
+ if (
708
+ node.kind === 'binary' &&
709
+ typeof node.left ===
710
+ 'number' &&
711
+ typeof node.right ===
712
+ 'number'
713
+ ) {
714
+ switch (node.op) {
715
+ case '+':
716
+ node.kind =
717
+ 'number';
718
+
719
+ node.value =
720
+ node.left +
721
+ node.right;
722
+ }
723
+ }
724
+ });
725
+
726
+ return this;
727
+ }
728
+
729
+ deadCodeElimination() {
730
+ let foundReturn = false;
731
+
732
+ this.ast = this.ast.filter(
733
+ node => {
734
+ if (foundReturn)
735
+ return false;
736
+
737
+ if (
738
+ node.kind ===
739
+ 'return'
740
+ ) {
741
+ foundReturn = true;
742
+ }
743
+
744
+ return true;
745
+ }
746
+ );
747
+
748
+ return this;
749
+ }
750
+
751
+ inlineCalls(name, body) {
752
+ this.select(n =>
753
+ n.kind === 'call' &&
754
+ n.name === name
755
+ )
756
+ .reverse()
757
+ .forEach(idx => {
758
+ this.replace(
759
+ idx,
760
+ deepClone(body)
761
+ );
762
+ });
763
+
764
+ return this;
765
+ }
766
+
767
+ // ----------------------------------------
768
+ // walking
769
+ // ----------------------------------------
770
+
771
+ walk(visitor) {
772
+ this.ast.forEach(node => {
773
+ Walker.walk(
774
+ node,
775
+ visitor
776
+ );
777
+ });
778
+
779
+ return this;
780
+ }
781
+
782
+ map(transformer) {
783
+ this.ast = this.ast.map(
784
+ transformer
785
+ );
786
+
787
+ return this;
788
+ }
789
+
790
+ filter(predicate) {
791
+ this.ast = this.ast.filter(
792
+ predicate
793
+ );
794
+
795
+ return this;
796
+ }
797
+
798
+ // ----------------------------------------
799
+ // snapshots
800
+ // ----------------------------------------
801
+
802
+ snapshot() {
803
+ return deepClone(this.ast);
804
+ }
805
+
806
+ compare(snapshot) {
807
+ return ASTDiff.diff(
808
+ snapshot,
809
+ this.ast
810
+ );
811
+ }
812
+
813
+ // ----------------------------------------
814
+ // finalize
815
+ // ----------------------------------------
816
+
817
+ patch() {
818
+ this.fn.body = this.ast;
819
+
820
+ Patcher.hooks.emit(
821
+ 'patcher:commit',
822
+ this
823
+ );
824
+
825
+ return this.fn;
826
+ }
827
+ }
828
+
829
+ // ============================================
830
+ // plugin system
831
+ // ============================================
832
+
833
+ class PluginSystem {
834
+ constructor() {
835
+ this.plugins = [];
836
+ }
837
+
838
+ use(plugin) {
839
+ this.plugins.push(plugin);
840
+
841
+ if (plugin.install)
842
+ plugin.install(this);
843
+ }
844
+
845
+ apply(patcher) {
846
+ this.plugins.forEach(p => {
847
+ if (p.transform)
848
+ p.transform(patcher);
849
+ });
850
+ }
851
+ }
852
+
853
+ // ============================================
854
+ // built-in plugins
855
+ // ============================================
856
+
857
+ const DebugPlugin = {
858
+ transform(patcher) {
859
+ patcher.trace();
860
+ }
861
+ };
862
+
863
+ const SafetyPlugin = {
864
+ transform(patcher) {
865
+ patcher.before(
866
+ 'return',
867
+ AST.log(
868
+ '[SAFE RETURN]'
869
+ )
870
+ );
871
+ }
872
+ };
873
+
874
+ // ============================================
875
+ // exports
876
+ // ============================================
877
+
878
+ const registry =
879
+ new LiveRegistry();
880
+
881
+ const plugins =
882
+ new PluginSystem();
883
+
884
+ const kitdef = {
885
+ patcher: Patcher,
886
+ walker: Walker,
887
+ ast: AST,
888
+ diff: ASTDiff,
889
+ registry,
890
+ plugins,
891
+ hooks: Patcher.hooks,
892
+ transaction: PatchTransaction,
893
+ debugPlugin: DebugPlugin,
894
+ safetyPlugin: SafetyPlugin,
895
+
896
+ utils: {
897
+ deepClone,
898
+ deepEqual,
899
+ stripMeta,
900
+ randomID
901
+ }
902
+ };
903
+
904
+ module.exports = {
905
+ kitdef
906
+ };