depyo 1.0.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/depyo.js +213 -0
  4. package/lib/BinaryReader.js +153 -0
  5. package/lib/OpCode.js +90 -0
  6. package/lib/OpCodes.js +940 -0
  7. package/lib/PycDecompiler.js +2031 -0
  8. package/lib/PycDisassembler.js +55 -0
  9. package/lib/PycReader.js +905 -0
  10. package/lib/PycResult.js +82 -0
  11. package/lib/PythonObject.js +242 -0
  12. package/lib/Unpickle.js +173 -0
  13. package/lib/ast/ast_node.js +3442 -0
  14. package/lib/bytecode/python_1_0.js +116 -0
  15. package/lib/bytecode/python_1_1.js +116 -0
  16. package/lib/bytecode/python_1_3.js +119 -0
  17. package/lib/bytecode/python_1_4.js +121 -0
  18. package/lib/bytecode/python_1_5.js +120 -0
  19. package/lib/bytecode/python_1_6.js +124 -0
  20. package/lib/bytecode/python_2_0.js +137 -0
  21. package/lib/bytecode/python_2_1.js +142 -0
  22. package/lib/bytecode/python_2_2.js +147 -0
  23. package/lib/bytecode/python_2_3.js +145 -0
  24. package/lib/bytecode/python_2_4.js +147 -0
  25. package/lib/bytecode/python_2_5.js +147 -0
  26. package/lib/bytecode/python_2_6.js +147 -0
  27. package/lib/bytecode/python_2_7.js +151 -0
  28. package/lib/bytecode/python_3_0.js +132 -0
  29. package/lib/bytecode/python_3_1.js +135 -0
  30. package/lib/bytecode/python_3_10.js +312 -0
  31. package/lib/bytecode/python_3_11.js +284 -0
  32. package/lib/bytecode/python_3_12.js +327 -0
  33. package/lib/bytecode/python_3_13.js +173 -0
  34. package/lib/bytecode/python_3_14.js +177 -0
  35. package/lib/bytecode/python_3_2.js +136 -0
  36. package/lib/bytecode/python_3_3.js +136 -0
  37. package/lib/bytecode/python_3_4.js +137 -0
  38. package/lib/bytecode/python_3_5.js +149 -0
  39. package/lib/bytecode/python_3_6.js +153 -0
  40. package/lib/bytecode/python_3_7.js +292 -0
  41. package/lib/bytecode/python_3_8.js +294 -0
  42. package/lib/bytecode/python_3_9.js +296 -0
  43. package/lib/code_reader.js +146 -0
  44. package/lib/handlers/binary_ops.js +174 -0
  45. package/lib/handlers/collections_update.js +239 -0
  46. package/lib/handlers/comparisons.js +95 -0
  47. package/lib/handlers/context_managers.js +250 -0
  48. package/lib/handlers/control_flow_jumps.js +954 -0
  49. package/lib/handlers/exceptions_blocks.js +952 -0
  50. package/lib/handlers/formatting.js +31 -0
  51. package/lib/handlers/function_calls.js +496 -0
  52. package/lib/handlers/function_class_build.js +330 -0
  53. package/lib/handlers/generators_async.js +172 -0
  54. package/lib/handlers/imports.js +53 -0
  55. package/lib/handlers/load_store_names.js +711 -0
  56. package/lib/handlers/loop_iterator.js +318 -0
  57. package/lib/handlers/misc_other.js +1201 -0
  58. package/lib/handlers/pattern_matching.js +226 -0
  59. package/lib/handlers/stack_ops.js +280 -0
  60. package/lib/handlers/subscript_slice.js +394 -0
  61. package/lib/handlers/unary_ops.js +91 -0
  62. package/lib/handlers/unpack.js +141 -0
  63. package/lib/stack_history.js +63 -0
  64. package/lib/zip_reader.js +217 -0
  65. package/package.json +35 -0
@@ -0,0 +1,3442 @@
1
+ const OpCodes = require('../OpCodes');
2
+ const PycResult = require('../PycResult');
3
+
4
+ const SPACES_PER_LEVEL = 4;
5
+
6
+ function indent(level = 1) {
7
+ return Buffer.alloc(level * SPACES_PER_LEVEL, ' ').toString('ascii');
8
+ }
9
+
10
+ class ASTNode {
11
+
12
+ m_lineNo = -1;
13
+ m_prevSibling = null;
14
+ m_nextSibling = null;
15
+ m_skip = false;
16
+
17
+ get line() {
18
+ return this.m_lineNo;
19
+ }
20
+
21
+ set line(lineNo) {
22
+ this.m_lineNo = lineNo;
23
+ }
24
+
25
+ get lastLine() {
26
+ return this.m_lineNo;
27
+ }
28
+
29
+ get prevSibling() {
30
+ return this.m_prevSibling;
31
+ }
32
+
33
+ set prevSibling(value) {
34
+ this.m_prevSibling = value;
35
+ }
36
+
37
+ get nextSibling() {
38
+ return this.m_nextSibling;
39
+ }
40
+
41
+ set nextSibling(value) {
42
+ this.m_nextSibling = value;
43
+ }
44
+
45
+ get skip() {
46
+ return this.m_skip;
47
+ }
48
+
49
+ set skip(value) {
50
+ this.m_skip = value;
51
+ }
52
+
53
+ codeFragment() {
54
+ let result = `#TODO ${this.constructor.name}`;
55
+ if (result === null || result === undefined) {
56
+ return `${result}`;
57
+ }
58
+ return result;
59
+ }
60
+
61
+ static calculateSpacing(prevNode, node) {
62
+ const rawSpacing = global?.g_cliArgs?.rawSpacing;
63
+ if (node?.key) {
64
+ prevNode = prevNode?.key;
65
+ node = node.key;
66
+ }
67
+ if (!prevNode || prevNode.lastLine == node.line) {
68
+ return 0;
69
+ } else {
70
+ let prevLine = prevNode.lastLine > -1 ? prevNode.lastLine : (node?.line || -1) - 1;
71
+ let line = (node?.lastLine || -1) > -1 ? (node?.lastLine || -1) : prevLine > -1 ? prevLine + 1 : 1;
72
+ let delta = line - prevLine;
73
+ if (!rawSpacing && delta > 3) {
74
+ delta = 3;
75
+ }
76
+ return delta;
77
+ }
78
+ }
79
+
80
+ static renderList(list, openBracket, closeBracket, callback) {
81
+ let result = new PycResult(openBracket, true);
82
+ result.increaseIndent();
83
+ let prevNode = null;
84
+ const rawSpacing = global?.g_cliArgs?.rawSpacing;
85
+
86
+ for (let node of list) {
87
+ let sourceFragment = callback ? callback(node) : (node?.codeFragment() || "##ERROR##");
88
+ let spacing = ASTNode.calculateSpacing(prevNode, node);
89
+
90
+ if (spacing == 0) {
91
+ result.lastLineAppend((prevNode ? " " : "") + sourceFragment + ",", false)
92
+ } else {
93
+ const blankCount = rawSpacing ? Math.max(0, spacing - 1) : (spacing > 2 ? 1 : 0);
94
+ for (let i = 0; i < blankCount; i++) {
95
+ result.add("");
96
+ }
97
+ result.add(sourceFragment + ",");
98
+ }
99
+
100
+ prevNode = node;
101
+ }
102
+ result.chop(",");
103
+ result.lastLineAppend(closeBracket);
104
+
105
+ return result;
106
+ }
107
+
108
+ toASTString() {
109
+ return JSON.stringify(this, (key, value) => {
110
+ if (["Reader"].includes(key)) {
111
+ return null;
112
+ }
113
+ if (value?.type == "Buffer") {
114
+ return value.toString('ascii');
115
+ }
116
+ if (value && typeof(value) == "object") {
117
+ value.ast_class_name = value.constructor.name;
118
+ return value;
119
+ }
120
+ return value;
121
+ }, 2);
122
+ }
123
+ }
124
+
125
+ class ASTNone extends ASTNode {
126
+ m_override = null;
127
+ constructor(override) {
128
+ super();
129
+ if (override) {
130
+ this.m_override = override;
131
+ }
132
+ }
133
+
134
+ codeFragment() {
135
+ return this.m_override !== null ? this.m_override : "None";
136
+ }
137
+
138
+ toString() {
139
+ return `ASTNone`;
140
+ }
141
+ }
142
+
143
+
144
+ class ASTLocals extends ASTNode {
145
+ constructor() {
146
+ super();
147
+ }
148
+
149
+ codeFragment() {
150
+ return "";
151
+ }
152
+
153
+ toString() {
154
+ return `ASTLocals`;
155
+ }
156
+ }
157
+
158
+ class ASTNodeList extends ASTNode {
159
+ m_list = [];
160
+ m_isModuleLevel = false;
161
+
162
+ constructor(nodes) {
163
+ super();
164
+ this.m_list = nodes || [];
165
+ }
166
+
167
+ get list() {
168
+ return this.m_list;
169
+ }
170
+
171
+ get line() {
172
+ return this.m_list[0].line;
173
+ }
174
+
175
+ get last() {
176
+ return this.list[this.list.length - 1];
177
+ }
178
+
179
+ get lastLine() {
180
+ return this.last.line;
181
+ }
182
+
183
+ get isModuleLevel() {
184
+ return this.m_isModuleLevel;
185
+ }
186
+
187
+ set isModuleLevel(value) {
188
+ this.m_isModuleLevel = !!value;
189
+ }
190
+
191
+ emptyBlock() {
192
+ return this.list.length == 1 && this.list[0] instanceof ASTReturn && (this.list[0].value == null || this.list[0].value instanceof ASTNone);
193
+ }
194
+
195
+ codeFragment() {
196
+ if (this.isEgCleanupElse()) {
197
+ this.skip = true;
198
+ return "";
199
+ }
200
+
201
+ let result = new PycResult("", true);
202
+
203
+ if (this.emptyBlock()) {
204
+ result.add("pass");
205
+ } else {
206
+ let prevNode = null;
207
+
208
+ for (let node of this.list) {
209
+ if (prevNode) {
210
+ prevNode.nextSibling = node;
211
+ }
212
+ node.prevSibling = prevNode;
213
+ prevNode = node;
214
+ }
215
+ prevNode = null;
216
+
217
+ for (let node of this.list) {
218
+ if (node.skip) {
219
+ continue;
220
+ }
221
+ // Drop implicit module-level "return None"
222
+ if (this.isModuleLevel && node instanceof ASTReturn) {
223
+ const val = node.value;
224
+ const isNone = val == null || val instanceof ASTNone || (val.codeFragment?.().toString?.() === "None");
225
+ if (isNone) {
226
+ prevNode = node;
227
+ continue;
228
+ }
229
+ }
230
+ let sourceFragment = node.codeFragment();
231
+ if (!sourceFragment) {
232
+ prevNode = node;
233
+ continue;
234
+ }
235
+ const rawSpacing = global?.g_cliArgs?.rawSpacing;
236
+ let spacing = ASTNode.calculateSpacing(prevNode, node);
237
+ if (!rawSpacing) {
238
+ if (!this.isModuleLevel && spacing > 1) {
239
+ spacing = 1;
240
+ }
241
+ if (prevNode instanceof ASTStore && node instanceof ASTStore &&
242
+ prevNode.dest instanceof ASTAnnotatedVar && node.dest instanceof ASTAnnotatedVar) {
243
+ spacing = Math.max(spacing, 1);
244
+ } else if (prevNode instanceof ASTStore && node instanceof ASTStore &&
245
+ prevNode.src instanceof ASTTypeAlias && node.src instanceof ASTTypeAlias) {
246
+ spacing = Math.max(spacing, 1);
247
+ } else if (this.requiresModuleSpacing(prevNode, node)) {
248
+ spacing = Math.max(spacing, 2);
249
+ }
250
+ }
251
+
252
+ if (prevNode && spacing == 0 && sourceFragment.length == 1) {
253
+ result.lastLineAppend((prevNode ? "; " : "") + sourceFragment.toString(), false);
254
+ } else {
255
+ const blankCount = rawSpacing ? Math.max(0, spacing - 1) : (spacing > 1 ? 1 : 0);
256
+ for (let i = 0; i < blankCount; i++) {
257
+ result.add("");
258
+ }
259
+ result.add(sourceFragment)
260
+ }
261
+
262
+ prevNode = node;
263
+ }
264
+ }
265
+ return result;
266
+ }
267
+
268
+ isEgCleanupElse() {
269
+ if (this.blockType != ASTBlock.BlockType.Else || !this.nodes || this.nodes.length === 0) {
270
+ return false;
271
+ }
272
+ return this.nodes.every(node => {
273
+ const fragment = node.codeFragment ? node.codeFragment() : '';
274
+ if (typeof fragment === 'string') {
275
+ if (fragment.includes('##ERROR##')) {
276
+ return true;
277
+ }
278
+ if (fragment.startsWith('del ')) {
279
+ return true;
280
+ }
281
+ if (fragment.includes(' = None')) {
282
+ return true;
283
+ }
284
+ }
285
+ return false;
286
+ });
287
+ }
288
+
289
+ requiresModuleSpacing(prevNode, node) {
290
+ if (!this.isModuleLevel || !prevNode || !node) {
291
+ return false;
292
+ }
293
+ const isDefinition = (candidate) => {
294
+ return candidate instanceof ASTStore &&
295
+ (candidate.src instanceof ASTFunction ||
296
+ candidate.src instanceof ASTClass ||
297
+ candidate.src instanceof ASTTypeAlias);
298
+ };
299
+ const isTypeAliasStore = (candidate) => candidate instanceof ASTStore && candidate.src instanceof ASTTypeAlias;
300
+ const isAnnotatedStore = (candidate) => candidate instanceof ASTStore && candidate.dest instanceof ASTAnnotatedVar;
301
+ const hasMetaTypeBase = (candidate) => {
302
+ if (!(candidate instanceof ASTStore) || !(candidate.src instanceof ASTClass)) {
303
+ return false;
304
+ }
305
+ return Array.isArray(candidate.src.bases?.values) &&
306
+ candidate.src.bases.values.some(b => {
307
+ const frag = b?.codeFragment?.();
308
+ const str = frag?.toString?.();
309
+ return typeof str === 'string' && str.startsWith('type(');
310
+ });
311
+ };
312
+ const prevIsDef = isDefinition(prevNode);
313
+ const nodeIsDef = isDefinition(node) || node instanceof ASTTypeAlias;
314
+
315
+ if (nodeIsDef) {
316
+ if (isTypeAliasStore(prevNode) && isTypeAliasStore(node)) {
317
+ return false;
318
+ }
319
+ if (hasMetaTypeBase(node)) {
320
+ return false;
321
+ }
322
+ return prevIsDef || prevNode instanceof ASTImport;
323
+ }
324
+
325
+ // Ensure a blank line between a module-level definition and the following statement,
326
+ // even when they share the same line number (3.13 adaptive cache collapses lines).
327
+ if (prevIsDef) {
328
+ return true;
329
+ }
330
+ if (isAnnotatedStore(prevNode) && isAnnotatedStore(node)) {
331
+ return true;
332
+ }
333
+
334
+ return false;
335
+ }
336
+
337
+ toString() {
338
+ return `ASTNodeList: lines=[${this.line} - ${this.lastLine}], {\n${this.codeFragment().toString()}\n}`;
339
+ }
340
+ }
341
+
342
+ class ASTChainStore extends ASTNodeList {
343
+ m_src = null;
344
+
345
+ constructor(nodes, src) {
346
+ super(nodes);
347
+ this.m_src = src;
348
+ }
349
+
350
+ get source() {
351
+ return this.m_src;
352
+ }
353
+
354
+ set line(value) {
355
+ this.m_lineNo = value;
356
+ }
357
+
358
+ get line() {
359
+ let list = [this.m_lineNo, this.source.line].concat(this.list.map(node => node.line).filter(el => el > -1));
360
+ return Math.min.apply(null, list);
361
+ }
362
+
363
+ get lastLine() {
364
+ let list = [this.m_lineNo, this.source.line].concat(this.list.map(node => node.line).filter(el => el > -1));
365
+ return Math.max.apply(null, list);
366
+ }
367
+
368
+ append(element) {
369
+ this.list.push(element);
370
+ }
371
+
372
+ codeFragment() {
373
+ if (this.shouldSkipEgCleanupBlock()) {
374
+ this.skip = true;
375
+ return "";
376
+ }
377
+
378
+ let result = new PycResult("", true);
379
+
380
+ if (this.list.length == 0) {
381
+ return "###FIXME###";
382
+ }
383
+ let chain = this.list.map(node => node.codeFragment()).join(' = ');
384
+ result.add(chain);
385
+ result.lastLineAppend(" = ", false);
386
+ result.lastLineAppend(this.source.codeFragment());
387
+
388
+ return result;
389
+ }
390
+
391
+ toString() {
392
+ return `ASTChainStore: lines=[${this.line} - ${this.lastLine}], ${this.codeFragment().toString()}`;
393
+ }
394
+
395
+ shouldSkipEgCleanupBlock() {
396
+ if (this.blockType == ASTBlock.BlockType.Else) {
397
+ const fragments = (this.nodes || []).map(n => n.codeFragment ? n.codeFragment() : "");
398
+ if (fragments.length > 0 && fragments.every(f => typeof f === 'string' && (f.includes('##ERROR##') || f.includes(' = None') || f.startsWith('del ')))) {
399
+ return true;
400
+ }
401
+ }
402
+ if (this.blockType == ASTBlock.BlockType.If && this.nodes && this.nodes.length == 1) {
403
+ const conditionStr = this.condition?.codeFragment?.() || '';
404
+ const bodyStr = this.nodes[0]?.codeFragment?.() || '';
405
+ if (conditionStr.includes('__prep_reraise_star__') && bodyStr.includes('__prep_reraise_star__')) {
406
+ return true;
407
+ }
408
+ }
409
+ return false;
410
+ }
411
+ }
412
+
413
+ class ASTObject extends ASTNode {
414
+ m_obj = null;
415
+
416
+ constructor(op) {
417
+ super();
418
+ this.m_obj = op;
419
+ }
420
+ get object() {
421
+ return this.m_obj;
422
+ }
423
+
424
+ set object(value) {
425
+ this.m_obj = value;
426
+ }
427
+
428
+ codeFragment() {
429
+ if (!this.object) {
430
+ return 'None';
431
+ }
432
+
433
+ // Handle numeric literals with underscore formatting (Python 3.6+)
434
+ if (this.object.ClassName === 'Py_Int' && typeof this.object.Value === 'number') {
435
+ let num = this.object.Value;
436
+ // Add underscores for large integers (>= 10000) for readability
437
+ if (Math.abs(num) >= 10000 && Number.isInteger(num)) {
438
+ let sign = num < 0 ? '-' : '';
439
+ let absStr = Math.abs(num).toString();
440
+ // Add underscore every 3 digits from right
441
+ let parts = [];
442
+ for (let i = absStr.length; i > 0; i -= 3) {
443
+ parts.unshift(absStr.substring(Math.max(0, i - 3), i));
444
+ }
445
+ return sign + parts.join('_');
446
+ }
447
+ }
448
+
449
+ let result = this.object.toString();
450
+ if (["Py_String", "Py_Unicode"].includes(this.object.ClassName)) {
451
+ let quote = '"';
452
+ if (result.includes('"')) {
453
+ if (!result.includes("'")) {
454
+ quote = "'";
455
+ } else {
456
+ quote = '"';
457
+ result = result.replace(/"/g, '\\"');
458
+ }
459
+ }
460
+ return quote + result + quote;
461
+ }
462
+ if (result === null || result === undefined) {
463
+ return `${result}`;
464
+ }
465
+ return result;
466
+ }
467
+
468
+ toString() {
469
+ return `ASTObject: lines=[${this.line} - ${this.lastLine}], ${this.codeFragment()}`;
470
+ }
471
+ }
472
+
473
+ class ASTUnary extends ASTNode {
474
+ static UnaryOp = {
475
+ Positive: 0,
476
+ Negative: 1,
477
+ Invert: 2,
478
+ Not: 3,
479
+ Await: 4
480
+ };
481
+ static UnaryOpString = ["+", "-", "~", "not ", "await "];
482
+
483
+ m_op = null;
484
+ m_operand = null;
485
+
486
+ constructor(operand, op) {
487
+ super();
488
+ this.m_op = op;
489
+ this.m_operand = operand;
490
+ }
491
+
492
+ get op() {
493
+ return this.m_op;
494
+ }
495
+
496
+ get operand() {
497
+ return this.m_operand;
498
+ }
499
+
500
+ codeFragment() {
501
+ let result = `${ASTUnary.UnaryOpString[this.op]}${this.operand.codeFragment()}`;
502
+
503
+ return result;
504
+ }
505
+
506
+ toString() {
507
+ return `ASTUnary: line=${this.line}, ${this.codeFragment()}`;
508
+ }
509
+ }
510
+
511
+ class ASTBinary extends ASTNode {
512
+ static BinOp = {
513
+ Attr: 0,
514
+ Power: 1,
515
+ Multiply: 2,
516
+ Divide: 3,
517
+ FloorDivide: 4,
518
+ Modulo: 5,
519
+ Add: 6,
520
+ Subtract: 7,
521
+ LeftShift: 8,
522
+ RightShift: 9,
523
+ And: 10,
524
+ Xor: 11,
525
+ Or: 12,
526
+ LogicalAnd: 13,
527
+ LogicalOr: 14,
528
+ MatrixMultiply: 15,
529
+ InplaceAdd: 16,
530
+ InplaceSubtract: 17,
531
+ InplaceMultiply: 18,
532
+ InplaceDivide: 19,
533
+ InplaceModulo: 20,
534
+ InplacePower: 21,
535
+ InplaceLeftShift: 22,
536
+ InplaceRightShift: 23,
537
+ InplaceAnd: 24,
538
+ InplaceXor: 25,
539
+ InplaceOr: 26,
540
+ InplaceFloorDivide: 27,
541
+ InplaceMatrixMultiply: 28,
542
+ InvalidOp: 29
543
+ };
544
+
545
+ m_op = null;
546
+ m_left = null;
547
+ m_right = null;
548
+
549
+ constructor(left, right, op) {
550
+ super();
551
+ this.m_left = left;
552
+ this.m_right =right;
553
+ this.m_op = op;
554
+ }
555
+
556
+ get left() {
557
+ return this.m_left;
558
+ }
559
+ get right() {
560
+ return this.m_right;
561
+ }
562
+ get op() {
563
+ return this.m_op;
564
+ }
565
+
566
+ set line(value) {
567
+ this.m_lineNo = value;
568
+ }
569
+
570
+ get line() {
571
+ return this.m_left.line;
572
+ }
573
+
574
+ get lastLine() {
575
+ return this.m_right.line;
576
+ }
577
+
578
+ get isInplace() {
579
+ return this.m_op >= ASTBinary.BinOp.InplaceAdd;
580
+ }
581
+
582
+ op_str()
583
+ {
584
+ return [
585
+ ".", "**", " * ", " / ", " // ", " % ", " + ", " - ",
586
+ " << ", " >> ", " & ", " ^ ", " | ", " and ", " or ", " @ ",
587
+ " += ", " -= ", " *= ", " /= ", " %= ", " **= ", " <<= ",
588
+ " >>= ", " &= ", " ^= ", " |= ", " //= ", " @= ", " <INVALID> "
589
+ ][this.op];
590
+ }
591
+
592
+ static from_opcode(opcode)
593
+ {
594
+ switch (opcode) {
595
+ case OpCodes.BINARY_ADD:
596
+ return ASTBinary.BinOp.Add;
597
+ case OpCodes.BINARY_AND:
598
+ return ASTBinary.BinOp.And;
599
+ case OpCodes.BINARY_DIVIDE:
600
+ return ASTBinary.BinOp.Divide;
601
+ case OpCodes.BINARY_FLOOR_DIVIDE:
602
+ return ASTBinary.BinOp.FloorDivide;
603
+ case OpCodes.BINARY_LSHIFT:
604
+ return ASTBinary.BinOp.LeftShift;
605
+ case OpCodes.BINARY_MODULO:
606
+ return ASTBinary.BinOp.Modulo;
607
+ case OpCodes.BINARY_MULTIPLY:
608
+ return ASTBinary.BinOp.Multiply;
609
+ case OpCodes.BINARY_OR:
610
+ return ASTBinary.BinOp.Or;
611
+ case OpCodes.BINARY_POWER:
612
+ return ASTBinary.BinOp.Power;
613
+ case OpCodes.BINARY_RSHIFT:
614
+ return ASTBinary.BinOp.RightShift;
615
+ case OpCodes.BINARY_SUBTRACT:
616
+ return ASTBinary.BinOp.Subtract;
617
+ case OpCodes.BINARY_TRUE_DIVIDE:
618
+ return ASTBinary.BinOp.Divide;
619
+ case OpCodes.BINARY_XOR:
620
+ return ASTBinary.BinOp.Xor;
621
+ case OpCodes.BINARY_MATRIX_MULTIPLY:
622
+ return ASTBinary.BinOp.MatrixMultiply;
623
+ case OpCodes.INPLACE_ADD:
624
+ return ASTBinary.BinOp.InplaceAdd;
625
+ case OpCodes.INPLACE_AND:
626
+ return ASTBinary.BinOp.InplaceAnd;
627
+ case OpCodes.INPLACE_DIVIDE:
628
+ return ASTBinary.BinOp.InplaceDivide;
629
+ case OpCodes.INPLACE_FLOOR_DIVIDE:
630
+ return ASTBinary.BinOp.InplaceFloorDivide;
631
+ case OpCodes.INPLACE_LSHIFT:
632
+ return ASTBinary.BinOp.InplaceLeftShift;
633
+ case OpCodes.INPLACE_MODULO:
634
+ return ASTBinary.BinOp.InplaceModulo;
635
+ case OpCodes.INPLACE_MULTIPLY:
636
+ return ASTBinary.BinOp.InplaceMultiply;
637
+ case OpCodes.INPLACE_OR:
638
+ return ASTBinary.BinOp.InplaceOr;
639
+ case OpCodes.INPLACE_POWER:
640
+ return ASTBinary.BinOp.InplacePower;
641
+ case OpCodes.INPLACE_RSHIFT:
642
+ return ASTBinary.BinOp.InplaceRightShift;
643
+ case OpCodes.INPLACE_SUBTRACT:
644
+ return ASTBinary.BinOp.InplaceSubtract;
645
+ case OpCodes.INPLACE_TRUE_DIVIDE:
646
+ return ASTBinary.BinOp.InplaceDivide;
647
+ case OpCodes.INPLACE_XOR:
648
+ return ASTBinary.BinOp.InplaceXor;
649
+ case OpCodes.INPLACE_MATRIX_MULTIPLY:
650
+ return ASTBinary.BinOp.InplaceMatrixMultiply;
651
+ default:
652
+ return ASTBinary.InvalidOp;
653
+ }
654
+ }
655
+
656
+ static from_binary_op(operand)
657
+ {
658
+ switch (operand) {
659
+ case 0:
660
+ return ASTBinary.BinOp.Add;
661
+ case 1:
662
+ return ASTBinary.BinOp.And;
663
+ case 2:
664
+ return ASTBinary.BinOp.FloorDivide;
665
+ case 3:
666
+ return ASTBinary.BinOp.LeftShift;
667
+ case 4:
668
+ return ASTBinary.BinOp.MatrixMultiply;
669
+ case 5:
670
+ return ASTBinary.BinOp.Multiply;
671
+ case 6:
672
+ return ASTBinary.BinOp.Modulo;
673
+ case 7:
674
+ return ASTBinary.BinOp.Or;
675
+ case 8:
676
+ return ASTBinary.BinOp.Power;
677
+ case 9:
678
+ return ASTBinary.BinOp.RightShift;
679
+ case 10:
680
+ return ASTBinary.BinOp.Subtract;
681
+ case 11:
682
+ return ASTBinary.BinOp.Divide;
683
+ case 12:
684
+ return ASTBinary.BinOp.Xor;
685
+ case 13:
686
+ return ASTBinary.BinOp.InplaceAdd;
687
+ case 14:
688
+ return ASTBinary.BinOp.InplaceAnd;
689
+ case 15:
690
+ return ASTBinary.BinOp.InplaceFloorDivide;
691
+ case 16:
692
+ return ASTBinary.BinOp.InplaceLeftShift;
693
+ case 17:
694
+ return ASTBinary.BinOp.MatrixMultiply;
695
+ case 18:
696
+ return ASTBinary.BinOp.InplaceMultiply;
697
+ case 19:
698
+ return ASTBinary.BinOp.InplaceModulo;
699
+ case 20:
700
+ return ASTBinary.BinOp.InplaceOr;
701
+ case 21:
702
+ return ASTBinary.BinOp.InplacePower;
703
+ case 22:
704
+ return ASTBinary.BinOp.InplaceRightShift;
705
+ case 23:
706
+ return ASTBinary.BinOp.InplaceSubtract;
707
+ case 24:
708
+ return ASTBinary.BinOp.InplaceDivide;
709
+ case 25:
710
+ return ASTBinary.BinOp.InplaceXor;
711
+ default:
712
+ return ASTBinary.BinOp.InvalidOp; // Return BIN_INVALID for out-of-range operand
713
+ }
714
+ }
715
+
716
+ codeFragment() {
717
+ const precedence = (op) => {
718
+ switch (op) {
719
+ case ASTBinary.BinOp.Power:
720
+ return 4;
721
+ case ASTBinary.BinOp.Multiply:
722
+ case ASTBinary.BinOp.Divide:
723
+ case ASTBinary.BinOp.FloorDivide:
724
+ case ASTBinary.BinOp.Modulo:
725
+ case ASTBinary.BinOp.MatrixMultiply:
726
+ return 3;
727
+ case ASTBinary.BinOp.Add:
728
+ case ASTBinary.BinOp.Subtract:
729
+ return 2;
730
+ case ASTBinary.BinOp.LeftShift:
731
+ case ASTBinary.BinOp.RightShift:
732
+ return 1;
733
+ default:
734
+ return 0;
735
+ }
736
+ };
737
+
738
+ const precThis = precedence(this.op);
739
+ const wrapChild = (child, position) => {
740
+ let fragment = child?.codeFragment?.() || "##ERROR##";
741
+ if (child instanceof ASTBinary) {
742
+ const childPrec = precedence(child.op);
743
+ if (childPrec < precThis || (this.op === ASTBinary.BinOp.Power && position === 'left')) {
744
+ fragment = `(${fragment})`;
745
+ }
746
+ }
747
+ return fragment;
748
+ };
749
+
750
+ let result = new PycResult(`${wrapChild(this.left, 'left')}${this.op_str()}`, true);
751
+ let rightSideResult = wrapChild(this.right, 'right');
752
+ result.lastLineAppend(rightSideResult);
753
+
754
+ return result;
755
+ }
756
+
757
+ toString() {
758
+ return `ASTBinary: line=${this.line}, ${this.codeFragment()}`;
759
+ }
760
+ }
761
+
762
+ class ASTCompare extends ASTBinary {
763
+ static CompareOp = {
764
+ Less: 0,
765
+ LessEqual: 1,
766
+ Equal: 2,
767
+ NotEqual: 3,
768
+ Greater: 4,
769
+ GreaterEqual: 5,
770
+ In: 6,
771
+ NotIn: 7,
772
+ Is: 8,
773
+ IsNot: 9,
774
+ Exception: 10,
775
+ Bad: 11
776
+ };
777
+
778
+ constructor(left, right, op) {
779
+ super(left, right, op);
780
+ }
781
+
782
+ get isInplace() {
783
+ return false;
784
+ }
785
+
786
+ op_str()
787
+ {
788
+ return [
789
+ " < ", " <= ", " == ", " != ", " > ", " >= ", " in ", " not in ",
790
+ " is ", " is not ", "<EXCEPTION MATCH>", "<BAD>"
791
+ ][this.op];
792
+ }
793
+
794
+ codeFragment() {
795
+ let leftStr = this.left?.codeFragment() || "##ERROR##";
796
+ let rightStr = this.right?.codeFragment() || "##ERROR##";
797
+ let result = `${leftStr}${this.op_str()}${rightStr}`;
798
+
799
+ return result;
800
+ }
801
+
802
+ toString() {
803
+ return `ASTCompare: line=${this.line}, ${this.codeFragment()}`;
804
+ }}
805
+
806
+ class ASTSlice extends ASTBinary {
807
+ static SliceOp = {
808
+ Slice0: 0,
809
+ Slice1: 1,
810
+ Slice2: 2,
811
+ Slice3: 3
812
+ };
813
+
814
+ constructor(op, left, right) {
815
+ left ||= new ASTNone('');
816
+ right ||= new ASTNone('');
817
+ super(left, right, op);
818
+ }
819
+
820
+ get isInplace() {
821
+ return false;
822
+ }
823
+
824
+ codeFragment() {
825
+ let str = '';
826
+
827
+ switch (this.op) {
828
+ case ASTSlice.SliceOp.Slice0:
829
+ str = '[:]';
830
+ break;
831
+ case ASTSlice.SliceOp.Slice1:
832
+ str = `[${this.left.codeFragment()}:]`;
833
+ break;
834
+ case ASTSlice.SliceOp.Slice2:
835
+ str = `[:${this.right.codeFragment()}]`;
836
+ break;
837
+ case ASTSlice.SliceOp.Slice3:
838
+ let startRange = this.left instanceof ASTNone ? '' : this.left.codeFragment();
839
+ let endRange = this.right instanceof ASTNone ? '' : this.right.codeFragment();
840
+ str = `[${startRange}:${endRange}]`;
841
+ break;
842
+ default:
843
+ console.error(`Slice op ${this.op} is not defined.`);
844
+ break;
845
+ }
846
+ return str;
847
+ }
848
+
849
+ toString() {
850
+ return `ASTSlice: line=${this.line}, ${this.codeFragment()}`;
851
+ }
852
+ }
853
+
854
+ class ASTStore extends ASTNode {
855
+
856
+ m_src = null;
857
+ m_dest = null;
858
+ m_decorators = [];
859
+
860
+ constructor(src, dest) {
861
+ super();
862
+ this.m_src = src;
863
+ this.m_dest = dest;
864
+ }
865
+
866
+ get src() {
867
+ return this.m_src;
868
+ }
869
+
870
+ set src(value) {
871
+ this.m_src = value;
872
+ }
873
+
874
+ get dest() {
875
+ return this.m_dest;
876
+ }
877
+
878
+ set dest(value) {
879
+ this.m_dest = value;
880
+ }
881
+
882
+ get decorators() {
883
+ return this.m_decorators;
884
+ }
885
+
886
+ set decorators(value) {
887
+ this.m_decorators = value || [];
888
+ }
889
+
890
+ addDecorator(decorator) {
891
+ if (!this.m_decorators) {
892
+ this.m_decorators = [];
893
+ }
894
+ this.m_decorators.push(decorator);
895
+ }
896
+ codeFragment() {
897
+ let result = new PycResult("", true);
898
+ let argNames = [];
899
+ const toVarName = (value, fallback) => {
900
+ if (value && typeof value.toString === 'function') {
901
+ return value.toString();
902
+ }
903
+ return fallback || "###MISSING_VAR###";
904
+ };
905
+
906
+ // Malformed store without source/dest – keep placeholder to avoid crashes.
907
+ if (!this.src || !this.dest) {
908
+ result.lastLineAppend(this.dest?.codeFragment?.() || '##ERROR_STORE_DEST##');
909
+ return result;
910
+ }
911
+ const destName = this.dest?.codeFragment?.() || '##ERROR_STORE_DEST##';
912
+
913
+ // Handle type statements (PEP 695): prefer bare `type Name = ...`
914
+ if (this.src instanceof ASTTypeAlias) {
915
+ const srcName = this.src.name;
916
+ const matchName = typeof srcName === 'string' ? srcName : srcName?.name || srcName?.codeFragment?.();
917
+ if (matchName && matchName.toString() === destName.toString()) {
918
+ result.add(this.src.codeFragment());
919
+ return result;
920
+ }
921
+ result.add(`${destName} = ${this.src.codeFragment()}`);
922
+ return result;
923
+ }
924
+
925
+ if (this.src instanceof ASTFunction) {
926
+ let codeObject = this.src.code.object;
927
+ let inLambda = codeObject.Name == "<lambda>";
928
+
929
+ if (inLambda) {
930
+ result.add(`${destName} = lambda`);
931
+ } else {
932
+ if (!this.src.decorators.empty()) {
933
+ for (let decorator of this.src.decorators) {
934
+ result.add('@' + (decorator?.codeFragment?.() || '##ERROR_DECORATOR##'));
935
+ }
936
+ }
937
+ if (this.decorators && this.decorators.length > 0) {
938
+ for (let decorator of this.decorators) {
939
+ const decoText = decorator?.codeFragment ? decorator.codeFragment() : decorator;
940
+ result.add('@' + decoText);
941
+ }
942
+ }
943
+ // Check for async def (both coroutines and async generators)
944
+ let isAsync = (codeObject.Flags & ASTFunction.CodeFlags.CO_COROUTINE) ||
945
+ (codeObject.Flags & ASTFunction.CodeFlags.CO_ASYNC_GENERATOR);
946
+ result.add(isAsync ? "async ": "");
947
+ const typeParams = (this.src.typeParams || []).map(tp => {
948
+ if (tp?.codeFragment) {
949
+ return tp.codeFragment();
950
+ }
951
+ if (tp?.toString) {
952
+ return tp.toString();
953
+ }
954
+ return tp;
955
+ });
956
+ const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
957
+ result.lastLineAppend(`def ${destName}${typeParamsStr}(`);
958
+ }
959
+
960
+ let argIndex = 0;
961
+ if (codeObject.ArgCount) {
962
+ let default_params = this.src.defargs;
963
+ let posOnlyCount = codeObject.PosOnlyArgCount || 0;
964
+
965
+ // Unpack tuple defaults if needed (Python loads defaults as single tuple constant)
966
+ if (default_params.length === 1 && default_params[0] instanceof ASTTuple) {
967
+ default_params = default_params[0].values;
968
+ }
969
+
970
+ // Process all regular args (positional-only + regular)
971
+ for (argIndex = 0; argIndex < codeObject.ArgCount; argIndex++) {
972
+ const rawName = toVarName(codeObject.VarNames.Value?.[argIndex]);
973
+ let argName = rawName;
974
+
975
+ // Add annotation from explicit map, if present
976
+ const annNode = this.src.annotations?.[rawName];
977
+ if (annNode) {
978
+ argName = `${argName}: ${annNode.codeFragment?.() || annNode.toString?.() || '##ERROR##'}`;
979
+ }
980
+
981
+ // Add default value if present
982
+ if ((argIndex + 1) > (codeObject.ArgCount - default_params.length)) {
983
+ let defaultIdx = argIndex - (codeObject.ArgCount - default_params.length);
984
+ let defaultNode = default_params[defaultIdx];
985
+ let annotation = this.extractAnnotationFromDefault(argName, defaultNode);
986
+ if (annotation) {
987
+ argName = `${argName}: ${annotation}`;
988
+ } else if (defaultNode) {
989
+ argName += "=" + (defaultNode.codeFragment?.() || '##ERROR##');
990
+ }
991
+ }
992
+ argNames.push(argName);
993
+
994
+ // Add / separator after positional-only args
995
+ if (posOnlyCount > 0 && argIndex === posOnlyCount - 1) {
996
+ argNames.push("/");
997
+ }
998
+ }
999
+
1000
+ if (codeObject.KWOnlyArgCount) {
1001
+ argNames.push("*");
1002
+ default_params = this.src.kwdefargs;
1003
+ for (let kwIdx = 0; kwIdx < codeObject.KWOnlyArgCount; kwIdx++) {
1004
+ const rawKwName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
1005
+ let argName = rawKwName;
1006
+
1007
+ // Check if this kwonly arg has a default value
1008
+ if (default_params && default_params.length > 0) {
1009
+ let defaultIdx = kwIdx - (codeObject.KWOnlyArgCount - default_params.length);
1010
+ if (defaultIdx >= 0 && default_params[defaultIdx]) {
1011
+ argName += "=" + default_params[defaultIdx].value.codeFragment();
1012
+ }
1013
+ }
1014
+ const kwAnn = this.src.annotations?.[rawKwName];
1015
+ if (kwAnn) {
1016
+ argName = `${argName}: ${kwAnn.codeFragment?.() || kwAnn.toString?.() || '##ERROR##'}`;
1017
+ }
1018
+ argNames.push(argName);
1019
+ }
1020
+ }
1021
+ }
1022
+ if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARARGS) {
1023
+ argNames.push('*' + toVarName(codeObject.VarNames.Value?.[argIndex++]));
1024
+ }
1025
+ if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARKEYWORDS) {
1026
+ argNames.push('**' + toVarName(codeObject.VarNames.Value?.[argIndex++]));
1027
+ }
1028
+
1029
+ result.lastLineAppend((argNames.length > 0 ? " " : "") + argNames.join(", "));
1030
+
1031
+ if (inLambda) {
1032
+ result.lastLineAppend(": ", false);
1033
+ } else {
1034
+ const retAnn = this.src.annotations?.return;
1035
+ if (retAnn) {
1036
+ result.lastLineAppend(`) -> ${retAnn.codeFragment?.() || retAnn.toString?.() || '##ERROR##'}:`);
1037
+ } else {
1038
+ result.lastLineAppend("):");
1039
+ }
1040
+ }
1041
+
1042
+ result.increaseIndent();
1043
+ if (codeObject.Globals.size) {
1044
+ for (let globalVar of codeObject.Globals) {
1045
+ result.add(`global ` + globalVar);
1046
+ }
1047
+ }
1048
+
1049
+ let methodBody = codeObject.SourceCode.codeFragment();
1050
+ if (inLambda) {
1051
+ result.lastLineAppend(methodBody);
1052
+ } else {
1053
+ result.add(methodBody);
1054
+ }
1055
+ result.decreaseIndent();
1056
+
1057
+ } else if (this.src instanceof ASTClass) {
1058
+ if (this.decorators && this.decorators.length > 0) {
1059
+ for (let decorator of this.decorators) {
1060
+ const decoText = decorator?.codeFragment ? decorator.codeFragment() : decorator;
1061
+ result.add('@' + decoText);
1062
+ }
1063
+ }
1064
+ let classNode = this.src;
1065
+ const typeParams = (classNode.typeParams || []).map(tp => {
1066
+ if (tp?.codeFragment) {
1067
+ return tp.codeFragment();
1068
+ }
1069
+ if (tp?.toString) {
1070
+ return tp.toString();
1071
+ }
1072
+ return tp;
1073
+ });
1074
+ const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
1075
+ result.add(`class ${this.dest.codeFragment()}${typeParamsStr}`);
1076
+ const bases = classNode?.bases?.values || [];
1077
+ if (bases.length > 0) {
1078
+ result.lastLineAppend(`(${bases.map(node => node?.codeFragment() || "#ERROR##").join(", ")})`, false);
1079
+ }
1080
+ result.lastLineAppend(":");
1081
+ let codeObject = classNode.code?.func?.code?.object || classNode.code?.code?.object || {};
1082
+ if (codeObject.SourceCode?.last instanceof ASTReturn ) {
1083
+ codeObject.SourceCode.list.pop();
1084
+ }
1085
+ result.increaseIndent();
1086
+ const classBodyNodeList = codeObject.SourceCode?.list || [];
1087
+ const isSyntheticEmptyReturn =
1088
+ classBodyNodeList.length === 1 &&
1089
+ classBodyNodeList[0]?.constructor?.name === 'ASTReturn' &&
1090
+ classBodyNodeList[0].value instanceof ASTLocals;
1091
+ if (isSyntheticEmptyReturn) {
1092
+ result.decreaseIndent();
1093
+ return result;
1094
+ }
1095
+ let classBody = codeObject.SourceCode?.codeFragment?.();
1096
+ const hasContent = classBody && classBody.toString()?.trim?.().length;
1097
+ if (global.g_cliArgs?.debug) {
1098
+ console.log(`[ASTStore class render] name=${this.dest?.name}, len=${classBodyNodeList.length}, first=${classBodyNodeList[0]?.constructor?.name}, retVal=${classBodyNodeList[0]?.value?.constructor?.name}, isSyntheticEmptyReturn=${isSyntheticEmptyReturn}, hasContent=${hasContent}`);
1099
+ }
1100
+ const hasMetaTypeBase = Array.isArray(classNode?.bases?.values) &&
1101
+ classNode.bases.values.some(b => {
1102
+ const frag = b?.codeFragment?.();
1103
+ const str = frag?.toString?.();
1104
+ return typeof str === 'string' && str.startsWith('type(');
1105
+ });
1106
+
1107
+ if (hasContent && !isSyntheticEmptyReturn) {
1108
+ result.add(classBody);
1109
+ } else if (!isSyntheticEmptyReturn && !hasMetaTypeBase) {
1110
+ result.add("pass");
1111
+ }
1112
+ result.decreaseIndent();
1113
+ } else if (this.src instanceof ASTImport) {
1114
+ result.lastLineAppend(this.src.codeFragment());
1115
+ } else if (this.dest instanceof ASTName && this.dest.name == "__doc__" && this.src.object) {
1116
+ let docRows = this.src.object.toString().split("\\n");
1117
+ result.add('"""');
1118
+ result.lastLineAppend(docRows.shift());
1119
+ docRows.map(result.add.bind(result));
1120
+ result.lastLineAppend('"""');
1121
+ } else {
1122
+ if (this.dest instanceof ASTName && this.dest.name == "__module__" &&
1123
+ this.src instanceof ASTName && this.src.name == "__name__"){
1124
+ return "";
1125
+ }
1126
+ if (this.dest instanceof ASTTuple) {
1127
+ this.dest.requireParens = false;
1128
+ }
1129
+ // Check if this is augmented assignment (+=, -=, etc.)
1130
+ if (this.src?.op >= ASTBinary.BinOp.InplaceAdd && this.src.op != ASTBinary.BinOp.InvalidOp) {
1131
+ // Compare dest and src.left by codeFragment to handle both simple names and attributes
1132
+ // IMPORTANT: codeFragment() returns PycResult object, need to convert to string
1133
+ let destCode = (this.dest?.codeFragment() || '').toString();
1134
+ let leftCode = (this.src.left?.codeFragment() || '').toString();
1135
+
1136
+ if (destCode === leftCode) {
1137
+ // Augmented assign: render as "a.value += 1" (src already contains the full expression)
1138
+ // Don't add "a.value = " prefix - src.codeFragment() already includes "a.value += 1"
1139
+ result.lastLineAppend(this.src?.codeFragment() || '##ERROR##');
1140
+ } else {
1141
+ // Regular assign where dest != src.left
1142
+ result.lastLineAppend(this.dest?.codeFragment() || '##ERROR##');
1143
+ result.lastLineAppend(" = ", false);
1144
+ result.lastLineAppend(this.src?.codeFragment() || '##ERROR##');
1145
+ }
1146
+ } else {
1147
+ // Regular assignment
1148
+ result.lastLineAppend(this.dest?.codeFragment() || '##ERROR##');
1149
+ result.lastLineAppend(" = ", false);
1150
+ result.lastLineAppend(this.src?.codeFragment() || '##ERROR##');
1151
+ }
1152
+ }
1153
+
1154
+ return result;
1155
+ }
1156
+
1157
+ extractAnnotationFromDefault(argName, defaultNode) {
1158
+ if (!(defaultNode instanceof ASTTuple) || !defaultNode.values || defaultNode.values.length !== 2) {
1159
+ return null;
1160
+ }
1161
+
1162
+ let [keyNode, typeNode] = defaultNode.values;
1163
+ let keyValue = this.extractAnnotationKey(keyNode);
1164
+ if (keyValue !== argName) {
1165
+ return null;
1166
+ }
1167
+
1168
+ return this.renderAnnotationValue(typeNode);
1169
+ }
1170
+
1171
+ extractAnnotationKey(node) {
1172
+ if (node instanceof ASTObject) {
1173
+ return node.object?.Value?.toString?.();
1174
+ }
1175
+ if (node?.codeFragment) {
1176
+ let fragment = node.codeFragment();
1177
+ let str = fragment?.toString?.();
1178
+ if (typeof str === 'string') {
1179
+ // Remove surrounding quotes if present
1180
+ if ((str.startsWith("'") && str.endsWith("'")) ||
1181
+ (str.startsWith('"') && str.endsWith('"'))) {
1182
+ return str.substring(1, str.length - 1);
1183
+ }
1184
+ return str;
1185
+ }
1186
+ }
1187
+ return null;
1188
+ }
1189
+
1190
+ renderAnnotationValue(node) {
1191
+ if (!node) {
1192
+ return null;
1193
+ }
1194
+ if (node instanceof ASTName) {
1195
+ return node.codeFragment();
1196
+ }
1197
+ if (node.codeFragment) {
1198
+ let fragment = node.codeFragment();
1199
+ return fragment?.toString?.();
1200
+ }
1201
+ return null;
1202
+ }
1203
+
1204
+ toString() {
1205
+ return `ASTStore: line=${this.line}, ${this.codeFragment().toString()}`;
1206
+ }
1207
+ }
1208
+
1209
+
1210
+ class ASTReturn extends ASTNode {
1211
+ static RetType = {
1212
+ Return: 0,
1213
+ Yield: 1,
1214
+ YieldFrom: 2
1215
+ };
1216
+
1217
+ m_value = null;
1218
+ m_rettype = ASTReturn.RetType.Return;
1219
+ m_inlambda = false;
1220
+
1221
+ constructor(value, rettype = ASTReturn.RetType.Return) {
1222
+ super();
1223
+ this.m_value = value;
1224
+ this.m_rettype = rettype;
1225
+ }
1226
+
1227
+ get value() {
1228
+ return this.m_value;
1229
+ }
1230
+
1231
+ get rettype() {
1232
+ return this.m_rettype;
1233
+ }
1234
+
1235
+ get inLambda() {
1236
+ return this.m_inlambda;
1237
+ }
1238
+
1239
+ set inLambda(value) {
1240
+ this.m_inlambda = value;
1241
+ }
1242
+
1243
+ codeFragment() {
1244
+ let result = new PycResult("", true);
1245
+
1246
+ if (!this.inLambda) {
1247
+ switch (this.rettype) {
1248
+ case ASTReturn.RetType.Return:
1249
+ if (!this.value || this.value instanceof ASTNone) {
1250
+ result.add("return");
1251
+ } else {
1252
+ result.add("return ");
1253
+ result.lastLineAppend(this.value.codeFragment());
1254
+ }
1255
+ break;
1256
+ case ASTReturn.RetType.Yield:
1257
+ result.add("yield ");
1258
+ result.lastLineAppend(this.value.codeFragment());
1259
+ break;
1260
+ case ASTReturn.RetType.YieldFrom:
1261
+ if (this.value instanceof ASTAwaitable) {
1262
+ result.add("await ");
1263
+ result.lastLineAppend(this.value.expression?.codeFragment() || "###FIXME###");
1264
+ } else {
1265
+ result.add("yield from ");
1266
+ result.lastLineAppend(this.value.codeFragment());
1267
+ }
1268
+ break;
1269
+ }
1270
+
1271
+ return result;
1272
+ }
1273
+
1274
+ return this.value?.codeFragment() || "";
1275
+ }
1276
+
1277
+ toString() {
1278
+ return `${this.constructor.name}: line=${this.line}, ${this.codeFragment()}`;
1279
+ }
1280
+ }
1281
+
1282
+ class ASTNamedExpr extends ASTNode {
1283
+ m_target = null;
1284
+ m_value = null;
1285
+ m_requireParens = true;
1286
+
1287
+ constructor(target, value) {
1288
+ super();
1289
+ this.m_target = target;
1290
+ this.m_value = value;
1291
+ }
1292
+
1293
+ get target() {
1294
+ return this.m_target;
1295
+ }
1296
+
1297
+ get value() {
1298
+ return this.m_value;
1299
+ }
1300
+
1301
+ get requireParens() {
1302
+ return this.m_requireParens;
1303
+ }
1304
+
1305
+ set requireParens(value) {
1306
+ this.m_requireParens = value;
1307
+ }
1308
+
1309
+ codeFragment() {
1310
+ let targetStr = this.target?.codeFragment() || "##ERROR##";
1311
+ let valueStr = this.value?.codeFragment() || "##ERROR##";
1312
+ const expr = `${targetStr} := ${valueStr}`;
1313
+ return this.requireParens ? `(${expr})` : expr;
1314
+ }
1315
+
1316
+ toString() {
1317
+ return `${this.constructor.name}: line=${this.line}, ${this.codeFragment()}`;
1318
+ }
1319
+ }
1320
+
1321
+ class ASTName extends ASTNode {
1322
+ m_name = null;
1323
+
1324
+ constructor(name) {
1325
+ super();
1326
+ this.m_name = name;
1327
+ }
1328
+
1329
+ set name(value) {
1330
+ this.m_name = value;
1331
+ }
1332
+
1333
+ get name() {
1334
+ return this.m_name;
1335
+ }
1336
+
1337
+
1338
+ codeFragment() {
1339
+ return this.name?.toString() || "##ERROR##";
1340
+ }
1341
+
1342
+ toString() {
1343
+ return `${this.constructor.name}: line=${this.line}, ${this.codeFragment()}`;
1344
+ }
1345
+ }
1346
+
1347
+ class ASTDelete extends ASTNode {
1348
+ m_value = null;
1349
+
1350
+ constructor(value, rettype) {
1351
+ super();
1352
+ this.m_value = value;
1353
+ }
1354
+
1355
+ get value() {
1356
+ return this.m_value;
1357
+ }
1358
+
1359
+ codeFragment() {
1360
+ let result = `del ${this.value?.codeFragment().toString().trim() || '##ERROR##'}`;
1361
+
1362
+ return result;
1363
+ }
1364
+
1365
+ toString() {
1366
+ return `${this.constructor.name}: line=${this.line}, ${this.codeFragment()}`;
1367
+ }
1368
+ }
1369
+
1370
+ class ASTFunction extends ASTNode {
1371
+
1372
+ static CodeFlags = {
1373
+ CO_OPTIMIZED: 0x1,
1374
+ CO_NEWLOCALS: 0x2,
1375
+ CO_VARARGS: 0x4,
1376
+ CO_VARKEYWORDS: 0x8,
1377
+ CO_NESTED: 0x10,
1378
+ CO_GENERATOR: 0x20,
1379
+ CO_NOFREE: 0x40,
1380
+ CO_COROUTINE: 0x80,
1381
+ CO_ITERABLE_COROUTINE: 0x100,
1382
+ CO_ASYNC_GENERATOR: 0x200,
1383
+ CO_GENERATOR_ALLOWED: 0x1000,
1384
+ CO_FUTURE_DIVISION: 0x2000,
1385
+ CO_FUTURE_ABSOLUTE_IMPORT: 0x4000,
1386
+ CO_FUTURE_WITH_STATEMENT: 0x8000,
1387
+ CO_FUTURE_PRINT_FUNCTION: 0x10000,
1388
+ CO_FUTURE_UNICODE_LITERALS: 0x20000,
1389
+ CO_FUTURE_BARRY_AS_BDFL: 0x40000,
1390
+ CO_FUTURE_GENERATOR_STOP: 0x80000,
1391
+ };
1392
+
1393
+
1394
+ m_code = null;
1395
+ m_defargs = null;
1396
+ m_kwdefargs = null;
1397
+ m_decorators = [];
1398
+ m_annotations = {};
1399
+ m_typeParams = [];
1400
+
1401
+ constructor(code, defargs = [], kwdefargs = [], annotations = []) {
1402
+ super();
1403
+ this.m_code = code;
1404
+ this.m_defargs = defargs;
1405
+ this.m_kwdefargs = kwdefargs;
1406
+ this.m_decorators = annotations;
1407
+ }
1408
+
1409
+ get code() {
1410
+ return this.m_code;
1411
+ }
1412
+
1413
+ get defargs() {
1414
+ return this.m_defargs;
1415
+ }
1416
+
1417
+ get kwdefargs() {
1418
+ return this.m_kwdefargs;
1419
+ }
1420
+
1421
+ get line() {
1422
+ return this.code.line;
1423
+ }
1424
+
1425
+ get lastLine() {
1426
+ return this.code.lastLine;
1427
+ }
1428
+
1429
+ add_decorator(name) {
1430
+ this.m_decorators.unshift(name);
1431
+ }
1432
+
1433
+ get decorators() {
1434
+ return this.m_decorators;
1435
+ }
1436
+
1437
+ get annotations() {
1438
+ return this.m_annotations;
1439
+ }
1440
+
1441
+ set annotations(map) {
1442
+ this.m_annotations = map || {};
1443
+ }
1444
+
1445
+ get typeParams() {
1446
+ return this.m_typeParams || [];
1447
+ }
1448
+
1449
+ set typeParams(params) {
1450
+ this.m_typeParams = params || [];
1451
+ }
1452
+
1453
+ codeFragment() {
1454
+ let result = "(lambda";
1455
+ let codeObject = this.code.object;
1456
+
1457
+ let argIndex = 0;
1458
+ let argNames = [];
1459
+ if (codeObject.ArgCount) {
1460
+ let default_params = this.defargs;
1461
+ for (let idx = 0; idx < codeObject.ArgCount; idx++) {
1462
+ let argName = codeObject.VarNames.Value[argIndex].toString();
1463
+
1464
+ if ((argIndex + 1) > (codeObject.ArgCount - default_params.length)) {
1465
+ argName += "=" + default_params[argIndex - (codeObject.ArgCount - default_params.length)].codeFragment();
1466
+ }
1467
+ argIndex++;
1468
+ argNames.push(argName);
1469
+ }
1470
+
1471
+ if (codeObject.KWOnlyArgCount) {
1472
+ default_params = this.kwdefargs;
1473
+ let firstFlag = true;
1474
+ for (let idx = 0; idx < codeObject.KWOnlyArgCount; idx++) {
1475
+ let argName = codeObject.VarNames.Value[argIndex].toString();
1476
+
1477
+ if (firstFlag) {
1478
+ argName = "*" + argName;
1479
+ firstFlag = false;
1480
+ }
1481
+
1482
+ if ((argIndex + 1) > (codeObject.ArgCount - default_params.length)) {
1483
+ argName += "=" + default_params[argIndex - (codeObject.ArgCount - default_params.length)].codeFragment();
1484
+ }
1485
+ argIndex++;
1486
+ argNames.push(argName);
1487
+ }
1488
+ if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARARGS) {
1489
+ argNames.push("*" + codeObject.VarNames.Value[argIndex++].toString());
1490
+ }
1491
+ if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARKEYWORDS) {
1492
+ argNames.push("**" + codeObject.VarNames.Value[argIndex++].toString());
1493
+ }
1494
+ }
1495
+ }
1496
+ result += (argNames.length > 0 ? " " : "") + argNames.join(", ") + ": ";
1497
+ codeObject.SourceCode.last.inLambda = true;
1498
+ result += codeObject.SourceCode.list.map(node => node.codeFragment()).filter(node => node.length > 0).join("; ") + ")";
1499
+ return result;
1500
+ }
1501
+
1502
+ toString() {
1503
+ return `${this.constructor.name}: lines=[${this.line} - ${this.lastLine}], ${this.codeFragment()}`;
1504
+ }
1505
+ }
1506
+
1507
+ class ASTClass extends ASTNode {
1508
+ m_code = null;
1509
+ m_bases = null;
1510
+ m_name = null;
1511
+ m_typeParams = [];
1512
+
1513
+ constructor(code, bases, name) {
1514
+ super();
1515
+ this.m_code = code;
1516
+ this.m_bases = bases;
1517
+ this.m_name = name;
1518
+ }
1519
+
1520
+ get code() {
1521
+ return this.m_code;
1522
+ }
1523
+
1524
+ get bases() {
1525
+ return this.m_bases;
1526
+ }
1527
+
1528
+ get name() {
1529
+ return this.m_name;
1530
+ }
1531
+
1532
+ get typeParams() {
1533
+ return this.m_typeParams || [];
1534
+ }
1535
+
1536
+ set typeParams(params) {
1537
+ this.m_typeParams = params || [];
1538
+ }
1539
+
1540
+ get line() {
1541
+ return this.code.line;
1542
+ }
1543
+
1544
+ get lastLine() {
1545
+ return this.code.lastLine;
1546
+ }
1547
+
1548
+ codeFragment() {
1549
+ let result = `#TODO ${this.constructor.name} ${this.code.codeFragment()}`;
1550
+
1551
+ return result;
1552
+ }
1553
+
1554
+ toString() {
1555
+ return `${this.constructor.name}: lines=[${this.line} - ${this.lastLine}], ${this.codeFragment()}`;
1556
+ }
1557
+ }
1558
+
1559
+ class ASTCall extends ASTNode {
1560
+ m_func = null;
1561
+ m_pparams = null;
1562
+ m_kwparams = null;
1563
+ m_var = null;
1564
+ m_kw = null;
1565
+
1566
+ constructor(func, pparams, kwparams) {
1567
+ super();
1568
+ this.m_func = func;
1569
+ this.m_pparams = pparams;
1570
+ this.m_kwparams = kwparams;
1571
+ }
1572
+
1573
+ get func() {
1574
+ return this.m_func;
1575
+ }
1576
+
1577
+ get pparams() {
1578
+ return this.m_pparams;
1579
+ }
1580
+
1581
+ get kwparams() {
1582
+ return this.m_kwparams;
1583
+ }
1584
+
1585
+ get var() {
1586
+ return this.m_var;
1587
+ }
1588
+
1589
+ set var(value) {
1590
+ this.m_var = value;
1591
+ }
1592
+
1593
+ get kw() {
1594
+ return this.m_kw;
1595
+ }
1596
+
1597
+ set kw(value) {
1598
+ this.m_kw = value;
1599
+ }
1600
+
1601
+ get hasVar() {
1602
+ return this.m_var != null;
1603
+ }
1604
+
1605
+ get hasKw() {
1606
+ return this.m_kw != null;
1607
+ }
1608
+
1609
+
1610
+ codeFragment() {
1611
+ let result = new PycResult("", true);
1612
+
1613
+ const formatParam = (node) => {
1614
+ if (!node) {
1615
+ return "##ERROR##";
1616
+ }
1617
+ if (node instanceof ASTNamedExpr && node.requireParens) {
1618
+ // Avoid double-wrapping walrus expressions when used as call args.
1619
+ const prev = node.requireParens;
1620
+ node.requireParens = false;
1621
+ const frag = node.codeFragment();
1622
+ node.requireParens = prev;
1623
+ return frag;
1624
+ }
1625
+ return node.codeFragment() || "##ERROR##";
1626
+ };
1627
+
1628
+ result.lastLineAppend((this.func?.codeFragment() || "##ERROR##") + '(');
1629
+ let params = [];
1630
+ if (this.pparams?.length > 0) {
1631
+ params.push(this.pparams.map(formatParam).join(', ').trim());
1632
+ }
1633
+ if (this.kwparams?.length > 0) {
1634
+ params.push(this.kwparams.map(node => `${node?.key?.codeFragment().toString().replaceAll("'",'')} = ${formatParam(node?.value)}`).join(', ').trim());
1635
+ }
1636
+ if (this.hasVar) {
1637
+ params.push('*' + this.var.codeFragment());
1638
+ }
1639
+ if (this.hasKw) {
1640
+ params.push('**' + this.kw.codeFragment());
1641
+ }
1642
+ result.lastLineAppend(params.join(', ') + ')');
1643
+ return result;
1644
+ }
1645
+
1646
+ toString() {
1647
+ return `${this.constructor.name}: line=${this.line}, ${this.codeFragment()}`;
1648
+ }
1649
+ }
1650
+
1651
+ class ASTImport extends ASTNode {
1652
+ m_name = null;
1653
+ m_alias = null;
1654
+ m_stores = [];
1655
+ m_fromlist = null;
1656
+
1657
+ constructor(name, fromlist, alias = null) {
1658
+ super();
1659
+ this.m_name = name;
1660
+ this.m_fromlist = fromlist;
1661
+ this.m_alias = alias;
1662
+ }
1663
+
1664
+ get name() {
1665
+ return this.m_name;
1666
+ }
1667
+
1668
+ get alias() {
1669
+ return this.m_alias;
1670
+ }
1671
+
1672
+ set alias(alias) {
1673
+ this.m_alias = alias;
1674
+ }
1675
+
1676
+ get stores() {
1677
+ return this.m_stores;
1678
+ }
1679
+
1680
+ get fromlist() {
1681
+ return this.m_fromlist;
1682
+ }
1683
+
1684
+ add_store(store) {
1685
+ this.stores.push(store);
1686
+ }
1687
+
1688
+ codeFragment() {
1689
+ let result;
1690
+
1691
+ if (!this.stores.empty()) {
1692
+ result = `from ${this.name instanceof ASTImport ? this.name.name.codeFragment() : this.name.codeFragment()} import `;
1693
+ result += this.stores.map(el => `${el.src.codeFragment()}${el.dest && el.src.codeFragment() != el.dest.codeFragment() ? ' as ' + el.dest.codeFragment() : ''}`).join(', ');
1694
+ } else {
1695
+ let nextNode = this;
1696
+ let importNodes = [];
1697
+ while (nextNode) {
1698
+ if (nextNode instanceof ASTImport && nextNode.stores.empty() && this.line == nextNode.line ) {
1699
+ let res = nextNode.name.codeFragment();
1700
+ if (nextNode.alias && nextNode.name.codeFragment() != nextNode.alias?.codeFragment()) {
1701
+ res += ` as ${nextNode.alias?.codeFragment()}`;
1702
+ }
1703
+ importNodes.push(res);
1704
+ nextNode.skip = true;
1705
+ } else {
1706
+ break;
1707
+ }
1708
+ nextNode = nextNode.nextSibling;
1709
+ }
1710
+
1711
+ result = "import " + importNodes.join(", ");
1712
+ }
1713
+ return result;
1714
+ }
1715
+
1716
+ toString() {
1717
+ return `ASTImport: line=${this.line}, ${this.codeFragment()}`;
1718
+ }
1719
+ }
1720
+
1721
+ class ASTTuple extends ASTNode {
1722
+ m_values = [];
1723
+ m_requireParens = true;
1724
+
1725
+ constructor(values) {
1726
+ super();
1727
+ this.m_values = values;
1728
+ }
1729
+
1730
+ get values() {
1731
+ return this.m_values;
1732
+ }
1733
+
1734
+ get requireParens() {
1735
+ return this.m_requireParens;
1736
+ }
1737
+
1738
+ set requireParens(value) {
1739
+ this.m_requireParens = value;
1740
+ }
1741
+
1742
+ add(name) {
1743
+ this.values.push(name);
1744
+ }
1745
+
1746
+ codeFragment() {
1747
+ let openParen = '', closeParen = '';
1748
+
1749
+ if (this.requireParens) {
1750
+ openParen = '(';
1751
+ closeParen = ')';
1752
+ }
1753
+
1754
+ // TODO: add new lines if tuple values are on different liens
1755
+ let result = ASTNode.renderList(this.values, openParen, closeParen);
1756
+
1757
+ return result;
1758
+ }
1759
+
1760
+ toString() {
1761
+ return `ASTTuple: line=${this.line}, ${this.codeFragment()}`;
1762
+ }
1763
+ }
1764
+
1765
+ class ASTList extends ASTNode {
1766
+ m_values = [];
1767
+
1768
+ constructor(values) {
1769
+ super();
1770
+ this.m_values = values;
1771
+ }
1772
+
1773
+ get values() {
1774
+ return this.m_values;
1775
+ }
1776
+
1777
+ get line() {
1778
+ return this.values[0]?.line;
1779
+ }
1780
+
1781
+ get lastLine() {
1782
+ return this.values[this.values.length - 1]?.lastLine;
1783
+ }
1784
+
1785
+ codeFragment() {
1786
+ let result = ASTNode.renderList(this.values, '[', ']');
1787
+ return result;
1788
+ }
1789
+
1790
+ toString() {
1791
+ return `ASTList: line=${this.line}, ${this.codeFragment()}`;
1792
+ }
1793
+ }
1794
+
1795
+ class ASTSet extends ASTNode {
1796
+ m_values = [];
1797
+
1798
+ constructor(values) {
1799
+ super();
1800
+ this.m_values = values;
1801
+ }
1802
+
1803
+ get values() {
1804
+ return this.m_values;
1805
+ }
1806
+
1807
+ get line() {
1808
+ return this.values[0]?.lastLine;
1809
+ }
1810
+
1811
+ get lastLine() {
1812
+ return this.values[this.values.length - 1]?.lastLine;
1813
+ }
1814
+
1815
+ add(value) {
1816
+ // TODO: Should we validate uniqueness of the value?
1817
+ this.values.push(value);
1818
+ }
1819
+
1820
+ codeFragment() {
1821
+ let result = ASTNode.renderList(this.values, '{', '}');
1822
+ return result;
1823
+ }
1824
+
1825
+ toString() {
1826
+ return `ASTSet: line=${this.line}, ${this.codeFragment()}`;
1827
+ }
1828
+ }
1829
+
1830
+ class ASTMap extends ASTNode {
1831
+ m_values = [];
1832
+
1833
+ constructor(values) {
1834
+ super();
1835
+ this.m_values = values || [];
1836
+ }
1837
+
1838
+ get values() {
1839
+ return this.m_values;
1840
+ }
1841
+
1842
+ get lastLine() {
1843
+ let keys = Object.keys(this.values);
1844
+ return keys[keys.length - 1]?.lastLine;
1845
+ }
1846
+
1847
+ add (key, value) {
1848
+ this.values.push({key, value});
1849
+ }
1850
+
1851
+ codeFragment() {
1852
+ let result = ASTNode.renderList(this.values, '{', '}', el => el.key.codeFragment() + ': ' + el.value.codeFragment());
1853
+ return result;
1854
+ }
1855
+
1856
+ toString() {
1857
+ return `ASTMap: line=${this.line}, ${this.codeFragment()}`;
1858
+ }
1859
+ }
1860
+
1861
+ class ASTKwNamesMap extends ASTNode {
1862
+ m_values = [];
1863
+
1864
+ constructor(values) {
1865
+ super();
1866
+ this.m_values = values;
1867
+ }
1868
+
1869
+ get values() {
1870
+ return this.m_values;
1871
+ }
1872
+
1873
+ get lastLine() {
1874
+ let keys = Object.keys(this.values);
1875
+ return keys[keys.length - 1]?.lastLine;
1876
+ }
1877
+
1878
+ add (key, value) {
1879
+ this.values.push({key, value});
1880
+ }
1881
+
1882
+ codeFragment() {
1883
+ let result = '{' + this.values.map(el => el.key.codeFragment() + ': ' + el.value.codeFragment()).join(', ') + '}';
1884
+
1885
+ return result;
1886
+ }
1887
+
1888
+ toString() {
1889
+ return `ASTKwNamesMap: line=${this.line}, ${this.codeFragment()}`;
1890
+ }
1891
+ }
1892
+
1893
+ class ASTConstMap extends ASTNode {
1894
+ m_keys = {};
1895
+ m_values = {};
1896
+
1897
+ constructor(keys, values) {
1898
+ super();
1899
+ this.m_keys = keys;
1900
+ this.m_values = values;
1901
+ }
1902
+
1903
+ get values() {
1904
+ return this.m_values;
1905
+ }
1906
+
1907
+ get keys() {
1908
+ return this.m_keys;
1909
+ }
1910
+
1911
+ get lastLine() {
1912
+ return this.keys[this.keys.length - 1]?.lastLine;
1913
+ }
1914
+
1915
+ codeFragment() {
1916
+ // Extract keys from ASTObject tuple
1917
+ let keysArray = [];
1918
+ if (this.keys instanceof ASTObject &&
1919
+ (this.keys.object?.ClassName === 'Py_Tuple' || this.keys.object?.ClassName === 'Py_SmallTuple')) {
1920
+ keysArray = this.keys.object.Value.map(v => new ASTObject(v));
1921
+ } else if (Array.isArray(this.keys)) {
1922
+ keysArray = this.keys;
1923
+ }
1924
+
1925
+ // values is already an array (from dataStack.pop loop)
1926
+ // Reverse because values are popped from stack in reverse order
1927
+ let valuesArray = Array.isArray(this.values) ? [...this.values].reverse() : [];
1928
+
1929
+ if (keysArray.length === 0) {
1930
+ return '{}';
1931
+ }
1932
+
1933
+ let pairs = [];
1934
+ for (let i = 0; i < keysArray.length; i++) {
1935
+ let keyStr = keysArray[i]?.codeFragment() || '##ERROR##';
1936
+ let valueStr = valuesArray[i]?.codeFragment() || '##ERROR##';
1937
+ pairs.push(`${keyStr}: ${valueStr}`);
1938
+ }
1939
+
1940
+ return '{' + pairs.join(', ') + '}';
1941
+ }
1942
+
1943
+ toString() {
1944
+ return `ASTConstMap: line=${this.line}, ${this.codeFragment()}`;
1945
+ }
1946
+ }
1947
+
1948
+ class ASTSubscr extends ASTNode {
1949
+ m_name = null;
1950
+ m_key = null;
1951
+
1952
+ constructor(name, key) {
1953
+ super();
1954
+ this.m_name = name;
1955
+ this.m_key = key;
1956
+ }
1957
+
1958
+ get name() {
1959
+ return this.m_name;
1960
+ }
1961
+
1962
+ get key() {
1963
+ return this.m_key;
1964
+ }
1965
+
1966
+ get lastLine() {
1967
+ return this.key?.lastLine || -1;
1968
+ }
1969
+
1970
+ codeFragment() {
1971
+ let result = new PycResult(this.name?.codeFragment() || '##ERROR##', true);
1972
+ let skipBrackets = this.key instanceof ASTSlice;
1973
+
1974
+ if (!skipBrackets) {
1975
+ result.lastLineAppend('[');
1976
+ }
1977
+ result.lastLineAppend(this.key?.codeFragment() || "##ERROR##");
1978
+ if (!skipBrackets) {
1979
+ result.lastLineAppend(']');
1980
+ }
1981
+
1982
+ return result;
1983
+ }
1984
+
1985
+ toString() {
1986
+ return `ASTSubscr: line=${this.line}, ${this.codeFragment()}`;
1987
+ }
1988
+ }
1989
+
1990
+ class ASTPrint extends ASTNode {
1991
+ m_values = [];
1992
+ m_stream = null;
1993
+ m_eol = false;
1994
+
1995
+ constructor(value, stream) {
1996
+ super();
1997
+
1998
+ this.m_stream = stream;
1999
+
2000
+ if (value) {
2001
+ this.m_values.push(value);
2002
+ this.m_eol = false;
2003
+ } else {
2004
+ this.m_eol = true;
2005
+ }
2006
+ }
2007
+
2008
+ get values() {
2009
+ return this.m_values;
2010
+ }
2011
+
2012
+ get stream() {
2013
+ return this.m_stream;
2014
+ }
2015
+
2016
+ get eol() {
2017
+ return this.m_eol;
2018
+ }
2019
+
2020
+ set eol(eol) {
2021
+ this.m_eol = eol;
2022
+ }
2023
+
2024
+ get lastLine() {
2025
+ return this.values[this.values.length - 1]?.lastLine;
2026
+ }
2027
+
2028
+ add (value) {
2029
+ this.values.push(value);
2030
+ }
2031
+
2032
+ codeFragment() {
2033
+ let result = `print`;
2034
+
2035
+ if (this.stream) {
2036
+ result += ` >> ${this.stream.codeFragment()}`;
2037
+ }
2038
+
2039
+ result += ' ' + this.values.map(node => node.codeFragment()).join(', ') + (this.eol ? '' : ',');
2040
+
2041
+ return result;
2042
+ }
2043
+
2044
+ toString() {
2045
+ return `ASTPrint: line=${this.line}, ${this.codeFragment()}`;
2046
+ }
2047
+ }
2048
+
2049
+ class ASTConvert extends ASTNode {
2050
+ m_name = null;
2051
+
2052
+ constructor(name) {
2053
+ super();
2054
+ this.m_name = name;
2055
+ }
2056
+
2057
+ get name() {
2058
+ return this.m_name;
2059
+ }
2060
+
2061
+ codeFragment() {
2062
+ let result = "`" + this.name.codeFragment() +"`";
2063
+
2064
+ return result;
2065
+ }
2066
+
2067
+ toString() {
2068
+ return `ASTPrint: line=${this.line}, ${this.codeFragment()}`;
2069
+ }
2070
+ }
2071
+
2072
+ class ASTKeyword extends ASTNode {
2073
+ static Word = {
2074
+ Pass: 0,
2075
+ Break: 1,
2076
+ Continue: 2
2077
+ }
2078
+
2079
+ m_key = null;
2080
+
2081
+ constructor(key) {
2082
+ super();
2083
+ this.m_key = key;
2084
+ }
2085
+
2086
+ get key() {
2087
+ return this.m_key;
2088
+ }
2089
+
2090
+ get word() {
2091
+ return ["pass", "break", "continue"][this.key];
2092
+ }
2093
+
2094
+ codeFragment() {
2095
+ return new PycResult(this.word, true);
2096
+ }
2097
+
2098
+ toString() {
2099
+ return `ASTKeyword: line=${this.line}, ${this.codeFragment()}`;
2100
+ }
2101
+ }
2102
+
2103
+ class ASTRaise extends ASTNode {
2104
+ m_params = [];
2105
+
2106
+ constructor(params) {
2107
+ super();
2108
+ this.m_params = params;
2109
+ }
2110
+
2111
+ get params() {
2112
+ return this.m_params;
2113
+ }
2114
+
2115
+ get lastLine() {
2116
+ return this.params[this.params.length - 1]?.lastLine;
2117
+ }
2118
+
2119
+ codeFragment() {
2120
+ if (!this.params || this.params.length === 0) {
2121
+ return 'raise';
2122
+ }
2123
+ return 'raise ' + this.params.map(node => node.codeFragment()).join(', ');
2124
+ }
2125
+
2126
+ toString() {
2127
+ return `ASTRaise: line=${this.line}, ${this.codeFragment()}`;
2128
+ }
2129
+ }
2130
+
2131
+ class ASTExec extends ASTNode {
2132
+ m_stmt = null;
2133
+ m_glob = null;
2134
+ m_loc = null;
2135
+
2136
+ constructor(stmt, glob, loc) {
2137
+ super();
2138
+ this.m_stmt = stmt;
2139
+ this.m_glob = glob;
2140
+ this.m_loc = loc;
2141
+ }
2142
+
2143
+ get statement() {
2144
+ return this.m_stmt;
2145
+ }
2146
+
2147
+ get globals() {
2148
+ return this.m_glob;
2149
+ }
2150
+
2151
+ get locals() {
2152
+ return this.m_loc;
2153
+ }
2154
+
2155
+ codeFragment() {
2156
+ let result = `exec ${this.statement.codeFragment()}`;
2157
+
2158
+ if (this.globals) {
2159
+ result += ` in ${this.globals.codeFragment()}`;
2160
+ if (this.locals && this.globals.codeFragment != this.locals.codeFragment) {
2161
+ result += `, ${this.locals.codeFragment()}`;
2162
+ }
2163
+ }
2164
+
2165
+
2166
+ return result;
2167
+ }
2168
+
2169
+ toString() {
2170
+ return `ASTRaise: line=${this.line}, ${this.codeFragment()}`;
2171
+ }
2172
+ }
2173
+
2174
+ class ASTIteratorValue extends ASTNode {
2175
+ m_value = null;
2176
+
2177
+ constructor(value) {
2178
+ super();
2179
+ this.m_value = value;
2180
+ }
2181
+
2182
+ get value() {
2183
+ return this.m_value;
2184
+ }
2185
+ }
2186
+
2187
+ class ASTBlock extends ASTNode {
2188
+ static BlockType = {
2189
+ Main: 0,
2190
+ If: 1,
2191
+ Else: 2,
2192
+ Elif: 3,
2193
+ Try: 4,
2194
+ Container: 5,
2195
+ Except: 6,
2196
+ Finally: 7,
2197
+ While: 8,
2198
+ For: 9,
2199
+ With: 10,
2200
+ AsyncFor: 11
2201
+ }
2202
+
2203
+ m_blockType = ASTBlock.BlockType.Main;
2204
+ m_nodes = [];
2205
+ m_start = 0;
2206
+ m_end = 0;
2207
+ m_inited = 0;
2208
+ m_isExceptStar = false;
2209
+
2210
+ constructor(blockType, start = 0, end = 0, inited = 0) {
2211
+ super();
2212
+ this.m_blockType = blockType;
2213
+ this.m_start = start;
2214
+ this.m_end = end;
2215
+ this.m_inited = inited;
2216
+ }
2217
+
2218
+ get blockType() {
2219
+ return this.m_blockType;
2220
+ }
2221
+
2222
+ get nodes() {
2223
+ return this.m_nodes;
2224
+ }
2225
+
2226
+ get isExceptStar() {
2227
+ return this.m_isExceptStar;
2228
+ }
2229
+
2230
+ set isExceptStar(value) {
2231
+ this.m_isExceptStar = value;
2232
+ }
2233
+
2234
+ get size() {
2235
+ return this.m_nodes.length;
2236
+ }
2237
+
2238
+ get start() {
2239
+ return this.m_start;
2240
+ }
2241
+
2242
+ set start(value) {
2243
+ this.m_start = value;
2244
+ }
2245
+
2246
+ get end() {
2247
+ return this.m_end;
2248
+ }
2249
+
2250
+ set end(value) {
2251
+ this.m_end = value;
2252
+ }
2253
+
2254
+ get nodes() {
2255
+ return this.m_nodes;
2256
+ }
2257
+
2258
+ get inited() {
2259
+ return this.m_inited;
2260
+ }
2261
+
2262
+ get line() {
2263
+ return this.m_lineNo > -1 ? this.m_lineNo : (this.nodes[0]?.lastLine || -1);
2264
+ }
2265
+ set line(lineNo) {
2266
+ this.m_lineNo = lineNo;
2267
+ }
2268
+
2269
+
2270
+ get lastLine() {
2271
+ return this.nodes[this.nodes.length - 1]?.lastLine || -1;
2272
+ }
2273
+
2274
+ get type_str() {
2275
+ return [
2276
+ "", "if", "else", "elif", "try", "CONTAINER", "except",
2277
+ "finally", "while", "for", "with", "async for"
2278
+ ][this.blockType];
2279
+ }
2280
+
2281
+ init(value = 1) {
2282
+ if (global.g_cliArgs?.debug && this.blockType == ASTBlock.BlockType.AsyncFor) {
2283
+ console.log(`[ASTBlock.init] AsyncFor.init() called, setting inited=${value}`);
2284
+ console.trace();
2285
+ }
2286
+ this.m_inited = value;
2287
+ }
2288
+
2289
+ removeFirst() {
2290
+ this.nodes.shift();
2291
+ }
2292
+
2293
+ removeLast() {
2294
+ this.nodes.length--;
2295
+ }
2296
+
2297
+ append(node) {
2298
+ this.nodes.push(node);
2299
+ }
2300
+
2301
+ empty() {
2302
+ return this.nodes.length == 0;
2303
+ }
2304
+
2305
+ /**
2306
+ * Check if this block contains any nested blocks (if/elif/else/while/for/etc.)
2307
+ * Used for elif detection to avoid converting nested if to elif
2308
+ */
2309
+ hasNestedBlocks() {
2310
+ return this.nodes.some(node => node instanceof ASTBlock);
2311
+ }
2312
+
2313
+ codeFragment() {
2314
+ let result = new PycResult("", true);
2315
+
2316
+ if (this.blockType == ASTBlock.BlockType.Else && this.nodes.empty()) {
2317
+ return "";
2318
+ }
2319
+
2320
+ let header = this.type_str;
2321
+ if (this.blockType == ASTBlock.BlockType.Except) {
2322
+ if (this.isExceptStar) {
2323
+ header = "except*";
2324
+ }
2325
+ // Drop synthetic condition marker "__exception__<EXCEPTION MATCH>"
2326
+ const condStr = this.condition?.codeFragment?.();
2327
+ const isSynthetic = typeof condStr === 'string' && condStr.includes("__exception__<EXCEPTION MATCH>");
2328
+ if (condStr && !isSynthetic) {
2329
+ header += " ";
2330
+ if (this.condition instanceof ASTStore) {
2331
+ if (this.condition.src) {
2332
+ header += this.condition.src.codeFragment();
2333
+ }
2334
+ if (this.condition.dest) {
2335
+ header += " as " + this.condition.dest.codeFragment();
2336
+ }
2337
+ } else {
2338
+ header += this.condition.codeFragment();
2339
+ }
2340
+ }
2341
+ result.add(header + ":");
2342
+ result.increaseIndent();
2343
+ const firstNode = this.nodes.find(n => !(n instanceof ASTBlock) && !(n instanceof ASTCondBlock));
2344
+ if (firstNode) {
2345
+ result.add(firstNode.codeFragment());
2346
+ if (this.nodes.length > 1) {
2347
+ const nextNode = this.nodes.find(n => n instanceof ASTRaise);
2348
+ if (nextNode) {
2349
+ result.add(nextNode.codeFragment());
2350
+ }
2351
+ }
2352
+ } else {
2353
+ result.add("pass");
2354
+ }
2355
+ result.decreaseIndent();
2356
+ return result;
2357
+ }
2358
+ result.add(header + ":");
2359
+
2360
+ result.increaseIndent();
2361
+ if (!this.nodes.empty()) {
2362
+ // Filter out undefined/null nodes first
2363
+ const validNodes = this.nodes.filter(n => n != null);
2364
+ // TODO: Refactor this code to use ASTNodeList that already natively handles that
2365
+ if (validNodes.length > 1 && !validNodes[0].nextSibling) {
2366
+ let prevNode = null;
2367
+
2368
+ for (let node of validNodes) {
2369
+ if (prevNode) {
2370
+ prevNode.nextSibling = node;
2371
+ }
2372
+ node.prevSibling = prevNode;
2373
+ prevNode = node;
2374
+ }
2375
+ }
2376
+
2377
+ // Filter synthetic cleanup/guards in except blocks
2378
+ let renderNodes = validNodes;
2379
+ if (this.blockType == ASTBlock.BlockType.Except) {
2380
+ renderNodes = validNodes.filter(node => {
2381
+ if (!node || node.skip) return false;
2382
+ const condStr = node.condition?.codeFragment?.();
2383
+ if (node instanceof ASTBlock || node instanceof ASTCondBlock) {
2384
+ if (typeof condStr === 'string' && condStr.includes("__exception__<EXCEPTION MATCH>")) {
2385
+ return false;
2386
+ }
2387
+ }
2388
+ if (node instanceof ASTStore &&
2389
+ node.dest instanceof ASTName &&
2390
+ node.src instanceof ASTNone) {
2391
+ return false;
2392
+ }
2393
+ return true;
2394
+ });
2395
+ }
2396
+
2397
+ if (this.blockType == ASTBlock.BlockType.Except) {
2398
+ // Render only first non-block node to avoid runaway nesting
2399
+ const firstNode = renderNodes.find(n => !(n instanceof ASTBlock) && !(n instanceof ASTCondBlock));
2400
+ if (firstNode) {
2401
+ result.add(firstNode.codeFragment());
2402
+ } else {
2403
+ result.add("pass");
2404
+ }
2405
+ } else {
2406
+ renderNodes.map(node => {
2407
+ if (!node || node.skip) {
2408
+ return;
2409
+ }
2410
+ const isHandler = node.blockType == ASTBlock.BlockType.Except || node.blockType == ASTBlock.BlockType.Finally;
2411
+ if (isHandler) {
2412
+ result.decreaseIndent();
2413
+ result.add(node.codeFragment());
2414
+ result.increaseIndent();
2415
+ } else {
2416
+ result.add(node.codeFragment());
2417
+ }
2418
+ });
2419
+ }
2420
+ } else {
2421
+ result.add("pass");
2422
+ }
2423
+ result.decreaseIndent();
2424
+
2425
+ return result;
2426
+ }
2427
+
2428
+ toString() {
2429
+ return `${this.type_str} block: {${this.start} - ${this.end}}`;
2430
+ }
2431
+ }
2432
+
2433
+ class ASTCondBlock extends ASTBlock {
2434
+ static InitCondition = {
2435
+ Uninited: 0,
2436
+ Popped: 1,
2437
+ PrePopped: 2
2438
+ }
2439
+
2440
+ m_cond = null;
2441
+ m_negative = false;
2442
+
2443
+ constructor(blockType, start = 0, end = 0, cond, negative) {
2444
+ super(blockType, start, end);
2445
+ this.m_cond = cond;
2446
+ this.m_negative = negative;
2447
+ }
2448
+
2449
+ get condition() {
2450
+ return this.m_cond;
2451
+ }
2452
+
2453
+ set condition(cond) {
2454
+ this.m_cond = cond;
2455
+ }
2456
+
2457
+ get negative() {
2458
+ return this.m_negative;
2459
+ }
2460
+
2461
+ set negative(neg) {
2462
+ this.m_negative = neg;
2463
+ }
2464
+
2465
+ codeFragment() {
2466
+ let result = new PycResult("", true);
2467
+
2468
+ // This is the assert case
2469
+ if (this.blockType == ASTBlock.BlockType.If && this.negative && this.condition && this.nodes.length == 1 && this.nodes[0] instanceof ASTRaise) {
2470
+ result.lastLineAppend("assert ", false);
2471
+ result.lastLineAppend(this.condition.codeFragment());
2472
+ if (this.nodes.length == 1 && this.nodes[0] instanceof ASTRaise && this.nodes[0].params.length == 1 && this.nodes[0].params[0] instanceof ASTCall) {
2473
+ result.lastLineAppend(', ' + this.nodes[0].params[0].pparams.map(node => node.codeFragment()).join(", "));
2474
+ }
2475
+ return result;
2476
+ }
2477
+
2478
+ if (
2479
+ this.prevSibling instanceof ASTStore &&
2480
+ this.blockType == ASTBlock.BlockType.If &&
2481
+ this.nodes.length == 1 &&
2482
+ this.nextSibling instanceof ASTCondBlock &&
2483
+ this.nextSibling?.type == ASTBlock.BlockType.Else &&
2484
+ this.nextSibling?.nodes.length == 1 &&
2485
+ this.condition.line >= 0 &&
2486
+ this.condition.line == this.nodes[0].line &&
2487
+ this.condition.line == this.nextSibling?.nodes[0].line
2488
+ ) {
2489
+ result.add(this.nodes[0].codeFragment() || "###ERROR###");
2490
+ result.lastLineAppend(" if", false);
2491
+ result.lastLineAppend(this.negative ? " not" : "", false);
2492
+ result.lastLineAppend(" ", false);
2493
+ result.lastLineAppend(this.condition?.codeFragment() || "True");
2494
+ result.lastLineAppend(" else ", false);
2495
+ result.lastLineAppend(this.nextSibling.nodes[0].codeFragment() || "###ERROR###");
2496
+ this.nextSibling.skip = true;
2497
+ } else if (
2498
+ this.prevSibling == null &&
2499
+ this.blockType == ASTBlock.BlockType.If &&
2500
+ this.nodes.length == 1 &&
2501
+ this.nextSibling instanceof ASTReturn &&
2502
+ this.condition.line == this.nodes[0].line &&
2503
+ this.condition.line == this.nextSibling?.value.line
2504
+ ) {
2505
+ result.add(this.nodes[0].codeFragment() || "###ERROR###");
2506
+ result.lastLineAppend(" if", false);
2507
+ result.lastLineAppend(this.negative ? " not" : "", false);
2508
+ result.lastLineAppend(" ", false);
2509
+ result.lastLineAppend(this.condition?.codeFragment() || "True");
2510
+ result.lastLineAppend(" else ", false);
2511
+ result.lastLineAppend(this.nextSibling.value.codeFragment() || "###ERROR###");
2512
+ this.nextSibling.skip = true;
2513
+ } else {
2514
+ let header = this.type_str;
2515
+ if (this.blockType == ASTBlock.BlockType.Except && this.isExceptStar) {
2516
+ header = "except*";
2517
+ }
2518
+ result.add(header);
2519
+ if ([ASTBlock.BlockType.If, ASTBlock.BlockType.Elif, ASTBlock.BlockType.While].includes(this.blockType)) {
2520
+ result.lastLineAppend(this.negative ? " not" : "", false);
2521
+ result.lastLineAppend(" ", false);
2522
+
2523
+ // For while loops without explicit condition (null), use "1" instead of "True" (Python convention: while 1:)
2524
+ let conditionStr = this.condition?.codeFragment();
2525
+ if (!conditionStr) {
2526
+ conditionStr = this.blockType == ASTBlock.BlockType.While ? "1" : "True";
2527
+ }
2528
+ result.lastLineAppend(conditionStr);
2529
+ } else if ([ASTBlock.BlockType.Except].includes(this.blockType) && this.condition) {
2530
+ if (this.condition instanceof ASTStore) {
2531
+ result.lastLineAppend(" ", false);
2532
+ if (this.condition.src) {
2533
+ result.lastLineAppend(this.condition.src.codeFragment());
2534
+ }
2535
+ result.lastLineAppend(" as ", false);
2536
+ if (this.condition.dest) {
2537
+ result.lastLineAppend(this.condition.dest.codeFragment());
2538
+ }
2539
+ } else if (this.condition) {
2540
+ result.lastLineAppend(" ", false);
2541
+ result.lastLineAppend(this.condition.codeFragment());
2542
+ }
2543
+ }
2544
+
2545
+ result.lastLineAppend(":");
2546
+ result.increaseIndent();
2547
+
2548
+ // Filter nodes for rendering (exclude synthetic __exception__ variables from except blocks)
2549
+ let nodesToRender = this.nodes.filter(el=>el).filter(node => {
2550
+ // Filter out synthetic __exception__ variables from except blocks
2551
+ if (this.blockType == ASTBlock.BlockType.Except &&
2552
+ node instanceof ASTName &&
2553
+ node.name == '__exception__') {
2554
+ return false;
2555
+ }
2556
+ return node && !node.skip && node.codeFragment;
2557
+ });
2558
+
2559
+ if (nodesToRender.length === 0) {
2560
+ result.add("pass");
2561
+ } else {
2562
+ // TODO: Refactor this code to use ASTNodeList that already natively handles that
2563
+ const firstRenderable = nodesToRender[0];
2564
+ if (nodesToRender.length > 1 && firstRenderable && !firstRenderable.nextSibling) {
2565
+ let prevNode = null;
2566
+ for (let node of nodesToRender) {
2567
+ if (!node) {
2568
+ continue;
2569
+ }
2570
+ if (prevNode) {
2571
+ prevNode.nextSibling = node;
2572
+ }
2573
+ node.prevSibling = prevNode;
2574
+ prevNode = node;
2575
+ }
2576
+ }
2577
+ nodesToRender.forEach(node => {
2578
+ result.add(node.codeFragment());
2579
+ });
2580
+ }
2581
+ result.decreaseIndent();
2582
+ }
2583
+ return result;
2584
+ }
2585
+
2586
+ toString() {
2587
+ return `${this.type_str} block: {${this.start} - ${this.end}}`;
2588
+ }
2589
+ }
2590
+
2591
+ class ASTIterBlock extends ASTBlock {
2592
+
2593
+ m_iter = null;
2594
+ m_idx = null;
2595
+ m_cond = null;
2596
+ m_comp = false;
2597
+
2598
+ constructor(blockType, start = 0, end = 0, iter) {
2599
+ super(blockType, start, end);
2600
+ this.m_iter = iter;
2601
+ }
2602
+
2603
+ get start() {
2604
+ return this.m_start;
2605
+ }
2606
+
2607
+ get iter() {
2608
+ return this.m_iter;
2609
+ }
2610
+
2611
+ set iter(value) {
2612
+ this.m_iter = value;
2613
+ }
2614
+
2615
+ get index() {
2616
+ return this.m_idx;
2617
+ }
2618
+
2619
+ set index(value) {
2620
+ this.m_idx = value;
2621
+ }
2622
+
2623
+ get condition() {
2624
+ return this.m_cond;
2625
+ }
2626
+
2627
+ set condition(value) {
2628
+ this.m_cond = value;
2629
+ }
2630
+
2631
+ get comprehension() {
2632
+ return this.m_comp;
2633
+ }
2634
+
2635
+ set comprehension(value) {
2636
+ this.m_comp = value;
2637
+ }
2638
+
2639
+ codeFragment() {
2640
+ let result = new PycResult("", true);
2641
+
2642
+ // Add "async" prefix for async for loops
2643
+ if (this.blockType == ASTBlock.BlockType.AsyncFor) {
2644
+ result.lastLineAppend("async for ", false);
2645
+ } else {
2646
+ result.lastLineAppend("for ", false);
2647
+ }
2648
+
2649
+ result.lastLineAppend(this.index?.codeFragment() || "##ERROR##");
2650
+ result.lastLineAppend(" in ", false);
2651
+ result.lastLineAppend(this.iter?.codeFragment() || "##ERROR##");
2652
+ result.lastLineAppend(":");
2653
+ result.increaseIndent();
2654
+ this.nodes.map(node => node && result.add(node.codeFragment()));
2655
+ result.decreaseIndent();
2656
+
2657
+ return result;
2658
+ }
2659
+
2660
+ toString() {
2661
+ return `${this.type_str} block: {${this.start} - ${this.end}}`;
2662
+ }
2663
+ }
2664
+
2665
+ class ASTContainerBlock extends ASTBlock {
2666
+
2667
+ m_finally = 0;
2668
+ m_except = 0;
2669
+
2670
+ constructor(start = 0, _finally, except = 0) {
2671
+ super(ASTBlock.BlockType.Container, start);
2672
+ this.m_finally = _finally;
2673
+ this.m_except = except;
2674
+ }
2675
+
2676
+ get finally() {
2677
+ return this.m_finally;
2678
+ }
2679
+
2680
+ set finally(value) {
2681
+ this.m_finally = value;
2682
+ }
2683
+
2684
+ get hasFinally() {
2685
+ return !!this.m_finally;
2686
+ }
2687
+
2688
+ get except() {
2689
+ return this.m_except;
2690
+ }
2691
+
2692
+ set except(value) {
2693
+ this.m_except = value;
2694
+ }
2695
+
2696
+ get hasExcept() {
2697
+ return !!this.m_except;
2698
+ }
2699
+
2700
+ codeFragment() {
2701
+ let result = new PycResult("", true);
2702
+
2703
+ this.nodes.filter(el => el instanceof ASTNode).map(node => result.add(node.codeFragment()));
2704
+
2705
+ return result;
2706
+ }
2707
+
2708
+ toString() {
2709
+ return `${this.type_str} block: {${this.start} - ${this.end}}`;
2710
+ }
2711
+ }
2712
+
2713
+ class ASTWithBlock extends ASTBlock {
2714
+
2715
+ m_expr = null;
2716
+ m_var = null;
2717
+
2718
+ constructor(start = 0, end = 0) {
2719
+ super(ASTBlock.BlockType.With, start, end);
2720
+ }
2721
+
2722
+ get expr() {
2723
+ return this.m_expr;
2724
+ }
2725
+
2726
+ set expr(value) {
2727
+ this.m_expr = value;
2728
+ }
2729
+
2730
+ get var() {
2731
+ return this.m_var;
2732
+ }
2733
+
2734
+ set var(value) {
2735
+ this.m_var = value;
2736
+ }
2737
+
2738
+ codeFragment() {
2739
+ let result = new PycResult();
2740
+ result.doNotIndent = true;
2741
+
2742
+ result.lastLineAppend("with ", false);
2743
+ const exprCode = this.expr?.codeFragment ? this.expr.codeFragment() : "None";
2744
+ result.lastLineAppend(exprCode);
2745
+
2746
+ if (this.var) {
2747
+ result.lastLineAppend(" as ", false);
2748
+ result.lastLineAppend(this.var.codeFragment());
2749
+ }
2750
+
2751
+ result.lastLineAppend(":");
2752
+ result.increaseIndent();
2753
+ this.nodes.filter(Boolean).forEach(node => result.add(node.codeFragment()));
2754
+ result.decreaseIndent();
2755
+
2756
+ return result;
2757
+ }
2758
+
2759
+ toString() {
2760
+ return `${this.type_str} block: {${this.start} - ${this.end}}`;
2761
+ }
2762
+ }
2763
+
2764
+ class ASTComprehension extends ASTNode {
2765
+
2766
+ static LIST = 0;
2767
+ static SET = 1;
2768
+ static DICT = 2;
2769
+
2770
+ m_kind = ASTComprehension.LIST;
2771
+ m_key = null;
2772
+ m_result = null;
2773
+ m_generators = [];
2774
+
2775
+ constructor(result, key = null) {
2776
+ super();
2777
+ this.m_key = key;
2778
+ this.m_result = result;
2779
+ }
2780
+
2781
+ set kind (value) {
2782
+ this.m_kind = value;
2783
+ }
2784
+
2785
+ get kind () {
2786
+ return this.m_kind;
2787
+ }
2788
+
2789
+ set key (value) {
2790
+ this.m_key = value;
2791
+ }
2792
+
2793
+ get key () {
2794
+ return this.m_key;
2795
+ }
2796
+
2797
+ get result() {
2798
+ return this.m_result;
2799
+ }
2800
+
2801
+ get generators() {
2802
+ return this.m_generators;
2803
+ }
2804
+
2805
+ get lastLine() {
2806
+ return this.generators[this.generators.length - 1]?.lastLine;
2807
+ }
2808
+
2809
+ addGenerator(generator) {
2810
+ this.generators.unshift(generator);
2811
+ }
2812
+
2813
+ codeFragment() {
2814
+ let openingBracket = '[';
2815
+ let closingBracket = ']';
2816
+
2817
+ if ([ASTComprehension.SET, ASTComprehension.DICT].includes(this.kind)) {
2818
+ openingBracket = '{';
2819
+ closingBracket = '}';
2820
+ }
2821
+
2822
+ if (!this.result) {
2823
+ return `${openingBracket}##ERROR##${closingBracket}`;
2824
+ }
2825
+
2826
+ let result = `${openingBracket}${this.kind == ASTComprehension.DICT ? (this.key?.codeFragment() || "##ERROR##") + ": " : ""}${this.result.codeFragment?.() || "##ERROR##"}`;
2827
+
2828
+ result += this.generators.map(gen => {
2829
+ let genString = ` for ${gen.index?.codeFragment?.() || "##ERROR##"} in ${gen.iter?.codeFragment?.() || "##ERROR##"}`;
2830
+
2831
+ if (gen.condition) {
2832
+ genString += ` if ${gen.condition.codeFragment?.() || "##ERROR##"}`;
2833
+ }
2834
+
2835
+ return genString;
2836
+ }).join("") + closingBracket;
2837
+
2838
+ return result;
2839
+ }
2840
+
2841
+ toString() {
2842
+ return `ASTComprehension: {${this.start} - ${this.end}}, ${this.codeFragment()}`;
2843
+ }
2844
+ }
2845
+
2846
+ class ASTLoadBuildClass extends ASTNode {
2847
+
2848
+ m_obj = null;
2849
+
2850
+ constructor(obj) {
2851
+ super();
2852
+ this.m_obj = obj;
2853
+ }
2854
+
2855
+ get object() {
2856
+ return this.m_obj;
2857
+ }
2858
+
2859
+ codeFragment() {
2860
+ return "";
2861
+ }
2862
+
2863
+ toString() {
2864
+ return `ASTLoadBuildClass: [${this.line} - ${this.lastLine}], ${this.codeFragment()}`;
2865
+ }
2866
+ }
2867
+
2868
+ class ASTAwaitable extends ASTNode {
2869
+
2870
+ m_expr = null;
2871
+
2872
+ constructor(expr) {
2873
+ super();
2874
+ this.m_expr = expr;
2875
+ }
2876
+
2877
+ get expression() {
2878
+ return this.m_expr;
2879
+ }
2880
+
2881
+ codeFragment() {
2882
+ // await <expression>
2883
+ const inner = this.m_expr?.codeFragment ? this.m_expr.codeFragment() : "None";
2884
+ return `await ${inner}`;
2885
+ }
2886
+
2887
+ toString() {
2888
+ return `ASTAwaitable: [${this.line} - ${this.lastLine}], ${this.codeFragment()}`;
2889
+ }
2890
+ }
2891
+
2892
+ class ASTFormattedValue extends ASTNode {
2893
+ static ConversionFlag = {
2894
+ None: 0,
2895
+ Str: 1,
2896
+ Repr: 2,
2897
+ ASCII: 3,
2898
+ FmtSpec: 4
2899
+ }
2900
+
2901
+ m_val = null;
2902
+ m_conversion = null;
2903
+ m_format_spec = null;
2904
+
2905
+ constructor(val, conversion, format_spec) {
2906
+ super();
2907
+ this.m_val = val;
2908
+ this.m_conversion = conversion;
2909
+ this.m_format_spec = format_spec;
2910
+ }
2911
+
2912
+ get val() {
2913
+ return this.m_val;
2914
+ }
2915
+
2916
+ get conversion() {
2917
+ return this.m_conversion;
2918
+ }
2919
+
2920
+ get format_spec() {
2921
+ return this.m_format_spec;
2922
+ }
2923
+
2924
+ codeFragment() {
2925
+ // Format: {value} or {value!r} or {value:.2f}
2926
+ let result = "{";
2927
+
2928
+ if (this.m_val) {
2929
+ result += this.m_val.codeFragment();
2930
+ }
2931
+
2932
+ // Add conversion flag (!s, !r, !a)
2933
+ switch (this.m_conversion) {
2934
+ case ASTFormattedValue.ConversionFlag.Str:
2935
+ result += "!s";
2936
+ break;
2937
+ case ASTFormattedValue.ConversionFlag.Repr:
2938
+ result += "!r";
2939
+ break;
2940
+ case ASTFormattedValue.ConversionFlag.ASCII:
2941
+ result += "!a";
2942
+ break;
2943
+ }
2944
+
2945
+ // Add format spec (:.2f, :x, etc.)
2946
+ if (this.m_format_spec) {
2947
+ result += ":";
2948
+ // Format spec can be ASTJoinedStr (nested f-string) or string constant
2949
+ if (this.m_format_spec instanceof ASTJoinedStr) {
2950
+ result += this.m_format_spec.codeFragment();
2951
+ } else if (this.m_format_spec instanceof ASTObject) {
2952
+ // String constant like ".2f"
2953
+ result += this.m_format_spec.object.Value;
2954
+ } else {
2955
+ result += this.m_format_spec.codeFragment();
2956
+ }
2957
+ }
2958
+
2959
+ result += "}";
2960
+ return result;
2961
+ }
2962
+
2963
+ toString() {
2964
+ return `${this.constructor.name}: Line ${this.line}, ${this.codeFragment()}`;
2965
+ }
2966
+ }
2967
+
2968
+ class ASTJoinedStr extends ASTNode {
2969
+ m_values = null;
2970
+
2971
+ constructor(values) {
2972
+ super();
2973
+ this.m_values = values;
2974
+ }
2975
+
2976
+ get values() {
2977
+ return this.m_values;
2978
+ }
2979
+
2980
+ get lastLine() {
2981
+ return this.values[this.values.length - 1]?.lastLine;
2982
+ }
2983
+ codeFragment() {
2984
+ // f-string format: f"literal {expr} literal"
2985
+ let result = 'f"';
2986
+
2987
+ // Values are in reverse order (BUILD_STRING pops from stack)
2988
+ // So we need to reverse them
2989
+ let values = [...this.m_values].reverse();
2990
+
2991
+ for (let i = 0; i < values.length; i++) {
2992
+ let value = values[i];
2993
+
2994
+ if (value instanceof ASTFormattedValue) {
2995
+ // Check for f-string = debugging pattern (Python 3.8+)
2996
+ // Pattern: literal ending with "varname=" followed by {varname!r}
2997
+ if (i > 0 && values[i-1] instanceof ASTObject) {
2998
+ let prevStr = values[i-1].object?.Value || '';
2999
+ if (typeof prevStr !== 'string') {
3000
+ prevStr = String(prevStr ?? '');
3001
+ }
3002
+ let match = prevStr.match(/([a-zA-Z_]\w*)\s*=$/);
3003
+
3004
+ if (match && value.m_val instanceof ASTName &&
3005
+ value.m_val.name === match[1] &&
3006
+ value.m_conversion === ASTFormattedValue.ConversionFlag.Repr &&
3007
+ !value.m_format_spec) {
3008
+ // This is f"{varname=}" syntax
3009
+ // Remove the "varname=" suffix from previous literal
3010
+ let prefix = prevStr.substring(0, prevStr.length - match[0].length);
3011
+
3012
+ // Remove the previously added literal and replace with prefix + {var=}
3013
+ let beforeLiteral = result.lastIndexOf(prevStr.replace(/\\/g, '\\\\')
3014
+ .replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\t/g, '\\t'));
3015
+
3016
+ if (beforeLiteral !== -1) {
3017
+ result = result.substring(0, beforeLiteral);
3018
+ }
3019
+
3020
+ // Add prefix (escaped) if present
3021
+ if (prefix) {
3022
+ let escapedPrefix = prefix.replace(/\\/g, '\\\\');
3023
+ escapedPrefix = escapedPrefix.replace(/"/g, '\\"');
3024
+ escapedPrefix = escapedPrefix.replace(/\n/g, '\\n');
3025
+ escapedPrefix = escapedPrefix.replace(/\t/g, '\\t');
3026
+ result += escapedPrefix;
3027
+ }
3028
+
3029
+ // Add {varname=} instead of varname={varname!r}
3030
+ result += `{${match[1]}=}`;
3031
+ continue;
3032
+ }
3033
+ }
3034
+
3035
+ // {expression} part
3036
+ result += value.codeFragment();
3037
+ } else if (value instanceof ASTObject && value.object?.ClassName === 'Py_String') {
3038
+ // Literal string part - need to escape special chars
3039
+ let str = value.object.Value;
3040
+ // Escape backslashes and quotes
3041
+ str = str.replace(/\\/g, '\\\\');
3042
+ str = str.replace(/"/g, '\\"');
3043
+ str = str.replace(/\n/g, '\\n');
3044
+ str = str.replace(/\t/g, '\\t');
3045
+ result += str;
3046
+ } else {
3047
+ // Fallback for unexpected types
3048
+ result += value.codeFragment();
3049
+ }
3050
+ }
3051
+
3052
+ result += '"';
3053
+ return result;
3054
+ }
3055
+
3056
+ toString() {
3057
+ return `${this.constructor.name}: Line ${this.line}, ${this.codeFragment()}`;
3058
+ }
3059
+ }
3060
+
3061
+ class ASTAnnotatedVar extends ASTNode {
3062
+ m_name = null;
3063
+ m_type = null;
3064
+
3065
+ constructor(name, type) {
3066
+ super();
3067
+ this.m_name = name;
3068
+ this.m_type = type;
3069
+ }
3070
+
3071
+ get name() {
3072
+ return this.m_name;
3073
+ }
3074
+
3075
+ get annotation() {
3076
+ return this.m_type;
3077
+ }
3078
+
3079
+ formatIdentifier(node) {
3080
+ if (!node) {
3081
+ return '##ERROR##';
3082
+ }
3083
+ if (node instanceof ASTName) {
3084
+ return node.name;
3085
+ }
3086
+ if (node instanceof ASTObject) {
3087
+ let value = node.object?.Value;
3088
+ if (value !== undefined && value !== null) {
3089
+ return value.toString();
3090
+ }
3091
+ }
3092
+ let fragment = node.codeFragment?.();
3093
+ if (!fragment) {
3094
+ fragment = node.toString?.();
3095
+ }
3096
+ if (!fragment) {
3097
+ return '##ERROR##';
3098
+ }
3099
+ fragment = fragment.toString ? fragment.toString() : fragment;
3100
+ if (typeof fragment === 'string') {
3101
+ let trimmed = fragment.trim();
3102
+ if ((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
3103
+ (trimmed.startsWith('"') && trimmed.endsWith('"'))) {
3104
+ return trimmed.substring(1, trimmed.length - 1);
3105
+ }
3106
+ return trimmed;
3107
+ }
3108
+ return fragment;
3109
+ }
3110
+
3111
+ codeFragment() {
3112
+ let name = this.formatIdentifier(this.name);
3113
+ let annotation = this.annotation?.codeFragment ? this.annotation.codeFragment().toString() : this.annotation?.toString() || '##ERROR##';
3114
+ return `${name}: ${annotation}`;
3115
+ }
3116
+
3117
+ toString() {
3118
+ return `${this.constructor.name}: Line ${this.line}, ${this.codeFragment()}`;
3119
+ }
3120
+ }
3121
+
3122
+ class ASTTypeAlias extends ASTNode {
3123
+ m_name = null;
3124
+ m_value = null;
3125
+
3126
+ constructor(name, value) {
3127
+ super();
3128
+ this.m_name = name;
3129
+ this.m_value = value;
3130
+ }
3131
+
3132
+ get name() {
3133
+ return this.m_name;
3134
+ }
3135
+
3136
+ get value() {
3137
+ return this.m_value;
3138
+ }
3139
+
3140
+ formatName() {
3141
+ if (typeof this.name === 'string') {
3142
+ return this.name;
3143
+ }
3144
+ if (this.name?.codeFragment) {
3145
+ const fragment = this.name.codeFragment();
3146
+ return fragment?.toString?.() || '##ERROR##';
3147
+ }
3148
+ return `${this.name}`;
3149
+ }
3150
+
3151
+ codeFragment() {
3152
+ const name = this.formatName();
3153
+ const valueFragment = this.value?.codeFragment ? this.value.codeFragment() : this.value?.toString?.() || 'None';
3154
+ return `type ${name} = ${valueFragment}`;
3155
+ }
3156
+ }
3157
+
3158
+ class ASTTernary extends ASTNode {
3159
+ m_if_block = null;
3160
+ m_if_expr = null;
3161
+ m_else_expr = null;
3162
+ constructor(if_block, if_expr, else_expr) {
3163
+ super();
3164
+ this.m_if_block = if_block;
3165
+ this.m_if_expr = if_expr;
3166
+ this.m_else_expr = else_expr;
3167
+ }
3168
+
3169
+ get if_block() {
3170
+ return this.m_if_block;
3171
+ }
3172
+
3173
+ get if_expr() {
3174
+ return this.m_if_expr;
3175
+ }
3176
+
3177
+ get else_expr() {
3178
+ return this.m_else_expr;
3179
+ }
3180
+
3181
+ get lastLine() {
3182
+ return this.else_expr?.lastLine;
3183
+ }
3184
+
3185
+ codeFragment() {
3186
+ const neg = this.if_block?.negative ? "not " : "";
3187
+ let result = `${this.if_expr?.codeFragment() || '##ERROR##'} if ${neg}${this.if_block.condition?.codeFragment() || '##ERROR##'} else ${this.else_expr?.codeFragment() || '##ERROR##'}`;
3188
+ return result;
3189
+ }
3190
+
3191
+ toString() {
3192
+ return `${this.constructor.name}: Line ${this.line}, ${this.codeFragment()}`;
3193
+ }}
3194
+
3195
+ // Match/Case statement (Python 3.10+)
3196
+ class ASTMatch extends ASTNode {
3197
+ m_subject = null;
3198
+ m_cases = [];
3199
+
3200
+ constructor(subject, cases = []) {
3201
+ super();
3202
+ this.m_subject = subject;
3203
+ this.m_cases = cases;
3204
+ }
3205
+
3206
+ get subject() {
3207
+ return this.m_subject;
3208
+ }
3209
+
3210
+ get cases() {
3211
+ return this.m_cases;
3212
+ }
3213
+
3214
+ addCase(caseNode) {
3215
+ this.m_cases.push(caseNode);
3216
+ }
3217
+
3218
+ codeFragment() {
3219
+ let result = new PycResult("", true);
3220
+ result.add(`match ${this.m_subject.codeFragment()}:`);
3221
+ result.increaseIndent();
3222
+
3223
+ for (let caseNode of this.m_cases) {
3224
+ result.add(caseNode.codeFragment());
3225
+ }
3226
+
3227
+ result.decreaseIndent();
3228
+ return result;
3229
+ }
3230
+
3231
+ toString() {
3232
+ return `ASTMatch: ${this.m_cases.length} cases`;
3233
+ }
3234
+ }
3235
+
3236
+ class ASTCase extends ASTNode {
3237
+ m_pattern = null;
3238
+ m_guard = null;
3239
+ m_body = null;
3240
+
3241
+ constructor(pattern, body, guard = null) {
3242
+ super();
3243
+ this.m_pattern = pattern;
3244
+ this.m_body = body;
3245
+ this.m_guard = guard;
3246
+ }
3247
+
3248
+ get pattern() {
3249
+ return this.m_pattern;
3250
+ }
3251
+
3252
+ get guard() {
3253
+ return this.m_guard;
3254
+ }
3255
+
3256
+ get body() {
3257
+ return this.m_body;
3258
+ }
3259
+
3260
+ codeFragment() {
3261
+ let result = new PycResult("", true);
3262
+ let patternStr = this.m_pattern?.codeFragment() || '##ERROR##';
3263
+
3264
+ if (this.m_guard) {
3265
+ result.add(`case ${patternStr} if ${this.m_guard.codeFragment()}:`);
3266
+ } else {
3267
+ result.add(`case ${patternStr}:`);
3268
+ }
3269
+
3270
+ result.increaseIndent();
3271
+ if (this.m_body) {
3272
+ result.add(this.m_body.codeFragment());
3273
+ } else {
3274
+ result.add("pass");
3275
+ }
3276
+ result.decreaseIndent();
3277
+
3278
+ return result;
3279
+ }
3280
+
3281
+ toString() {
3282
+ return `ASTCase: pattern=${this.m_pattern?.constructor.name}`;
3283
+ }
3284
+ }
3285
+
3286
+ class ASTPattern extends ASTNode {
3287
+ static PatternType = {
3288
+ Literal: 0, // 1, "str", None
3289
+ Variable: 1, // x (captures value)
3290
+ Wildcard: 2, // _ (matches anything)
3291
+ Sequence: 3, // (x, y) or [a, b]
3292
+ Mapping: 4, // {"key": pattern}
3293
+ Class: 5, // ClassName(pattern)
3294
+ Or: 6, // pattern1 | pattern2
3295
+ As: 7 // pattern as name
3296
+ };
3297
+
3298
+ m_type = null;
3299
+ m_value = null;
3300
+
3301
+ constructor(type, value) {
3302
+ super();
3303
+ this.m_type = type;
3304
+ this.m_value = value;
3305
+ }
3306
+
3307
+ get type() {
3308
+ return this.m_type;
3309
+ }
3310
+
3311
+ get value() {
3312
+ return this.m_value;
3313
+ }
3314
+
3315
+ codeFragment() {
3316
+ switch (this.m_type) {
3317
+ case ASTPattern.PatternType.Literal:
3318
+ // value is AST node for literal (ASTObject, ASTNone, etc.)
3319
+ return this.m_value?.codeFragment() || '##ERROR##';
3320
+
3321
+ case ASTPattern.PatternType.Variable:
3322
+ // value is variable name (string)
3323
+ return this.m_value;
3324
+
3325
+ case ASTPattern.PatternType.Wildcard:
3326
+ return '_';
3327
+
3328
+ case ASTPattern.PatternType.Sequence:
3329
+ // value is array of patterns
3330
+ if (Array.isArray(this.m_value)) {
3331
+ let items = this.m_value.map(p => p.codeFragment()).join(', ');
3332
+ return `(${items})`;
3333
+ }
3334
+ return '()';
3335
+
3336
+ case ASTPattern.PatternType.Mapping:
3337
+ // value is array of {key, pattern} or plain object {key: pattern}
3338
+ const renderKey = (key) => key?.codeFragment ? key.codeFragment() : (key?.toString?.() || '##ERROR##');
3339
+ if (Array.isArray(this.m_value)) {
3340
+ const pairs = this.m_value.map(entry => `${renderKey(entry.key)}: ${entry.pattern.codeFragment()}`);
3341
+ return `{${pairs.join(', ')}}`;
3342
+ }
3343
+ if (typeof this.m_value === 'object') {
3344
+ let pairs = [];
3345
+ for (let [key, pattern] of Object.entries(this.m_value)) {
3346
+ pairs.push(`${key}: ${pattern.codeFragment()}`);
3347
+ }
3348
+ return `{${pairs.join(', ')}}`;
3349
+ }
3350
+ return '{}';
3351
+
3352
+ case ASTPattern.PatternType.Class:
3353
+ if (this.m_value) {
3354
+ const classExpr = this.m_value.classExpr;
3355
+ const classStr = classExpr?.codeFragment ? classExpr.codeFragment() : (classExpr?.constructor?.name ? classExpr.codeFragment?.() : classExpr) || 'object';
3356
+ const attrs = (this.m_value.attributes || []).map(attr => {
3357
+ const rhs = attr.pattern?.codeFragment ? attr.pattern.codeFragment() : attr.pattern;
3358
+ return attr.name ? `${attr.name}=${rhs}` : rhs;
3359
+ });
3360
+ return `${classStr}(${attrs.join(', ')})`;
3361
+ }
3362
+ return 'object()';
3363
+
3364
+ case ASTPattern.PatternType.Or:
3365
+ // value is array of patterns
3366
+ if (Array.isArray(this.m_value)) {
3367
+ return this.m_value.map(p => p.codeFragment()).join(' | ');
3368
+ }
3369
+ return '##ERROR##';
3370
+
3371
+ case ASTPattern.PatternType.As:
3372
+ // value is {pattern, name}
3373
+ if (this.m_value?.pattern && this.m_value?.name) {
3374
+ return `${this.m_value.pattern.codeFragment()} as ${this.m_value.name}`;
3375
+ }
3376
+ return '##ERROR##';
3377
+
3378
+ default:
3379
+ return '#TODO pattern';
3380
+ }
3381
+ }
3382
+
3383
+ toString() {
3384
+ return `ASTPattern: type=${this.m_type}`;
3385
+ }
3386
+ }
3387
+
3388
+ module.exports = {
3389
+ ASTNode,
3390
+ ASTNone,
3391
+ ASTLocals,
3392
+ ASTNodeList,
3393
+ ASTChainStore,
3394
+ ASTObject,
3395
+ ASTUnary,
3396
+ ASTBinary,
3397
+ ASTCompare,
3398
+ ASTSlice,
3399
+ ASTStore,
3400
+ ASTReturn,
3401
+ ASTNamedExpr,
3402
+ ASTName,
3403
+ ASTDelete,
3404
+ ASTFunction,
3405
+ ASTClass,
3406
+ ASTCall,
3407
+ ASTImport,
3408
+ ASTTuple,
3409
+ ASTList,
3410
+ ASTSet,
3411
+ ASTMap,
3412
+ ASTKwNamesMap,
3413
+ ASTConstMap,
3414
+ ASTSubscr,
3415
+ ASTPrint,
3416
+ ASTConvert,
3417
+ ASTKeyword,
3418
+ ASTRaise,
3419
+ ASTExec,
3420
+ ASTBlock,
3421
+ ASTCondBlock,
3422
+ ASTIterBlock,
3423
+ ASTContainerBlock,
3424
+ ASTWithBlock,
3425
+ ASTComprehension,
3426
+ ASTLoadBuildClass,
3427
+ ASTAwaitable,
3428
+ ASTFormattedValue,
3429
+ ASTJoinedStr,
3430
+ ASTAnnotatedVar,
3431
+ ASTTypeAlias,
3432
+ ASTTernary,
3433
+ ASTMatch,
3434
+ ASTCase,
3435
+ ASTPattern,
3436
+ ASTIteratorValue
3437
+ };
3438
+
3439
+ // Registering classes in global scope for propoer class deserialization.
3440
+ for (let className of Object.keys(module.exports)) {
3441
+ global[className] = new module.exports[className]();
3442
+ }