python2ts 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -92
  3. package/dist/chunk-VYG6GDWU.js +4482 -0
  4. package/dist/chunk-VYG6GDWU.js.map +1 -0
  5. package/dist/cli/index.js +1 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.js +1 -6
  9. package/dist/index.js.map +1 -1
  10. package/package.json +29 -14
  11. package/dist/acorn-CDFV7HXL.js +0 -3131
  12. package/dist/acorn-CDFV7HXL.js.map +0 -1
  13. package/dist/angular-PCJAXZFD.js +0 -3071
  14. package/dist/angular-PCJAXZFD.js.map +0 -1
  15. package/dist/babel-O6RKFHDQ.js +0 -7297
  16. package/dist/babel-O6RKFHDQ.js.map +0 -1
  17. package/dist/chunk-DTI6R2GT.js +0 -23942
  18. package/dist/chunk-DTI6R2GT.js.map +0 -1
  19. package/dist/chunk-PZ5AY32C.js +0 -10
  20. package/dist/chunk-PZ5AY32C.js.map +0 -1
  21. package/dist/estree-467CT4RF.js +0 -4613
  22. package/dist/estree-467CT4RF.js.map +0 -1
  23. package/dist/flow-FCFYGKSM.js +0 -27547
  24. package/dist/flow-FCFYGKSM.js.map +0 -1
  25. package/dist/glimmer-P46N72G3.js +0 -2895
  26. package/dist/glimmer-P46N72G3.js.map +0 -1
  27. package/dist/graphql-TFOZJU7B.js +0 -1267
  28. package/dist/graphql-TFOZJU7B.js.map +0 -1
  29. package/dist/html-KZKNLN3R.js +0 -2934
  30. package/dist/html-KZKNLN3R.js.map +0 -1
  31. package/dist/markdown-BS2B5AKD.js +0 -3554
  32. package/dist/markdown-BS2B5AKD.js.map +0 -1
  33. package/dist/meriyah-W6HUPGCY.js +0 -2685
  34. package/dist/meriyah-W6HUPGCY.js.map +0 -1
  35. package/dist/postcss-LMJBCD2Q.js +0 -5081
  36. package/dist/postcss-LMJBCD2Q.js.map +0 -1
  37. package/dist/typescript-Y5EPKFX5.js +0 -13204
  38. package/dist/typescript-Y5EPKFX5.js.map +0 -1
  39. package/dist/yaml-ZNP3H3BX.js +0 -4225
  40. package/dist/yaml-ZNP3H3BX.js.map +0 -1
@@ -0,0 +1,4482 @@
1
+ // src/parser/index.ts
2
+ import { parser } from "@lezer/python";
3
+ function parse(source) {
4
+ const tree = parser.parse(source);
5
+ return { tree, source };
6
+ }
7
+ function getNodeText(node, source) {
8
+ return source.slice(node.from, node.to);
9
+ }
10
+ function getChildren(node) {
11
+ const children = [];
12
+ let child = node.firstChild;
13
+ while (child) {
14
+ children.push(child);
15
+ child = child.nextSibling;
16
+ }
17
+ return children;
18
+ }
19
+ function getChildByType(node, type) {
20
+ let child = node.firstChild;
21
+ while (child) {
22
+ if (child.name === type) {
23
+ return child;
24
+ }
25
+ child = child.nextSibling;
26
+ }
27
+ return null;
28
+ }
29
+ function getChildrenByType(node, type) {
30
+ const children = [];
31
+ let child = node.firstChild;
32
+ while (child) {
33
+ if (child.name === type) {
34
+ children.push(child);
35
+ }
36
+ child = child.nextSibling;
37
+ }
38
+ return children;
39
+ }
40
+ function walkTree(tree, callback) {
41
+ const cursor = tree.cursor();
42
+ do {
43
+ callback(cursor.node);
44
+ } while (cursor.next());
45
+ }
46
+ function debugTree(tree, source) {
47
+ const lines = [];
48
+ const cursor = tree.cursor();
49
+ let depth = 0;
50
+ do {
51
+ const indent = " ".repeat(depth);
52
+ const text = source.slice(cursor.from, cursor.to).replace(/\n/g, "\\n");
53
+ const preview = text.length > 30 ? text.slice(0, 30) + "..." : text;
54
+ lines.push(`${indent}${cursor.name} [${String(cursor.from)}-${String(cursor.to)}] "${preview}"`);
55
+ if (cursor.firstChild()) {
56
+ depth++;
57
+ } else {
58
+ while (!cursor.nextSibling()) {
59
+ if (!cursor.parent()) break;
60
+ depth--;
61
+ }
62
+ }
63
+ } while (depth > 0 || cursor.nextSibling());
64
+ return lines.join("\n");
65
+ }
66
+
67
+ // src/transformer/name-mappings.ts
68
+ var PYTHON_TO_JS_NAMES = {
69
+ // ============================================================================
70
+ // functools module
71
+ // ============================================================================
72
+ lru_cache: "lruCache",
73
+ cache_info: "cacheInfo",
74
+ cache_clear: "cacheClear",
75
+ partialmethod: "partialMethod",
76
+ singledispatch: "singleDispatch",
77
+ attrgetter: "attrGetter",
78
+ itemgetter: "itemGetter",
79
+ methodcaller: "methodCaller",
80
+ cmp_to_key: "cmpToKey",
81
+ total_ordering: "totalOrdering",
82
+ // ============================================================================
83
+ // itertools module
84
+ // ============================================================================
85
+ zip_longest: "zipLongest",
86
+ takewhile: "takeWhile",
87
+ dropwhile: "dropWhile",
88
+ filterfalse: "filterFalse",
89
+ combinations_with_replacement: "combinationsWithReplacement",
90
+ // ============================================================================
91
+ // collections module (deque methods)
92
+ // ============================================================================
93
+ appendleft: "appendLeft",
94
+ popleft: "popLeft",
95
+ extendleft: "extendLeft",
96
+ // ============================================================================
97
+ // datetime module
98
+ // ============================================================================
99
+ total_seconds: "totalSeconds",
100
+ fromtimestamp: "fromTimestamp",
101
+ fromisoformat: "fromIsoFormat",
102
+ fromordinal: "fromOrdinal",
103
+ toordinal: "toOrdinal",
104
+ isoweekday: "isoWeekday",
105
+ isocalendar: "isoCalendar",
106
+ isoformat: "isoFormat",
107
+ utcnow: "utcNow",
108
+ utcfromtimestamp: "utcFromTimestamp",
109
+ // ============================================================================
110
+ // random module
111
+ // ============================================================================
112
+ randint: "randInt",
113
+ randrange: "randRange",
114
+ betavariate: "betaVariate",
115
+ expovariate: "expoVariate",
116
+ gammavariate: "gammaVariate",
117
+ lognormvariate: "logNormVariate",
118
+ vonmisesvariate: "vonMisesVariate",
119
+ paretovariate: "paretoVariate",
120
+ weibullvariate: "weibullVariate",
121
+ normalvariate: "normalVariate",
122
+ // ============================================================================
123
+ // string module (constants)
124
+ // ============================================================================
125
+ ascii_lowercase: "asciiLowercase",
126
+ ascii_uppercase: "asciiUppercase",
127
+ ascii_letters: "asciiLetters",
128
+ hexdigits: "hexDigits",
129
+ octdigits: "octDigits",
130
+ // ============================================================================
131
+ // string module (methods)
132
+ // ============================================================================
133
+ safe_substitute: "safeSubstitute",
134
+ get_identifiers: "getIdentifiers",
135
+ capwords: "capWords",
136
+ startswith: "startsWith",
137
+ endswith: "endsWith",
138
+ isalpha: "isAlpha",
139
+ isdigit: "isDigit",
140
+ isalnum: "isAlnum",
141
+ isspace: "isSpace",
142
+ isupper: "isUpper",
143
+ islower: "isLower",
144
+ lstrip: "lStrip",
145
+ rstrip: "rStrip",
146
+ zfill: "zFill",
147
+ ljust: "lJust",
148
+ rjust: "rJust",
149
+ rpartition: "rPartition",
150
+ swapcase: "swapCase",
151
+ rfind: "rFind",
152
+ rindex: "rIndex",
153
+ rsplit: "rSplit",
154
+ // ============================================================================
155
+ // os module (constants)
156
+ // ============================================================================
157
+ altsep: "altSep",
158
+ pathsep: "pathSep",
159
+ linesep: "lineSep",
160
+ curdir: "curDir",
161
+ pardir: "parDir",
162
+ extsep: "extSep",
163
+ // ============================================================================
164
+ // os.path module
165
+ // ============================================================================
166
+ splitext: "splitExt",
167
+ extname: "extName",
168
+ isabs: "isAbs",
169
+ normpath: "normPath",
170
+ abspath: "absPath",
171
+ realpath: "realPath",
172
+ relpath: "relPath",
173
+ commonpath: "commonPath",
174
+ expanduser: "expandUser",
175
+ expandvars: "expandVars",
176
+ isfile: "isFile",
177
+ isdir: "isDir",
178
+ islink: "isLink",
179
+ getsize: "getSize",
180
+ getmtime: "getMtime",
181
+ getatime: "getAtime",
182
+ getctime: "getCtime",
183
+ // ============================================================================
184
+ // os module (functions)
185
+ // ============================================================================
186
+ getcwd: "getCwd",
187
+ getcwdb: "getCwdb",
188
+ listdir: "listDir",
189
+ makedirs: "makeDirs",
190
+ removedirs: "removeDirs",
191
+ // ============================================================================
192
+ // re module
193
+ // ============================================================================
194
+ groupdict: "groupDict",
195
+ groupindex: "groupIndex",
196
+ lastindex: "lastIndex",
197
+ lastgroup: "lastGroup",
198
+ fullmatch: "fullMatch",
199
+ findall: "findAll",
200
+ finditer: "findIter",
201
+ // ============================================================================
202
+ // dict module
203
+ // ============================================================================
204
+ setdefault: "setDefault",
205
+ popitem: "popItem",
206
+ fromkeys: "fromKeys",
207
+ // ============================================================================
208
+ // set module
209
+ // ============================================================================
210
+ issubset: "isSubset",
211
+ issuperset: "isSuperset",
212
+ isdisjoint: "isDisjoint",
213
+ // ============================================================================
214
+ // core module
215
+ // ============================================================================
216
+ floordiv: "floorDiv",
217
+ divmod: "divMod"
218
+ };
219
+ function toJsName(pythonName) {
220
+ return PYTHON_TO_JS_NAMES[pythonName] ?? pythonName;
221
+ }
222
+
223
+ // src/transformer/index.ts
224
+ function createContext(source) {
225
+ return {
226
+ source,
227
+ indentLevel: 0,
228
+ usesRuntime: /* @__PURE__ */ new Set(),
229
+ scopeStack: [/* @__PURE__ */ new Set()],
230
+ // Start with one global scope
231
+ definedClasses: /* @__PURE__ */ new Set()
232
+ };
233
+ }
234
+ function pushScope(ctx) {
235
+ ctx.scopeStack.push(/* @__PURE__ */ new Set());
236
+ }
237
+ function popScope(ctx) {
238
+ if (ctx.scopeStack.length > 1) {
239
+ ctx.scopeStack.pop();
240
+ }
241
+ }
242
+ function isVariableDeclared(ctx, name) {
243
+ for (const scope of ctx.scopeStack) {
244
+ if (scope.has(name)) return true;
245
+ }
246
+ return false;
247
+ }
248
+ function declareVariable(ctx, name) {
249
+ const currentScope = ctx.scopeStack[ctx.scopeStack.length - 1];
250
+ if (currentScope) {
251
+ currentScope.add(name);
252
+ }
253
+ }
254
+ function containsYield(node) {
255
+ if (node.name === "YieldStatement" || node.name === "YieldExpression") {
256
+ return true;
257
+ }
258
+ if (node.name === "FunctionDefinition" || node.name === "LambdaExpression") {
259
+ return false;
260
+ }
261
+ for (const child of getChildren(node)) {
262
+ if (containsYield(child)) {
263
+ return true;
264
+ }
265
+ }
266
+ return false;
267
+ }
268
+ var PYTHON_TO_TS_TYPES = {
269
+ str: "string",
270
+ int: "number",
271
+ float: "number",
272
+ bool: "boolean",
273
+ bytes: "Uint8Array",
274
+ None: "null",
275
+ Any: "any",
276
+ object: "object"
277
+ };
278
+ function transformPythonType(node, ctx) {
279
+ switch (node.name) {
280
+ case "VariableName": {
281
+ const typeName = getNodeText(node, ctx.source);
282
+ return PYTHON_TO_TS_TYPES[typeName] ?? typeName;
283
+ }
284
+ case "None":
285
+ return "null";
286
+ case "MemberExpression": {
287
+ const children = getChildren(node);
288
+ const baseType = children[0];
289
+ if (!baseType) return getNodeText(node, ctx.source);
290
+ const baseName = getNodeText(baseType, ctx.source);
291
+ const bracketStart = children.findIndex((c) => c.name === "[");
292
+ const bracketEnd = children.findIndex((c) => c.name === "]");
293
+ if (bracketStart === -1 || bracketEnd === -1) {
294
+ return PYTHON_TO_TS_TYPES[baseName] ?? baseName;
295
+ }
296
+ const rawTypeArgNodes = children.slice(bracketStart + 1, bracketEnd).filter((c) => c.name !== ",");
297
+ if (baseName === "Callable") {
298
+ return transformCallableType(rawTypeArgNodes, ctx);
299
+ }
300
+ const typeArgs = rawTypeArgNodes.map((c) => transformPythonType(c, ctx));
301
+ const first = typeArgs[0] ?? "unknown";
302
+ const second = typeArgs[1] ?? "unknown";
303
+ const last = typeArgs[typeArgs.length - 1] ?? "unknown";
304
+ switch (baseName) {
305
+ case "list":
306
+ case "List":
307
+ return typeArgs.length > 0 ? `${first}[]` : "unknown[]";
308
+ case "dict":
309
+ case "Dict":
310
+ return typeArgs.length >= 2 ? `Record<${first}, ${second}>` : "Record<string, unknown>";
311
+ case "set":
312
+ case "Set":
313
+ return typeArgs.length > 0 ? `Set<${first}>` : "Set<unknown>";
314
+ case "frozenset":
315
+ case "FrozenSet":
316
+ return typeArgs.length > 0 ? `ReadonlySet<${first}>` : "ReadonlySet<unknown>";
317
+ case "tuple":
318
+ case "Tuple":
319
+ return `[${typeArgs.join(", ")}]`;
320
+ case "Optional":
321
+ return typeArgs.length > 0 ? `${first} | null` : "unknown | null";
322
+ case "Union":
323
+ return typeArgs.join(" | ");
324
+ case "Final":
325
+ return typeArgs.length > 0 ? first : "unknown";
326
+ case "ClassVar":
327
+ return typeArgs.length > 0 ? first : "unknown";
328
+ // Callable is handled specially before the switch via transformCallableType
329
+ /* v8 ignore start -- rare typing module types @preserve */
330
+ case "Iterable":
331
+ return typeArgs.length > 0 ? `Iterable<${first}>` : "Iterable<unknown>";
332
+ case "Iterator":
333
+ return typeArgs.length > 0 ? `Iterator<${first}>` : "Iterator<unknown>";
334
+ case "Generator":
335
+ return typeArgs.length > 0 ? `Generator<${typeArgs.join(", ")}>` : "Generator<unknown>";
336
+ case "AsyncGenerator":
337
+ return typeArgs.length > 0 ? `AsyncGenerator<${typeArgs.join(", ")}>` : "AsyncGenerator<unknown>";
338
+ case "Awaitable":
339
+ return typeArgs.length > 0 ? `Promise<${first}>` : "Promise<unknown>";
340
+ case "Coroutine":
341
+ return typeArgs.length > 0 ? `Promise<${last}>` : "Promise<unknown>";
342
+ case "Type":
343
+ return typeArgs.length > 0 ? `new (...args: unknown[]) => ${first}` : "new (...args: unknown[]) => unknown";
344
+ /* v8 ignore stop */
345
+ /* v8 ignore start -- Literal type edge cases @preserve */
346
+ case "Literal": {
347
+ const literalValues = typeArgs.map((arg) => {
348
+ if (/^-?\d+(\.\d+)?$/.test(arg)) {
349
+ return arg;
350
+ }
351
+ if (arg.startsWith('"') || arg.startsWith("'")) {
352
+ return arg;
353
+ }
354
+ return `"${arg}"`;
355
+ });
356
+ return literalValues.join(" | ");
357
+ }
358
+ /* v8 ignore stop */
359
+ /* v8 ignore next 3 -- generic fallback for custom types @preserve */
360
+ default:
361
+ return typeArgs.length > 0 ? `${baseName}<${typeArgs.join(", ")}>` : baseName;
362
+ }
363
+ }
364
+ /* v8 ignore start -- rare type annotation patterns @preserve */
365
+ case "BinaryExpression": {
366
+ const children = getChildren(node);
367
+ const left = children[0];
368
+ const op = children[1];
369
+ const right = children[2];
370
+ if (op && getNodeText(op, ctx.source) === "|" && left && right) {
371
+ const leftType = transformPythonType(left, ctx);
372
+ const rightType = transformPythonType(right, ctx);
373
+ return `${leftType} | ${rightType}`;
374
+ }
375
+ return getNodeText(node, ctx.source);
376
+ }
377
+ case "String": {
378
+ const text = getNodeText(node, ctx.source);
379
+ return text.slice(1, -1);
380
+ }
381
+ case "TypeDef": {
382
+ const children = getChildren(node);
383
+ const typeNode = children.find((c) => c.name !== ":");
384
+ if (typeNode) {
385
+ return transformPythonType(typeNode, ctx);
386
+ }
387
+ return "unknown";
388
+ }
389
+ /* v8 ignore stop */
390
+ /* v8 ignore next 2 -- fallback for unhandled type nodes @preserve */
391
+ default:
392
+ return getNodeText(node, ctx.source);
393
+ }
394
+ }
395
+ function extractTypeAnnotation(typeDef, ctx) {
396
+ if (!typeDef || typeDef.name !== "TypeDef") return null;
397
+ const children = getChildren(typeDef);
398
+ const typeNode = children.find((c) => c.name !== ":" && c.name !== "->");
399
+ if (typeNode) {
400
+ return transformPythonType(typeNode, ctx);
401
+ }
402
+ return null;
403
+ }
404
+ function extractTypeModifiers(typeDef, ctx) {
405
+ const result = { isFinal: false, isClassVar: false };
406
+ if (!typeDef || typeDef.name !== "TypeDef") return result;
407
+ const children = getChildren(typeDef);
408
+ const typeNode = children.find((c) => c.name !== ":" && c.name !== "->");
409
+ if (!typeNode) return result;
410
+ if (typeNode.name === "MemberExpression") {
411
+ const typeChildren = getChildren(typeNode);
412
+ const baseType = typeChildren[0];
413
+ if (baseType) {
414
+ const baseName = getNodeText(baseType, ctx.source);
415
+ if (baseName === "Final") {
416
+ result.isFinal = true;
417
+ } else if (baseName === "ClassVar") {
418
+ result.isClassVar = true;
419
+ }
420
+ }
421
+ } else if (typeNode.name === "VariableName") {
422
+ const typeName = getNodeText(typeNode, ctx.source);
423
+ if (typeName === "Final") {
424
+ result.isFinal = true;
425
+ } else if (typeName === "ClassVar") {
426
+ result.isClassVar = true;
427
+ }
428
+ }
429
+ return result;
430
+ }
431
+ function transformCallableType(rawTypeArgNodes, ctx) {
432
+ if (rawTypeArgNodes.length < 2) {
433
+ return "(...args: unknown[]) => unknown";
434
+ }
435
+ const paramListNode = rawTypeArgNodes[0];
436
+ const returnTypeNode = rawTypeArgNodes[rawTypeArgNodes.length - 1];
437
+ let paramTypes = [];
438
+ if (paramListNode) {
439
+ const paramListChildren = getChildren(paramListNode);
440
+ const innerTypes = paramListChildren.filter(
441
+ (c) => c.name !== "[" && c.name !== "]" && c.name !== ","
442
+ );
443
+ if (innerTypes.length > 0) {
444
+ paramTypes = innerTypes.map((c) => transformPythonType(c, ctx));
445
+ }
446
+ }
447
+ const returnType = returnTypeNode ? transformPythonType(returnTypeNode, ctx) : "unknown";
448
+ if (paramListNode) {
449
+ const paramListChildren = getChildren(paramListNode);
450
+ const hasBrackets = paramListChildren.some((c) => c.name === "[" || c.name === "]");
451
+ if (hasBrackets) {
452
+ const params = paramTypes.map((t, i) => `arg${String(i)}: ${t}`).join(", ");
453
+ return `(${params}) => ${returnType}`;
454
+ }
455
+ }
456
+ return `(...args: unknown[]) => ${returnType}`;
457
+ }
458
+ function isDocstringNode(node, ctx) {
459
+ if (node.name !== "ExpressionStatement") return false;
460
+ const children = getChildren(node);
461
+ const firstChild = children[0];
462
+ if (!firstChild || firstChild.name !== "String") return false;
463
+ const text = getNodeText(firstChild, ctx.source);
464
+ return text.startsWith('"""') || text.startsWith("'''");
465
+ }
466
+ function extractDocstringContent(node, ctx) {
467
+ const children = getChildren(node);
468
+ const stringNode = children[0];
469
+ if (!stringNode) return "";
470
+ const text = getNodeText(stringNode, ctx.source);
471
+ let content = text;
472
+ if (content.startsWith('"""')) {
473
+ content = content.slice(3, -3);
474
+ } else if (content.startsWith("'''")) {
475
+ content = content.slice(3, -3);
476
+ }
477
+ content = content.replace(/\r\n/g, "\n").trim();
478
+ return content;
479
+ }
480
+ function parseDocstring(content) {
481
+ const result = {
482
+ description: "",
483
+ params: [],
484
+ returns: null,
485
+ throws: []
486
+ };
487
+ const lines = content.split("\n");
488
+ let currentSection = "description";
489
+ const descriptionLines = [];
490
+ let currentParamName = "";
491
+ let currentParamDesc = [];
492
+ let currentThrowsType = "";
493
+ let currentThrowsDesc = [];
494
+ const returnsLines = [];
495
+ const flushParam = () => {
496
+ if (currentParamName) {
497
+ result.params.push({
498
+ name: currentParamName,
499
+ description: currentParamDesc.join(" ").trim()
500
+ });
501
+ currentParamName = "";
502
+ currentParamDesc = [];
503
+ }
504
+ };
505
+ const flushThrows = () => {
506
+ if (currentThrowsType || currentThrowsDesc.length > 0) {
507
+ result.throws.push({
508
+ type: currentThrowsType || "Error",
509
+ description: currentThrowsDesc.join(" ").trim()
510
+ });
511
+ currentThrowsType = "";
512
+ currentThrowsDesc = [];
513
+ }
514
+ };
515
+ for (const line of lines) {
516
+ const trimmed = line.trim();
517
+ if (/^(Args|Arguments|Parameters):$/i.test(trimmed)) {
518
+ currentSection = "params";
519
+ continue;
520
+ }
521
+ if (/^(Returns?|Yields?):$/i.test(trimmed)) {
522
+ flushParam();
523
+ currentSection = "returns";
524
+ continue;
525
+ }
526
+ if (/^(Raises?|Throws?|Exceptions?):$/i.test(trimmed)) {
527
+ flushParam();
528
+ currentSection = "throws";
529
+ continue;
530
+ }
531
+ if (/^[A-Z][a-z]+:$/.test(trimmed)) {
532
+ continue;
533
+ }
534
+ switch (currentSection) {
535
+ case "description":
536
+ descriptionLines.push(trimmed);
537
+ break;
538
+ case "params": {
539
+ const googleMatch = trimmed.match(/^(\w+)\s*(?:\([^)]*\))?\s*:\s*(.*)$/);
540
+ if (googleMatch) {
541
+ flushParam();
542
+ currentParamName = googleMatch[1] ?? "";
543
+ const desc = googleMatch[2] ?? "";
544
+ if (desc) currentParamDesc.push(desc);
545
+ } else if (currentParamName && trimmed) {
546
+ currentParamDesc.push(trimmed);
547
+ }
548
+ break;
549
+ }
550
+ case "returns": {
551
+ const stripped = trimmed.replace(/^(?:\([^)]*\)|[^:]+):\s*/, "");
552
+ if (stripped || trimmed) {
553
+ returnsLines.push(stripped || trimmed);
554
+ }
555
+ break;
556
+ }
557
+ case "throws": {
558
+ const throwsMatch = trimmed.match(/^(\w+)\s*:\s*(.*)$/);
559
+ if (throwsMatch) {
560
+ flushThrows();
561
+ currentThrowsType = throwsMatch[1] ?? "Error";
562
+ const desc = throwsMatch[2] ?? "";
563
+ if (desc) currentThrowsDesc.push(desc);
564
+ } else if (trimmed) {
565
+ currentThrowsDesc.push(trimmed);
566
+ }
567
+ break;
568
+ }
569
+ }
570
+ }
571
+ flushParam();
572
+ flushThrows();
573
+ result.description = descriptionLines.join("\n").trim();
574
+ if (returnsLines.length > 0) {
575
+ result.returns = returnsLines.join(" ").trim();
576
+ }
577
+ return result;
578
+ }
579
+ function toJSDoc(parsed, indent) {
580
+ const lines = [];
581
+ lines.push(`${indent}/**`);
582
+ if (parsed.description) {
583
+ const descLines = parsed.description.split("\n");
584
+ for (const line of descLines) {
585
+ if (line.trim()) {
586
+ lines.push(`${indent} * ${line}`);
587
+ } else {
588
+ lines.push(`${indent} *`);
589
+ }
590
+ }
591
+ }
592
+ if (parsed.description && (parsed.params.length > 0 || parsed.returns || parsed.throws.length > 0)) {
593
+ lines.push(`${indent} *`);
594
+ }
595
+ for (const param of parsed.params) {
596
+ if (param.description) {
597
+ lines.push(`${indent} * @param ${param.name} - ${param.description}`);
598
+ } else {
599
+ lines.push(`${indent} * @param ${param.name}`);
600
+ }
601
+ }
602
+ if (parsed.returns) {
603
+ lines.push(`${indent} * @returns ${parsed.returns}`);
604
+ }
605
+ for (const t of parsed.throws) {
606
+ lines.push(`${indent} * @throws {${t.type}} ${t.description}`);
607
+ }
608
+ lines.push(`${indent} */`);
609
+ return lines.join("\n");
610
+ }
611
+ function extractDocstringFromBody(bodyNode, ctx, indent) {
612
+ const children = getChildren(bodyNode);
613
+ const statements = children.filter((c) => c.name !== ":");
614
+ const firstStatement = statements[0];
615
+ if (firstStatement && isDocstringNode(firstStatement, ctx)) {
616
+ const content = extractDocstringContent(firstStatement, ctx);
617
+ const parsed = parseDocstring(content);
618
+ const jsdoc = toJSDoc(parsed, indent);
619
+ return { jsdoc, skipFirstStatement: true };
620
+ }
621
+ return { jsdoc: null, skipFirstStatement: false };
622
+ }
623
+ function transform(input) {
624
+ const parseResult = typeof input === "string" ? parse(input) : input;
625
+ const ctx = createContext(parseResult.source);
626
+ const code = transformNode(parseResult.tree.topNode, ctx);
627
+ return {
628
+ code,
629
+ usesRuntime: ctx.usesRuntime
630
+ };
631
+ }
632
+ function transformNode(node, ctx) {
633
+ switch (node.name) {
634
+ case "Script":
635
+ return transformScript(node, ctx);
636
+ case "ExpressionStatement":
637
+ return transformExpressionStatement(node, ctx);
638
+ case "AssignStatement":
639
+ return transformAssignStatement(node, ctx);
640
+ case "BinaryExpression":
641
+ return transformBinaryExpression(node, ctx);
642
+ case "UnaryExpression":
643
+ return transformUnaryExpression(node, ctx);
644
+ case "ParenthesizedExpression":
645
+ return transformParenthesizedExpression(node, ctx);
646
+ case "NamedExpression":
647
+ return transformNamedExpression(node, ctx);
648
+ case "ConditionalExpression":
649
+ return transformConditionalExpression(node, ctx);
650
+ case "Number":
651
+ return transformNumber(node, ctx);
652
+ case "String":
653
+ return transformString(node, ctx);
654
+ case "FormatString":
655
+ return transformFormatString(node, ctx);
656
+ case "Boolean":
657
+ return transformBoolean(node, ctx);
658
+ case "None":
659
+ return "null";
660
+ case "VariableName":
661
+ return getNodeText(node, ctx.source);
662
+ case "CallExpression":
663
+ return transformCallExpression(node, ctx);
664
+ case "MemberExpression":
665
+ return transformMemberExpression(node, ctx);
666
+ case "ArrayExpression":
667
+ return transformArrayExpression(node, ctx);
668
+ case "ArrayComprehensionExpression":
669
+ return transformArrayComprehension(node, ctx);
670
+ case "DictionaryExpression":
671
+ return transformDictionaryExpression(node, ctx);
672
+ case "DictionaryComprehensionExpression":
673
+ return transformDictComprehension(node, ctx);
674
+ case "SetExpression":
675
+ return transformSetExpression(node, ctx);
676
+ case "SetComprehensionExpression":
677
+ return transformSetComprehension(node, ctx);
678
+ case "ComprehensionExpression":
679
+ return transformGeneratorExpression(node, ctx);
680
+ case "TupleExpression":
681
+ return transformTupleExpression(node, ctx);
682
+ case "IfStatement":
683
+ return transformIfStatement(node, ctx);
684
+ case "WhileStatement":
685
+ return transformWhileStatement(node, ctx);
686
+ case "ForStatement":
687
+ return transformForStatement(node, ctx);
688
+ case "PassStatement":
689
+ return "";
690
+ case "BreakStatement":
691
+ return "break";
692
+ case "ContinueStatement":
693
+ return "continue";
694
+ case "ReturnStatement":
695
+ return transformReturnStatement(node, ctx);
696
+ case "FunctionDefinition":
697
+ return transformFunctionDefinition(node, ctx);
698
+ case "ClassDefinition":
699
+ return transformClassDefinition(node, ctx);
700
+ case "DecoratedStatement":
701
+ return transformDecoratedStatement(node, ctx);
702
+ case "LambdaExpression":
703
+ return transformLambdaExpression(node, ctx);
704
+ case "Comment":
705
+ return transformComment(node, ctx);
706
+ case "TryStatement":
707
+ return transformTryStatement(node, ctx);
708
+ case "RaiseStatement":
709
+ return transformRaiseStatement(node, ctx);
710
+ case "ImportStatement":
711
+ return transformImportStatement(node, ctx);
712
+ case "AwaitExpression":
713
+ return transformAwaitExpression(node, ctx);
714
+ case "WithStatement":
715
+ return transformWithStatement(node, ctx);
716
+ case "MatchStatement":
717
+ return transformMatchStatement(node, ctx);
718
+ case "ScopeStatement":
719
+ return transformScopeStatement(node, ctx);
720
+ case "DeleteStatement":
721
+ return transformDeleteStatement(node, ctx);
722
+ case "AssertStatement":
723
+ return transformAssertStatement(node, ctx);
724
+ case "YieldStatement":
725
+ return transformYieldStatement(node, ctx);
726
+ /* v8 ignore next 2 -- fallback for unknown AST nodes @preserve */
727
+ default:
728
+ return getNodeText(node, ctx.source);
729
+ }
730
+ }
731
+ function transformScript(node, ctx) {
732
+ const children = getChildren(node);
733
+ const statements = children.filter((child) => child.name !== "Comment" || getNodeText(child, ctx.source).trim() !== "").map((child) => {
734
+ const transformed = transformNode(child, ctx);
735
+ if (transformed === "") {
736
+ return "";
737
+ }
738
+ if (child.name === "ExpressionStatement" || child.name === "AssignStatement" || child.name === "PassStatement" || child.name === "BreakStatement" || child.name === "ContinueStatement" || child.name === "ReturnStatement" || child.name === "RaiseStatement") {
739
+ return transformed + ";";
740
+ }
741
+ return transformed;
742
+ }).filter((s) => s.trim() !== "");
743
+ return statements.join("\n");
744
+ }
745
+ function transformExpressionStatement(node, ctx) {
746
+ const children = getChildren(node);
747
+ if (children.length === 0) return "";
748
+ const firstChild = children[0];
749
+ if (!firstChild) return "";
750
+ return transformNode(firstChild, ctx);
751
+ }
752
+ function transformAssignStatement(node, ctx) {
753
+ const children = getChildren(node);
754
+ if (children.length < 3) return getNodeText(node, ctx.source);
755
+ const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
756
+ if (assignOpIndex === -1) return getNodeText(node, ctx.source);
757
+ const typeDef = children.slice(0, assignOpIndex).find((c) => c.name === "TypeDef");
758
+ const targets = children.slice(0, assignOpIndex).filter((c) => c.name !== "," && c.name !== "TypeDef");
759
+ const values = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
760
+ if (targets.length === 0 || values.length === 0) {
761
+ return getNodeText(node, ctx.source);
762
+ }
763
+ if (values.length === 1 && values[0]?.name === "CallExpression") {
764
+ const callChildren = getChildren(values[0]);
765
+ const funcNode = callChildren.find((c) => c.name === "VariableName");
766
+ if (funcNode && getNodeText(funcNode, ctx.source) === "TypeVar") {
767
+ return "";
768
+ }
769
+ }
770
+ if (typeDef && targets.length === 1 && values.length === 1) {
771
+ const typeDefText = getNodeText(typeDef, ctx.source);
772
+ const target = targets[0];
773
+ const value = values[0];
774
+ if (typeDefText.includes("TypeAlias") && target && value) {
775
+ const aliasName = getNodeText(target, ctx.source);
776
+ const aliasType = transformPythonType(value, ctx);
777
+ return `type ${aliasName} = ${aliasType}`;
778
+ }
779
+ }
780
+ if (targets.length === 1) {
781
+ const target = targets[0];
782
+ if (!target) return getNodeText(node, ctx.source);
783
+ if (target.name === "MemberExpression" && isSliceExpression(target)) {
784
+ return transformSliceAssignment(target, values, ctx);
785
+ }
786
+ const targetCode = transformNode(target, ctx);
787
+ const tsType = extractTypeAnnotation(typeDef, ctx);
788
+ const typeAnnotation = tsType ? `: ${tsType}` : "";
789
+ const modifiers = extractTypeModifiers(typeDef, ctx);
790
+ let needsDeclaration = false;
791
+ if (target.name === "VariableName") {
792
+ const varName = getNodeText(target, ctx.source);
793
+ if (!isVariableDeclared(ctx, varName)) {
794
+ needsDeclaration = true;
795
+ declareVariable(ctx, varName);
796
+ }
797
+ }
798
+ const declarationKeyword = modifiers.isFinal ? "const" : "let";
799
+ if (values.length === 1) {
800
+ const value = values[0];
801
+ if (!value) return getNodeText(node, ctx.source);
802
+ const valueCode = transformNode(value, ctx);
803
+ if (needsDeclaration) {
804
+ return `${declarationKeyword} ${targetCode}${typeAnnotation} = ${valueCode}`;
805
+ }
806
+ return `${targetCode} = ${valueCode}`;
807
+ } else {
808
+ const valuesCodes = values.map((v) => transformNode(v, ctx));
809
+ if (needsDeclaration) {
810
+ return `${declarationKeyword} ${targetCode}${typeAnnotation} = [${valuesCodes.join(", ")}]`;
811
+ }
812
+ return `${targetCode} = [${valuesCodes.join(", ")}]`;
813
+ }
814
+ }
815
+ const targetCodes = targets.map((t) => transformAssignTarget(t, ctx));
816
+ const targetPattern = `[${targetCodes.join(", ")}]`;
817
+ const varNames = extractVariableNames(targets, ctx.source);
818
+ const allDeclaredAtAccessibleScope = varNames.every((v) => isVariableDeclared(ctx, v));
819
+ if (!allDeclaredAtAccessibleScope) {
820
+ varNames.forEach((v) => {
821
+ declareVariable(ctx, v);
822
+ });
823
+ }
824
+ if (values.length === 1) {
825
+ const value = values[0];
826
+ if (!value) return getNodeText(node, ctx.source);
827
+ const valueCode = transformNode(value, ctx);
828
+ return allDeclaredAtAccessibleScope ? `${targetPattern} = ${valueCode}` : `let ${targetPattern} = ${valueCode}`;
829
+ } else {
830
+ const valuesCodes = values.map((v) => transformNode(v, ctx));
831
+ return allDeclaredAtAccessibleScope ? `${targetPattern} = [${valuesCodes.join(", ")}]` : `let ${targetPattern} = [${valuesCodes.join(", ")}]`;
832
+ }
833
+ }
834
+ function extractVariableNames(nodes, source) {
835
+ const names = [];
836
+ for (const node of nodes) {
837
+ if (node.name === "VariableName") {
838
+ names.push(getNodeText(node, source));
839
+ } else if (node.name === "TupleExpression") {
840
+ const children = getChildren(node);
841
+ names.push(
842
+ ...extractVariableNames(
843
+ children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ","),
844
+ source
845
+ )
846
+ );
847
+ }
848
+ }
849
+ return names;
850
+ }
851
+ function isSliceExpression(node) {
852
+ const children = getChildren(node);
853
+ return children.some((c) => c.name === ":");
854
+ }
855
+ function transformSliceAssignment(target, values, ctx) {
856
+ const children = getChildren(target);
857
+ const obj = children[0];
858
+ if (!obj) return `/* slice assignment error */`;
859
+ const objCode = transformNode(obj, ctx);
860
+ const bracketStart = children.findIndex((c) => c.name === "[");
861
+ const bracketEnd = children.findIndex((c) => c.name === "]");
862
+ if (bracketStart === -1 || bracketEnd === -1) return `/* slice assignment error */`;
863
+ const sliceParts = children.slice(bracketStart + 1, bracketEnd);
864
+ let start;
865
+ let end;
866
+ let step;
867
+ const colonIndices = [];
868
+ for (let i = 0; i < sliceParts.length; i++) {
869
+ if (sliceParts[i]?.name === ":") {
870
+ colonIndices.push(i);
871
+ }
872
+ }
873
+ if (colonIndices.length >= 1) {
874
+ const beforeFirst = sliceParts.slice(0, colonIndices[0]);
875
+ if (beforeFirst.length > 0 && beforeFirst[0]?.name !== ":") {
876
+ start = beforeFirst.map((n) => transformNode(n, ctx)).join("");
877
+ }
878
+ const firstColon = colonIndices[0] ?? 0;
879
+ const secondColon = colonIndices[1];
880
+ if (colonIndices.length === 1) {
881
+ const afterFirst = sliceParts.slice(firstColon + 1);
882
+ if (afterFirst.length > 0) {
883
+ end = afterFirst.map((n) => transformNode(n, ctx)).join("");
884
+ }
885
+ } else if (secondColon !== void 0) {
886
+ const betweenColons = sliceParts.slice(firstColon + 1, secondColon);
887
+ if (betweenColons.length > 0) {
888
+ end = betweenColons.map((n) => transformNode(n, ctx)).join("");
889
+ }
890
+ const afterSecond = sliceParts.slice(secondColon + 1);
891
+ if (afterSecond.length > 0) {
892
+ step = afterSecond.map((n) => transformNode(n, ctx)).join("");
893
+ }
894
+ }
895
+ }
896
+ const firstValue = values[0];
897
+ const valuesCode = values.length === 1 && firstValue ? transformNode(firstValue, ctx) : `[${values.map((v) => transformNode(v, ctx)).join(", ")}]`;
898
+ ctx.usesRuntime.add("list.sliceAssign");
899
+ return `list.sliceAssign(${objCode}, ${start ?? "undefined"}, ${end ?? "undefined"}, ${step ?? "undefined"}, ${valuesCode})`;
900
+ }
901
+ function transformAssignTarget(node, ctx) {
902
+ if (node.name === "VariableName") {
903
+ return getNodeText(node, ctx.source);
904
+ } else if (node.name === "TupleExpression") {
905
+ const children = getChildren(node);
906
+ const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
907
+ return "[" + elements.map((e) => transformAssignTarget(e, ctx)).join(", ") + "]";
908
+ }
909
+ return transformNode(node, ctx);
910
+ }
911
+ function transformBinaryExpression(node, ctx) {
912
+ const children = getChildren(node);
913
+ if (children.length < 3) return getNodeText(node, ctx.source);
914
+ const left = children[0];
915
+ const op = children[1];
916
+ const right = children[2];
917
+ if (!left || !op || !right) return getNodeText(node, ctx.source);
918
+ const opText = getNodeText(op, ctx.source);
919
+ if (isComparisonOperator(opText) && isChainedComparison(left)) {
920
+ const leftComparison = transformNode(left, ctx);
921
+ const middleValue = extractRightOperand(left, ctx);
922
+ const rightCode2 = transformNode(right, ctx);
923
+ return `(${leftComparison} && (${middleValue} ${opText} ${rightCode2}))`;
924
+ }
925
+ const leftCode = transformNode(left, ctx);
926
+ const rightCode = transformNode(right, ctx);
927
+ switch (opText) {
928
+ case "//":
929
+ ctx.usesRuntime.add("floorDiv");
930
+ return `floorDiv(${leftCode}, ${rightCode})`;
931
+ case "**":
932
+ ctx.usesRuntime.add("pow");
933
+ return `pow(${leftCode}, ${rightCode})`;
934
+ case "%":
935
+ if (left.name === "String" || left.name === "FormatString") {
936
+ ctx.usesRuntime.add("sprintf");
937
+ return `sprintf(${leftCode}, ${rightCode})`;
938
+ }
939
+ ctx.usesRuntime.add("mod");
940
+ return `mod(${leftCode}, ${rightCode})`;
941
+ case "and":
942
+ return `(${leftCode} && ${rightCode})`;
943
+ case "or":
944
+ return `(${leftCode} || ${rightCode})`;
945
+ case "in":
946
+ ctx.usesRuntime.add("contains");
947
+ return `contains(${leftCode}, ${rightCode})`;
948
+ case "is":
949
+ return `(${leftCode} === ${rightCode})`;
950
+ case "+":
951
+ if (isArrayLiteral(left) && isArrayLiteral(right)) {
952
+ return `[...${leftCode}, ...${rightCode}]`;
953
+ }
954
+ return `(${leftCode} + ${rightCode})`;
955
+ case "*":
956
+ if (isStringOrArrayLiteral(left) && isNumberLiteral(right)) {
957
+ ctx.usesRuntime.add("repeatValue");
958
+ return `repeatValue(${leftCode}, ${rightCode})`;
959
+ }
960
+ if (isNumberLiteral(left) && isStringOrArrayLiteral(right)) {
961
+ ctx.usesRuntime.add("repeatValue");
962
+ return `repeatValue(${rightCode}, ${leftCode})`;
963
+ }
964
+ return `(${leftCode} * ${rightCode})`;
965
+ /* v8 ignore next 2 -- pass-through for standard operators @preserve */
966
+ default:
967
+ return `(${leftCode} ${opText} ${rightCode})`;
968
+ }
969
+ }
970
+ function isArrayLiteral(node) {
971
+ return node.name === "ArrayExpression";
972
+ }
973
+ function isStringOrArrayLiteral(node) {
974
+ return node.name === "String" || node.name === "ArrayExpression";
975
+ }
976
+ function isNumberLiteral(node) {
977
+ return node.name === "Number";
978
+ }
979
+ function isComparisonOperator(op) {
980
+ return ["<", ">", "<=", ">=", "==", "!="].includes(op);
981
+ }
982
+ function isChainedComparison(node) {
983
+ if (node.name !== "BinaryExpression") return false;
984
+ const children = getChildren(node);
985
+ const op = children[1];
986
+ if (!op || op.name !== "CompareOp") return false;
987
+ return true;
988
+ }
989
+ function extractRightOperand(node, ctx) {
990
+ const children = getChildren(node);
991
+ const right = children[2];
992
+ if (!right) return "";
993
+ return transformNode(right, ctx);
994
+ }
995
+ function transformUnaryExpression(node, ctx) {
996
+ const children = getChildren(node);
997
+ if (children.length < 2) return getNodeText(node, ctx.source);
998
+ const op = children[0];
999
+ const operand = children[1];
1000
+ if (!op || !operand) return getNodeText(node, ctx.source);
1001
+ const opText = getNodeText(op, ctx.source);
1002
+ const operandCode = transformNode(operand, ctx);
1003
+ switch (opText) {
1004
+ case "not":
1005
+ return `(!${operandCode})`;
1006
+ /* v8 ignore next 2 -- pass-through for unary operators like - and + @preserve */
1007
+ default:
1008
+ return `(${opText}${operandCode})`;
1009
+ }
1010
+ }
1011
+ function transformParenthesizedExpression(node, ctx) {
1012
+ const children = getChildren(node);
1013
+ const inner = children.find((c) => c.name !== "(" && c.name !== ")");
1014
+ if (!inner) return "()";
1015
+ return `(${transformNode(inner, ctx)})`;
1016
+ }
1017
+ function transformNamedExpression(node, ctx) {
1018
+ const children = getChildren(node);
1019
+ const varName = children.find((c) => c.name === "VariableName");
1020
+ const value = children.find((c) => c.name !== "VariableName" && c.name !== "AssignOp");
1021
+ if (!varName || !value) {
1022
+ return getNodeText(node, ctx.source);
1023
+ }
1024
+ const name = getNodeText(varName, ctx.source);
1025
+ const valueCode = transformNode(value, ctx);
1026
+ return `${name} = ${valueCode}`;
1027
+ }
1028
+ function transformConditionalExpression(node, ctx) {
1029
+ const children = getChildren(node);
1030
+ const exprs = children.filter((c) => c.name !== "if" && c.name !== "else" && c.name !== "Keyword");
1031
+ if (exprs.length >= 3) {
1032
+ const trueExpr = exprs[0];
1033
+ const condition = exprs[1];
1034
+ const falseExpr = exprs[2];
1035
+ if (trueExpr && condition && falseExpr) {
1036
+ const condCode = transformNode(condition, ctx);
1037
+ const trueCode = transformNode(trueExpr, ctx);
1038
+ const falseCode = transformNode(falseExpr, ctx);
1039
+ return `(${condCode} ? ${trueCode} : ${falseCode})`;
1040
+ }
1041
+ }
1042
+ return getNodeText(node, ctx.source);
1043
+ }
1044
+ function transformNumber(node, ctx) {
1045
+ const text = getNodeText(node, ctx.source);
1046
+ return text.replace(/_/g, "");
1047
+ }
1048
+ function transformString(node, ctx) {
1049
+ const text = getNodeText(node, ctx.source);
1050
+ if (text.startsWith('r"') || text.startsWith("r'")) {
1051
+ return text.slice(1);
1052
+ }
1053
+ if (text.startsWith('R"') || text.startsWith("R'")) {
1054
+ return text.slice(1);
1055
+ }
1056
+ if (text.startsWith('"""') || text.startsWith("'''")) {
1057
+ const content = text.slice(3, -3);
1058
+ return "`" + content.replace(/`/g, "\\`") + "`";
1059
+ }
1060
+ return text;
1061
+ }
1062
+ function transformFormatString(node, ctx) {
1063
+ const text = getNodeText(node, ctx.source);
1064
+ const children = getChildren(node);
1065
+ const replacements = children.filter((c) => c.name === "FormatReplacement");
1066
+ if (replacements.length === 0) {
1067
+ let content;
1068
+ if (text.startsWith('f"""') || text.startsWith("f'''")) {
1069
+ content = text.slice(4, -3);
1070
+ } else {
1071
+ content = text.slice(2, -1);
1072
+ }
1073
+ content = content.replace(/\{\{/g, "{").replace(/\}\}/g, "}");
1074
+ content = content.replace(/`/g, "\\`");
1075
+ return "`" + content + "`";
1076
+ }
1077
+ let result = "`";
1078
+ let pos = text.startsWith('f"""') || text.startsWith("f'''") ? 4 : 2;
1079
+ for (const replacement of replacements) {
1080
+ const staticText = text.slice(pos, replacement.from - node.from);
1081
+ result += staticText.replace(/\{\{/g, "{").replace(/\}\}/g, "}").replace(/`/g, "\\`");
1082
+ const replChildren = getChildren(replacement);
1083
+ let expr;
1084
+ let formatSpec;
1085
+ let conversion;
1086
+ for (const child of replChildren) {
1087
+ if (child.name === "{" || child.name === "}") continue;
1088
+ if (child.name === "FormatSpec") {
1089
+ formatSpec = getNodeText(child, ctx.source).slice(1);
1090
+ } else if (child.name === "FormatConversion") {
1091
+ conversion = getNodeText(child, ctx.source).slice(1);
1092
+ } else {
1093
+ expr = child;
1094
+ }
1095
+ }
1096
+ if (expr) {
1097
+ let exprCode = transformNode(expr, ctx);
1098
+ if (conversion === "r") {
1099
+ exprCode = `repr(${exprCode})`;
1100
+ ctx.usesRuntime.add("repr");
1101
+ } else if (conversion === "s") {
1102
+ exprCode = `str(${exprCode})`;
1103
+ ctx.usesRuntime.add("str");
1104
+ } else if (conversion === "a") {
1105
+ exprCode = `ascii(${exprCode})`;
1106
+ ctx.usesRuntime.add("ascii");
1107
+ }
1108
+ if (formatSpec) {
1109
+ ctx.usesRuntime.add("format");
1110
+ result += `\${format(${exprCode}, "${formatSpec}")}`;
1111
+ } else {
1112
+ result += `\${${exprCode}}`;
1113
+ }
1114
+ }
1115
+ pos = replacement.to - node.from;
1116
+ }
1117
+ const endPos = text.startsWith('f"""') || text.startsWith("f'''") ? text.length - 3 : text.length - 1;
1118
+ const remainingText = text.slice(pos, endPos);
1119
+ result += remainingText.replace(/\{\{/g, "{").replace(/\}\}/g, "}").replace(/`/g, "\\`");
1120
+ result += "`";
1121
+ return result;
1122
+ }
1123
+ function transformBoolean(node, ctx) {
1124
+ const text = getNodeText(node, ctx.source);
1125
+ return text === "True" ? "true" : "false";
1126
+ }
1127
+ function transformCallExpression(node, ctx) {
1128
+ const children = getChildren(node);
1129
+ const callee = children[0];
1130
+ const argList = children.find((c) => c.name === "ArgList");
1131
+ if (!callee) return getNodeText(node, ctx.source);
1132
+ const calleeName = getNodeText(callee, ctx.source);
1133
+ const args = argList ? transformArgList(argList, ctx) : "";
1134
+ if (callee.name === "MemberExpression") {
1135
+ const methodResult = transformMethodCall(callee, args, ctx);
1136
+ if (methodResult !== null) {
1137
+ return methodResult;
1138
+ }
1139
+ const moduleCallResult = transformModuleCall(calleeName, args, ctx);
1140
+ if (moduleCallResult !== null) {
1141
+ return moduleCallResult;
1142
+ }
1143
+ }
1144
+ if (callee.name === "VariableName" && ctx.definedClasses.has(calleeName)) {
1145
+ return `new ${calleeName}(${args})`;
1146
+ }
1147
+ switch (calleeName) {
1148
+ case "print":
1149
+ return `console.log(${args})`;
1150
+ case "len":
1151
+ ctx.usesRuntime.add("len");
1152
+ return `len(${args})`;
1153
+ case "range":
1154
+ ctx.usesRuntime.add("range");
1155
+ return `range(${args})`;
1156
+ case "int":
1157
+ ctx.usesRuntime.add("int");
1158
+ return `int(${args})`;
1159
+ case "float":
1160
+ ctx.usesRuntime.add("float");
1161
+ return `float(${args})`;
1162
+ case "str":
1163
+ ctx.usesRuntime.add("str");
1164
+ return `str(${args})`;
1165
+ case "bool":
1166
+ ctx.usesRuntime.add("bool");
1167
+ return `bool(${args})`;
1168
+ case "abs":
1169
+ ctx.usesRuntime.add("abs");
1170
+ return `abs(${args})`;
1171
+ case "min":
1172
+ ctx.usesRuntime.add("min");
1173
+ return `min(${args})`;
1174
+ case "max":
1175
+ ctx.usesRuntime.add("max");
1176
+ return `max(${args})`;
1177
+ case "sum":
1178
+ ctx.usesRuntime.add("sum");
1179
+ return `sum(${args})`;
1180
+ case "list":
1181
+ ctx.usesRuntime.add("list");
1182
+ return `list(${args})`;
1183
+ case "dict":
1184
+ ctx.usesRuntime.add("dict");
1185
+ return `dict(${args})`;
1186
+ case "set":
1187
+ ctx.usesRuntime.add("set");
1188
+ return `set(${args})`;
1189
+ case "tuple":
1190
+ ctx.usesRuntime.add("tuple");
1191
+ return `tuple(${args})`;
1192
+ case "enumerate":
1193
+ ctx.usesRuntime.add("enumerate");
1194
+ return `enumerate(${args})`;
1195
+ case "zip":
1196
+ ctx.usesRuntime.add("zip");
1197
+ return `zip(${args})`;
1198
+ case "sorted":
1199
+ ctx.usesRuntime.add("sorted");
1200
+ return `sorted(${args})`;
1201
+ case "reversed":
1202
+ ctx.usesRuntime.add("reversed");
1203
+ return `reversed(${args})`;
1204
+ case "isinstance":
1205
+ ctx.usesRuntime.add("isinstance");
1206
+ return `isinstance(${args})`;
1207
+ case "type":
1208
+ ctx.usesRuntime.add("type");
1209
+ return `type(${args})`;
1210
+ case "input":
1211
+ ctx.usesRuntime.add("input");
1212
+ return `input(${args})`;
1213
+ case "ord":
1214
+ ctx.usesRuntime.add("ord");
1215
+ return `ord(${args})`;
1216
+ case "chr":
1217
+ ctx.usesRuntime.add("chr");
1218
+ return `chr(${args})`;
1219
+ case "all":
1220
+ ctx.usesRuntime.add("all");
1221
+ return `all(${args})`;
1222
+ case "any":
1223
+ ctx.usesRuntime.add("any");
1224
+ return `any(${args})`;
1225
+ case "map":
1226
+ ctx.usesRuntime.add("map");
1227
+ return `map(${args})`;
1228
+ case "filter":
1229
+ ctx.usesRuntime.add("filter");
1230
+ return `filter(${args})`;
1231
+ case "repr":
1232
+ ctx.usesRuntime.add("repr");
1233
+ return `repr(${args})`;
1234
+ case "round":
1235
+ ctx.usesRuntime.add("round");
1236
+ return `round(${args})`;
1237
+ case "divmod":
1238
+ ctx.usesRuntime.add("divmod");
1239
+ return `divmod(${args})`;
1240
+ case "hex":
1241
+ ctx.usesRuntime.add("hex");
1242
+ return `hex(${args})`;
1243
+ case "oct":
1244
+ ctx.usesRuntime.add("oct");
1245
+ return `oct(${args})`;
1246
+ case "bin":
1247
+ ctx.usesRuntime.add("bin");
1248
+ return `bin(${args})`;
1249
+ // itertools functions
1250
+ case "chain":
1251
+ ctx.usesRuntime.add("itertools/chain");
1252
+ return `chain(${args})`;
1253
+ case "combinations":
1254
+ ctx.usesRuntime.add("itertools/combinations");
1255
+ return `combinations(${args})`;
1256
+ case "permutations":
1257
+ ctx.usesRuntime.add("itertools/permutations");
1258
+ return `permutations(${args})`;
1259
+ case "product":
1260
+ ctx.usesRuntime.add("itertools/product");
1261
+ return `product(${args})`;
1262
+ case "cycle":
1263
+ ctx.usesRuntime.add("itertools/cycle");
1264
+ return `cycle(${args})`;
1265
+ case "repeat":
1266
+ ctx.usesRuntime.add("itertools/repeat");
1267
+ return `repeat(${args})`;
1268
+ case "islice":
1269
+ ctx.usesRuntime.add("itertools/islice");
1270
+ return `islice(${args})`;
1271
+ case "takewhile":
1272
+ ctx.usesRuntime.add("itertools/takeWhile");
1273
+ return `takeWhile(${args})`;
1274
+ case "dropwhile":
1275
+ ctx.usesRuntime.add("itertools/dropWhile");
1276
+ return `dropWhile(${args})`;
1277
+ case "zip_longest":
1278
+ ctx.usesRuntime.add("itertools/zipLongest");
1279
+ return `zipLongest(${args})`;
1280
+ case "compress":
1281
+ ctx.usesRuntime.add("itertools/compress");
1282
+ return `compress(${args})`;
1283
+ case "filterfalse":
1284
+ ctx.usesRuntime.add("itertools/filterFalse");
1285
+ return `filterFalse(${args})`;
1286
+ case "accumulate":
1287
+ ctx.usesRuntime.add("itertools/accumulate");
1288
+ return `accumulate(${args})`;
1289
+ case "groupby":
1290
+ ctx.usesRuntime.add("itertools/groupby");
1291
+ return `groupby(${args})`;
1292
+ case "count":
1293
+ ctx.usesRuntime.add("itertools/count");
1294
+ return `count(${args})`;
1295
+ case "tee":
1296
+ ctx.usesRuntime.add("itertools/tee");
1297
+ return `tee(${args})`;
1298
+ case "pairwise":
1299
+ ctx.usesRuntime.add("itertools/pairwise");
1300
+ return `pairwise(${args})`;
1301
+ case "combinations_with_replacement":
1302
+ ctx.usesRuntime.add("itertools/combinationsWithReplacement");
1303
+ return `combinationsWithReplacement(${args})`;
1304
+ // collections classes/functions
1305
+ case "Counter":
1306
+ ctx.usesRuntime.add("collections/Counter");
1307
+ return `new Counter(${args})`;
1308
+ case "defaultdict":
1309
+ ctx.usesRuntime.add("collections/defaultdict");
1310
+ return `defaultdict(${args})`;
1311
+ case "deque":
1312
+ ctx.usesRuntime.add("collections/deque");
1313
+ return `new deque(${args})`;
1314
+ // functools functions
1315
+ case "partial":
1316
+ ctx.usesRuntime.add("functools/partial");
1317
+ return `partial(${args})`;
1318
+ case "reduce":
1319
+ ctx.usesRuntime.add("functools/reduce");
1320
+ return `reduce(${args})`;
1321
+ case "lru_cache":
1322
+ ctx.usesRuntime.add("functools/lruCache");
1323
+ return `lruCache(${args})`;
1324
+ case "cache":
1325
+ ctx.usesRuntime.add("functools/cache");
1326
+ return `cache(${args})`;
1327
+ case "wraps":
1328
+ ctx.usesRuntime.add("functools/wraps");
1329
+ return `wraps(${args})`;
1330
+ case "cmp_to_key":
1331
+ ctx.usesRuntime.add("functools/cmpToKey");
1332
+ return `cmpToKey(${args})`;
1333
+ case "total_ordering":
1334
+ ctx.usesRuntime.add("functools/totalOrdering");
1335
+ return `totalOrdering(${args})`;
1336
+ // json functions
1337
+ case "dumps":
1338
+ ctx.usesRuntime.add("json/dumps");
1339
+ return `dumps(${args})`;
1340
+ case "loads":
1341
+ ctx.usesRuntime.add("json/loads");
1342
+ return `loads(${args})`;
1343
+ case "dump":
1344
+ ctx.usesRuntime.add("json/dump");
1345
+ return `dump(${args})`;
1346
+ case "load":
1347
+ ctx.usesRuntime.add("json/load");
1348
+ return `load(${args})`;
1349
+ // datetime classes
1350
+ case "datetime":
1351
+ ctx.usesRuntime.add("datetime/datetime");
1352
+ return `new datetime(${args})`;
1353
+ case "date":
1354
+ ctx.usesRuntime.add("datetime/date");
1355
+ return `new date(${args})`;
1356
+ case "time":
1357
+ ctx.usesRuntime.add("datetime/time");
1358
+ return `new time(${args})`;
1359
+ case "timedelta":
1360
+ ctx.usesRuntime.add("datetime/timedelta");
1361
+ return `new timedelta(${args})`;
1362
+ // string module
1363
+ case "Template":
1364
+ ctx.usesRuntime.add("string/Template");
1365
+ return `new Template(${args})`;
1366
+ case "capwords":
1367
+ ctx.usesRuntime.add("string/capWords");
1368
+ return `capWords(${args})`;
1369
+ /* v8 ignore next 3 -- pass-through for user-defined functions @preserve */
1370
+ default:
1371
+ return `${transformNode(callee, ctx)}(${args})`;
1372
+ }
1373
+ }
1374
+ function transformModuleCall(calleeName, args, ctx) {
1375
+ const dotIndex = calleeName.indexOf(".");
1376
+ if (dotIndex === -1) return null;
1377
+ const moduleName = calleeName.slice(0, dotIndex);
1378
+ const funcName = calleeName.slice(dotIndex + 1);
1379
+ if (moduleName === "math") {
1380
+ const mathConstants = {
1381
+ pi: "pi",
1382
+ e: "e",
1383
+ tau: "tau",
1384
+ inf: "inf",
1385
+ nan: "nan"
1386
+ };
1387
+ if (funcName in mathConstants) {
1388
+ ctx.usesRuntime.add(`math/${funcName}`);
1389
+ return mathConstants[funcName];
1390
+ }
1391
+ ctx.usesRuntime.add(`math/${funcName}`);
1392
+ return `${funcName}(${args})`;
1393
+ }
1394
+ if (moduleName === "random") {
1395
+ const jsName = toJsName(funcName);
1396
+ ctx.usesRuntime.add(`random/${jsName}`);
1397
+ return `${jsName}(${args})`;
1398
+ }
1399
+ if (moduleName === "json") {
1400
+ ctx.usesRuntime.add(`json/${funcName}`);
1401
+ return `${funcName}(${args})`;
1402
+ }
1403
+ if (moduleName === "os") {
1404
+ if (funcName.startsWith("path.")) {
1405
+ const pathFuncName = funcName.slice(5);
1406
+ const jsPathFunc = toJsName(pathFuncName);
1407
+ ctx.usesRuntime.add("os/path");
1408
+ return `path.${jsPathFunc}(${args})`;
1409
+ }
1410
+ const jsName = toJsName(funcName);
1411
+ ctx.usesRuntime.add(`os/${jsName}`);
1412
+ return `${jsName}(${args})`;
1413
+ }
1414
+ if (moduleName === "datetime") {
1415
+ const jsName = toJsName(funcName);
1416
+ ctx.usesRuntime.add(`datetime/${jsName}`);
1417
+ if (["datetime", "date", "time", "timedelta"].includes(funcName)) {
1418
+ return `new ${jsName}(${args})`;
1419
+ }
1420
+ return `${jsName}(${args})`;
1421
+ }
1422
+ if (moduleName === "re") {
1423
+ const jsName = toJsName(funcName);
1424
+ ctx.usesRuntime.add(`re/${jsName}`);
1425
+ return `${jsName}(${args})`;
1426
+ }
1427
+ if (moduleName === "string") {
1428
+ const jsName = toJsName(funcName);
1429
+ ctx.usesRuntime.add(`string/${jsName}`);
1430
+ if (funcName === "Template") {
1431
+ return `new Template(${args})`;
1432
+ }
1433
+ return funcName.includes("(") ? `${jsName}(${args})` : jsName;
1434
+ }
1435
+ if (moduleName === "functools") {
1436
+ const jsName = toJsName(funcName);
1437
+ ctx.usesRuntime.add(`functools/${jsName}`);
1438
+ return `${jsName}(${args})`;
1439
+ }
1440
+ if (moduleName === "itertools") {
1441
+ const jsName = toJsName(funcName);
1442
+ ctx.usesRuntime.add(`itertools/${jsName}`);
1443
+ return `${jsName}(${args})`;
1444
+ }
1445
+ if (moduleName === "collections") {
1446
+ ctx.usesRuntime.add(`collections/${funcName}`);
1447
+ if (["Counter", "deque"].includes(funcName)) {
1448
+ return `new ${funcName}(${args})`;
1449
+ }
1450
+ return `${funcName}(${args})`;
1451
+ }
1452
+ return null;
1453
+ }
1454
+ function transformMethodCall(callee, args, ctx) {
1455
+ const children = getChildren(callee);
1456
+ if (children.length < 2) return null;
1457
+ const obj = children[0];
1458
+ const methodNode = children[children.length - 1];
1459
+ if (!obj || !methodNode) return null;
1460
+ if (obj.name === "MemberExpression") {
1461
+ return null;
1462
+ }
1463
+ const objCode = transformNode(obj, ctx);
1464
+ const methodName = getNodeText(methodNode, ctx.source);
1465
+ switch (methodName) {
1466
+ // String case conversion
1467
+ case "upper":
1468
+ return `${objCode}.toUpperCase()`;
1469
+ case "lower":
1470
+ return `${objCode}.toLowerCase()`;
1471
+ case "capitalize":
1472
+ ctx.usesRuntime.add("string");
1473
+ return `string.capitalize(${objCode})`;
1474
+ case "title":
1475
+ ctx.usesRuntime.add("string");
1476
+ return `string.title(${objCode})`;
1477
+ case "swapcase":
1478
+ ctx.usesRuntime.add("string");
1479
+ return `string.swapCase(${objCode})`;
1480
+ case "casefold":
1481
+ return `${objCode}.toLowerCase()`;
1482
+ // String whitespace
1483
+ case "strip":
1484
+ return args ? `${objCode}.split(${args}).join("")` : `${objCode}.trim()`;
1485
+ case "lstrip":
1486
+ return args ? `${objCode}.replace(new RegExp('^[' + ${args} + ']+'), '')` : `${objCode}.trimStart()`;
1487
+ case "rstrip":
1488
+ return args ? `${objCode}.replace(new RegExp('[' + ${args} + ']+$'), '')` : `${objCode}.trimEnd()`;
1489
+ // String search
1490
+ case "startswith":
1491
+ return `${objCode}.startsWith(${args})`;
1492
+ case "endswith":
1493
+ return `${objCode}.endsWith(${args})`;
1494
+ case "find":
1495
+ return `${objCode}.indexOf(${args})`;
1496
+ case "rfind":
1497
+ return `${objCode}.lastIndexOf(${args})`;
1498
+ case "index":
1499
+ ctx.usesRuntime.add("string");
1500
+ return `string.index(${objCode}, ${args})`;
1501
+ case "rindex":
1502
+ ctx.usesRuntime.add("string");
1503
+ return `string.rIndex(${objCode}, ${args})`;
1504
+ case "count":
1505
+ ctx.usesRuntime.add("string");
1506
+ return `string.count(${objCode}, ${args})`;
1507
+ // String testing
1508
+ case "isalpha":
1509
+ return `/^[a-zA-Z]+$/.test(${objCode})`;
1510
+ case "isdigit":
1511
+ return `/^[0-9]+$/.test(${objCode})`;
1512
+ case "isalnum":
1513
+ return `/^[a-zA-Z0-9]+$/.test(${objCode})`;
1514
+ case "isspace":
1515
+ return `/^\\s+$/.test(${objCode})`;
1516
+ case "isupper":
1517
+ return `(${objCode} === ${objCode}.toUpperCase() && ${objCode} !== ${objCode}.toLowerCase())`;
1518
+ case "islower":
1519
+ return `(${objCode} === ${objCode}.toLowerCase() && ${objCode} !== ${objCode}.toUpperCase())`;
1520
+ // String modification
1521
+ case "replace":
1522
+ ctx.usesRuntime.add("string");
1523
+ return `string.replace(${objCode}, ${args})`;
1524
+ case "zfill":
1525
+ ctx.usesRuntime.add("string");
1526
+ return `string.zFill(${objCode}, ${args})`;
1527
+ case "center":
1528
+ ctx.usesRuntime.add("string");
1529
+ return `string.center(${objCode}, ${args})`;
1530
+ case "ljust":
1531
+ return `${objCode}.padEnd(${args})`;
1532
+ case "rjust":
1533
+ return `${objCode}.padStart(${args})`;
1534
+ // String split/join - join is special: "sep".join(arr) -> arr.join("sep")
1535
+ case "join":
1536
+ return `(${args}).join(${objCode})`;
1537
+ case "split":
1538
+ return args ? `${objCode}.split(${args})` : `${objCode}.split(/\\s+/)`;
1539
+ case "rsplit":
1540
+ ctx.usesRuntime.add("string");
1541
+ return `string.rSplit(${objCode}, ${args})`;
1542
+ case "splitlines":
1543
+ return `${objCode}.split(/\\r?\\n/)`;
1544
+ case "partition":
1545
+ ctx.usesRuntime.add("string");
1546
+ return `string.partition(${objCode}, ${args})`;
1547
+ case "rpartition":
1548
+ ctx.usesRuntime.add("string");
1549
+ return `string.rPartition(${objCode}, ${args})`;
1550
+ // String format method
1551
+ case "format":
1552
+ ctx.usesRuntime.add("strFormat");
1553
+ return `strFormat(${objCode}, ${args})`;
1554
+ // List methods
1555
+ case "append":
1556
+ return `${objCode}.push(${args})`;
1557
+ case "extend":
1558
+ return `${objCode}.push(...${args})`;
1559
+ case "insert": {
1560
+ const insertArgs = args.split(",");
1561
+ const index = insertArgs[0] ?? "0";
1562
+ const value = insertArgs.slice(1).join(",");
1563
+ return `${objCode}.splice(${index}, 0, ${value})`;
1564
+ }
1565
+ case "remove":
1566
+ ctx.usesRuntime.add("list");
1567
+ return `list.remove(${objCode}, ${args})`;
1568
+ case "pop":
1569
+ if (!args) return `${objCode}.pop()`;
1570
+ if (args.trim() === "0") return `${objCode}.shift()`;
1571
+ return `${objCode}.splice(${args}, 1)[0]`;
1572
+ case "clear":
1573
+ return `${objCode}.length = 0`;
1574
+ case "copy":
1575
+ return `[...${objCode}]`;
1576
+ case "reverse":
1577
+ return `${objCode}.reverse()`;
1578
+ case "sort":
1579
+ ctx.usesRuntime.add("list");
1580
+ return args ? `list.sort(${objCode}, ${args})` : `${objCode}.sort()`;
1581
+ // Dict methods
1582
+ case "keys":
1583
+ return `Object.keys(${objCode})`;
1584
+ case "values":
1585
+ return `Object.values(${objCode})`;
1586
+ case "items":
1587
+ return `Object.entries(${objCode})`;
1588
+ case "get":
1589
+ ctx.usesRuntime.add("dict");
1590
+ return `dict.get(${objCode}, ${args})`;
1591
+ case "setdefault":
1592
+ ctx.usesRuntime.add("dict");
1593
+ return `dict.setdefault(${objCode}, ${args})`;
1594
+ case "update":
1595
+ return `Object.assign(${objCode}, ${args})`;
1596
+ case "fromkeys":
1597
+ ctx.usesRuntime.add("dict");
1598
+ return `dict.fromkeys(${args})`;
1599
+ // Set methods
1600
+ case "add":
1601
+ return `${objCode}.add(${args})`;
1602
+ case "discard":
1603
+ return `${objCode}.delete(${args})`;
1604
+ case "union":
1605
+ return `new Set([...${objCode}, ...${args}])`;
1606
+ case "intersection":
1607
+ ctx.usesRuntime.add("set");
1608
+ return `set.intersection(${objCode}, ${args})`;
1609
+ case "difference":
1610
+ ctx.usesRuntime.add("set");
1611
+ return `set.difference(${objCode}, ${args})`;
1612
+ case "symmetric_difference":
1613
+ ctx.usesRuntime.add("set");
1614
+ return `set.symmetricDifference(${objCode}, ${args})`;
1615
+ case "issubset":
1616
+ ctx.usesRuntime.add("set");
1617
+ return `set.issubset(${objCode}, ${args})`;
1618
+ case "issuperset":
1619
+ ctx.usesRuntime.add("set");
1620
+ return `set.issuperset(${objCode}, ${args})`;
1621
+ /* v8 ignore next 2 -- unknown method, let caller handle @preserve */
1622
+ default:
1623
+ return null;
1624
+ }
1625
+ }
1626
+ function transformArgList(node, ctx) {
1627
+ const children = getChildren(node);
1628
+ const items = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== "ArgList");
1629
+ const hasForKeyword = items.some(
1630
+ (c) => c.name === "for" || c.name === "Keyword" && getNodeText(c, ctx.source) === "for"
1631
+ );
1632
+ if (hasForKeyword) {
1633
+ const nonCommaItems = items.filter((c) => c.name !== ",");
1634
+ const { outputExpr, clauses } = parseComprehensionClauses(nonCommaItems, ctx);
1635
+ return buildGeneratorChain(outputExpr, clauses);
1636
+ }
1637
+ const args = [];
1638
+ const kwArgs = [];
1639
+ let i = 0;
1640
+ while (i < items.length) {
1641
+ const item = items[i];
1642
+ if (!item) {
1643
+ i++;
1644
+ continue;
1645
+ }
1646
+ if (item.name === ",") {
1647
+ i++;
1648
+ continue;
1649
+ }
1650
+ if (item.name === "*" || item.name === "ArithOp" && getNodeText(item, ctx.source) === "*") {
1651
+ const nextItem = items[i + 1];
1652
+ if (nextItem) {
1653
+ args.push(`...${transformNode(nextItem, ctx)}`);
1654
+ i += 2;
1655
+ continue;
1656
+ }
1657
+ }
1658
+ if (item.name === "**" || item.name === "ArithOp" && getNodeText(item, ctx.source) === "**") {
1659
+ const nextItem = items[i + 1];
1660
+ if (nextItem) {
1661
+ args.push(`...Object.entries(${transformNode(nextItem, ctx)})`);
1662
+ i += 2;
1663
+ continue;
1664
+ }
1665
+ }
1666
+ if (item.name === "VariableName") {
1667
+ const nextItem = items[i + 1];
1668
+ if (nextItem && nextItem.name === "AssignOp") {
1669
+ const valueItem = items[i + 2];
1670
+ if (valueItem) {
1671
+ const name = getNodeText(item, ctx.source);
1672
+ const value = transformNode(valueItem, ctx);
1673
+ kwArgs.push({ name, value });
1674
+ i += 3;
1675
+ continue;
1676
+ }
1677
+ }
1678
+ }
1679
+ args.push(transformNode(item, ctx));
1680
+ i++;
1681
+ }
1682
+ if (kwArgs.length > 0) {
1683
+ const kwArgsStr = kwArgs.map((kw) => `${kw.name}: ${kw.value}`).join(", ");
1684
+ args.push(`{ ${kwArgsStr} }`);
1685
+ }
1686
+ return args.join(", ");
1687
+ }
1688
+ function transformMemberExpression(node, ctx) {
1689
+ const children = getChildren(node);
1690
+ if (children.length < 2) return getNodeText(node, ctx.source);
1691
+ const obj = children[0];
1692
+ if (!obj) return getNodeText(node, ctx.source);
1693
+ const hasOpenBracket = children.some((c) => c.name === "[");
1694
+ if (hasOpenBracket) {
1695
+ const objCode = transformNode(obj, ctx);
1696
+ const text = getNodeText(node, ctx.source);
1697
+ if (text.includes(":")) {
1698
+ return transformSliceFromMember(obj, children, ctx);
1699
+ }
1700
+ const indexElements = children.filter((c) => c.name !== "[" && c.name !== "]" && c !== obj);
1701
+ const index = indexElements[0];
1702
+ if (!index) return `${objCode}[]`;
1703
+ const indexCode = transformNode(index, ctx);
1704
+ if (isNegativeIndexLiteral(index, ctx)) {
1705
+ ctx.usesRuntime.add("at");
1706
+ return `at(${objCode}, ${indexCode})`;
1707
+ }
1708
+ return `${objCode}[${indexCode}]`;
1709
+ } else {
1710
+ const prop = children[children.length - 1];
1711
+ if (!prop) return getNodeText(node, ctx.source);
1712
+ const objName = getNodeText(obj, ctx.source);
1713
+ const propName = getNodeText(prop, ctx.source);
1714
+ if (objName === "math") {
1715
+ ctx.usesRuntime.add(`math/${propName}`);
1716
+ return propName;
1717
+ }
1718
+ if (objName === "os") {
1719
+ if (propName === "path") {
1720
+ ctx.usesRuntime.add("os/path");
1721
+ return "path";
1722
+ }
1723
+ ctx.usesRuntime.add(`os/${propName}`);
1724
+ return propName;
1725
+ }
1726
+ if (objName === "string") {
1727
+ ctx.usesRuntime.add(`string/${propName}`);
1728
+ return propName;
1729
+ }
1730
+ if (objName === "re") {
1731
+ ctx.usesRuntime.add(`re/${propName}`);
1732
+ return propName;
1733
+ }
1734
+ if (objName === "datetime") {
1735
+ ctx.usesRuntime.add(`datetime/${propName}`);
1736
+ return propName;
1737
+ }
1738
+ const objCode = transformNode(obj, ctx);
1739
+ const attrMap = {
1740
+ __name__: "name",
1741
+ __doc__: "undefined",
1742
+ // JS functions don't have docstrings
1743
+ __class__: "constructor",
1744
+ __dict__: "this"
1745
+ // Rough equivalent
1746
+ };
1747
+ const mappedProp = attrMap[propName] ?? propName;
1748
+ return `${objCode}.${mappedProp}`;
1749
+ }
1750
+ }
1751
+ function transformSliceFromMember(obj, children, ctx) {
1752
+ ctx.usesRuntime.add("slice");
1753
+ const objCode = transformNode(obj, ctx);
1754
+ const bracketStart = children.findIndex((c) => c.name === "[");
1755
+ const bracketEnd = children.findIndex((c) => c.name === "]");
1756
+ if (bracketStart === -1 || bracketEnd === -1) {
1757
+ return `slice(${objCode})`;
1758
+ }
1759
+ const sliceElements = children.slice(bracketStart + 1, bracketEnd);
1760
+ const colonIndices = [];
1761
+ sliceElements.forEach((el, i) => {
1762
+ if (el.name === ":") colonIndices.push(i);
1763
+ });
1764
+ const parts = [];
1765
+ let lastIdx = 0;
1766
+ for (const colonIdx of colonIndices) {
1767
+ const beforeColon = sliceElements.slice(lastIdx, colonIdx);
1768
+ if (beforeColon.length > 0 && beforeColon[0]) {
1769
+ parts.push(transformNode(beforeColon[0], ctx));
1770
+ } else {
1771
+ parts.push("undefined");
1772
+ }
1773
+ lastIdx = colonIdx + 1;
1774
+ }
1775
+ const afterLastColon = sliceElements.slice(lastIdx);
1776
+ if (afterLastColon.length > 0 && afterLastColon[0] && afterLastColon[0].name !== ":") {
1777
+ parts.push(transformNode(afterLastColon[0], ctx));
1778
+ } else if (colonIndices.length > 0) {
1779
+ parts.push("undefined");
1780
+ }
1781
+ if (colonIndices.length === 0) {
1782
+ return `slice(${objCode})`;
1783
+ }
1784
+ return `slice(${objCode}, ${parts.join(", ")})`;
1785
+ }
1786
+ function transformArrayExpression(node, ctx) {
1787
+ const children = getChildren(node);
1788
+ const elements = children.filter((c) => c.name !== "[" && c.name !== "]" && c.name !== ",");
1789
+ const elementCodes = elements.map((el) => transformNode(el, ctx));
1790
+ return `[${elementCodes.join(", ")}]`;
1791
+ }
1792
+ function transformDictionaryExpression(node, ctx) {
1793
+ const children = getChildren(node);
1794
+ const pairs = [];
1795
+ const items = children.filter(
1796
+ (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":"
1797
+ );
1798
+ for (let i = 0; i < items.length; i += 2) {
1799
+ const key = items[i];
1800
+ const value = items[i + 1];
1801
+ if (key && value) {
1802
+ const keyCode = transformNode(key, ctx);
1803
+ const valueCode = transformNode(value, ctx);
1804
+ if (key.name === "VariableName") {
1805
+ pairs.push(`[${keyCode}]: ${valueCode}`);
1806
+ } else {
1807
+ pairs.push(`${keyCode}: ${valueCode}`);
1808
+ }
1809
+ }
1810
+ }
1811
+ return `{ ${pairs.join(", ")} }`;
1812
+ }
1813
+ function transformTupleExpression(node, ctx) {
1814
+ const children = getChildren(node);
1815
+ const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
1816
+ ctx.usesRuntime.add("tuple");
1817
+ const elementCodes = elements.map((el) => transformNode(el, ctx));
1818
+ return `tuple(${elementCodes.join(", ")})`;
1819
+ }
1820
+ function isNegativeIndexLiteral(node, ctx) {
1821
+ if (node.name === "UnaryExpression") {
1822
+ const children = getChildren(node);
1823
+ const hasMinusOp = children.some(
1824
+ (c) => c.name === "ArithOp" && getNodeText(c, ctx.source) === "-"
1825
+ );
1826
+ const hasNumber = children.some((c) => c.name === "Number");
1827
+ return hasMinusOp && hasNumber;
1828
+ }
1829
+ return false;
1830
+ }
1831
+ function transformIfStatement(node, ctx) {
1832
+ const children = getChildren(node);
1833
+ const parts = [];
1834
+ let i = 0;
1835
+ while (i < children.length) {
1836
+ const child = children[i];
1837
+ if (!child) {
1838
+ i++;
1839
+ continue;
1840
+ }
1841
+ if (child.name === "if" || child.name === "Keyword" && getNodeText(child, ctx.source) === "if") {
1842
+ const condition = children[i + 1];
1843
+ const body = children.find((c, idx) => idx > i && c.name === "Body");
1844
+ if (condition && body) {
1845
+ const condCode = transformNode(condition, ctx);
1846
+ const bodyCode = transformBody(body, ctx);
1847
+ parts.push(`if (${condCode}) {
1848
+ ${bodyCode}
1849
+ }`);
1850
+ }
1851
+ } else if (child.name === "elif" || child.name === "Keyword" && getNodeText(child, ctx.source) === "elif") {
1852
+ const condition = children[i + 1];
1853
+ const body = children.find((c, idx) => idx > i + 1 && c.name === "Body");
1854
+ if (condition && body) {
1855
+ const condCode = transformNode(condition, ctx);
1856
+ const bodyCode = transformBody(body, ctx);
1857
+ parts.push(` else if (${condCode}) {
1858
+ ${bodyCode}
1859
+ }`);
1860
+ }
1861
+ } else if (child.name === "else" || child.name === "Keyword" && getNodeText(child, ctx.source) === "else") {
1862
+ const body = children.find((c, idx) => idx > i && c.name === "Body");
1863
+ if (body) {
1864
+ const bodyCode = transformBody(body, ctx);
1865
+ parts.push(` else {
1866
+ ${bodyCode}
1867
+ }`);
1868
+ }
1869
+ }
1870
+ i++;
1871
+ }
1872
+ return parts.join("");
1873
+ }
1874
+ function transformWhileStatement(node, ctx) {
1875
+ const children = getChildren(node);
1876
+ const condition = children.find(
1877
+ (c) => c.name !== "while" && c.name !== "Body" && c.name !== "Keyword" && c.name !== ":"
1878
+ );
1879
+ const body = children.find((c) => c.name === "Body");
1880
+ if (!condition || !body) return getNodeText(node, ctx.source);
1881
+ const condCode = transformNode(condition, ctx);
1882
+ const bodyCode = transformBody(body, ctx);
1883
+ return `while (${condCode}) {
1884
+ ${bodyCode}
1885
+ }`;
1886
+ }
1887
+ function transformMatchStatement(node, ctx) {
1888
+ const children = getChildren(node);
1889
+ let subject = null;
1890
+ let matchBody = null;
1891
+ for (const child of children) {
1892
+ if (child.name === "match" || child.name === ":") continue;
1893
+ if (child.name === "MatchBody") {
1894
+ matchBody = child;
1895
+ } else if (!subject) {
1896
+ subject = child;
1897
+ }
1898
+ }
1899
+ if (!subject || !matchBody) return getNodeText(node, ctx.source);
1900
+ const subjectCode = transformNode(subject, ctx);
1901
+ const matchBodyChildren = getChildren(matchBody);
1902
+ const clauseNodes = matchBodyChildren.filter((c) => c.name === "MatchClause");
1903
+ const hasComplexPatterns = clauseNodes.some((clause) => {
1904
+ const clauseChildren = getChildren(clause);
1905
+ const pattern = clauseChildren.find(
1906
+ (c) => c.name !== "case" && c.name !== ":" && c.name !== "Body" && c.name !== "Guard"
1907
+ );
1908
+ const hasGuard = clauseChildren.some((c) => c.name === "Guard");
1909
+ if (hasGuard) return true;
1910
+ if (!pattern) return false;
1911
+ if (pattern.name === "SequencePattern" || pattern.name === "MappingPattern" || pattern.name === "ClassPattern" || pattern.name === "AsPattern") {
1912
+ return true;
1913
+ }
1914
+ if (pattern.name === "OrPattern") {
1915
+ const orChildren = getChildren(pattern);
1916
+ return orChildren.some(
1917
+ (c) => c.name === "SequencePattern" || c.name === "MappingPattern" || c.name === "ClassPattern" || c.name === "AsPattern"
1918
+ );
1919
+ }
1920
+ return false;
1921
+ });
1922
+ if (hasComplexPatterns) {
1923
+ return transformMatchAsIfElse(subjectCode, clauseNodes, ctx);
1924
+ }
1925
+ const clauses = transformMatchBody(matchBody, ctx);
1926
+ return `switch (${subjectCode}) {
1927
+ ${clauses}
1928
+ }`;
1929
+ }
1930
+ function transformMatchAsIfElse(subjectCode, clauses, ctx) {
1931
+ const parts = [];
1932
+ const indent = " ".repeat(ctx.indentLevel);
1933
+ for (let i = 0; i < clauses.length; i++) {
1934
+ const clause = clauses[i];
1935
+ if (!clause) continue;
1936
+ const children = getChildren(clause);
1937
+ let pattern = null;
1938
+ let body = null;
1939
+ let guard = null;
1940
+ for (const child of children) {
1941
+ if (child.name === "case" || child.name === ":") continue;
1942
+ if (child.name === "Body") {
1943
+ body = child;
1944
+ } else if (child.name === "Guard") {
1945
+ guard = child;
1946
+ } else if (!pattern) {
1947
+ pattern = child;
1948
+ }
1949
+ }
1950
+ if (!pattern || !body) continue;
1951
+ ctx.indentLevel++;
1952
+ const bodyCode = transformBody(body, ctx);
1953
+ ctx.indentLevel--;
1954
+ const patternText = getNodeText(pattern, ctx.source);
1955
+ const isWildcard = patternText === "_" || pattern.name === "CapturePattern" && patternText === "_";
1956
+ let guardCondition = null;
1957
+ if (guard) {
1958
+ const guardChildren = getChildren(guard);
1959
+ const guardExpr = guardChildren.find((c) => c.name !== "if");
1960
+ if (guardExpr) {
1961
+ let guardCode = transformNode(guardExpr, ctx);
1962
+ if (pattern.name === "CapturePattern") {
1963
+ const captureVar = getNodeText(pattern, ctx.source);
1964
+ if (captureVar !== "_") {
1965
+ const varRegex = new RegExp(`\\b${captureVar}\\b`, "g");
1966
+ guardCode = guardCode.replace(varRegex, subjectCode);
1967
+ }
1968
+ }
1969
+ guardCondition = guardCode;
1970
+ }
1971
+ }
1972
+ if (isWildcard && !guardCondition) {
1973
+ if (i === 0) {
1974
+ parts.push(`${indent}${bodyCode.trim()}`);
1975
+ } else {
1976
+ parts.push(` else {
1977
+ ${indent} ${bodyCode.trim()}
1978
+ ${indent}}`);
1979
+ }
1980
+ } else {
1981
+ const { condition, bindings } = transformComplexPattern(pattern, subjectCode, ctx);
1982
+ let fullCondition = condition;
1983
+ if (guardCondition) {
1984
+ if (condition === "true") {
1985
+ fullCondition = guardCondition;
1986
+ } else {
1987
+ fullCondition = `${condition} && ${guardCondition}`;
1988
+ }
1989
+ }
1990
+ const keyword = i === 0 ? "if" : " else if";
1991
+ const bindingsCode = bindings.length > 0 ? `
1992
+ ${indent} ${bindings.join(`
1993
+ ${indent} `)}` : "";
1994
+ parts.push(
1995
+ `${keyword} (${fullCondition}) {${bindingsCode}
1996
+ ${indent} ${bodyCode.trim()}
1997
+ ${indent}}`
1998
+ );
1999
+ }
2000
+ }
2001
+ return parts.join("");
2002
+ }
2003
+ function transformComplexPattern(pattern, subject, ctx) {
2004
+ switch (pattern.name) {
2005
+ case "SequencePattern":
2006
+ return transformSequencePattern(pattern, subject, ctx);
2007
+ case "MappingPattern":
2008
+ return transformMappingPattern(pattern, subject, ctx);
2009
+ case "ClassPattern":
2010
+ return transformClassPattern(pattern, subject, ctx);
2011
+ case "OrPattern":
2012
+ return transformOrPattern(pattern, subject, ctx);
2013
+ case "AsPattern":
2014
+ return transformAsPattern(pattern, subject, ctx);
2015
+ case "LiteralPattern": {
2016
+ const children = getChildren(pattern);
2017
+ const literal = children[0];
2018
+ const value = literal ? transformNode(literal, ctx) : getNodeText(pattern, ctx.source);
2019
+ return { condition: `${subject} === ${value}`, bindings: [] };
2020
+ }
2021
+ case "CapturePattern": {
2022
+ const varName = getNodeText(pattern, ctx.source);
2023
+ if (varName === "_") {
2024
+ return { condition: "true", bindings: [] };
2025
+ }
2026
+ return { condition: "true", bindings: [`const ${varName} = ${subject};`] };
2027
+ }
2028
+ /* v8 ignore next 3 -- fallback for unknown match patterns @preserve */
2029
+ default:
2030
+ return { condition: `${subject} === ${getNodeText(pattern, ctx.source)}`, bindings: [] };
2031
+ }
2032
+ }
2033
+ function transformSequencePattern(pattern, subject, ctx) {
2034
+ const children = getChildren(pattern);
2035
+ const elements = children.filter((c) => c.name !== "[" && c.name !== "]" && c.name !== ",");
2036
+ const conditions = [`Array.isArray(${subject})`];
2037
+ const bindings = [];
2038
+ const hasStarred = elements.some((e) => e.name === "StarPattern");
2039
+ if (!hasStarred) {
2040
+ conditions.push(`${subject}.length === ${String(elements.length)}`);
2041
+ }
2042
+ elements.forEach((elem, idx) => {
2043
+ const idxStr = String(idx);
2044
+ if (elem.name === "CapturePattern") {
2045
+ const varName = getNodeText(elem, ctx.source);
2046
+ if (varName !== "_") {
2047
+ bindings.push(`const ${varName} = ${subject}[${idxStr}];`);
2048
+ }
2049
+ } else if (elem.name === "LiteralPattern") {
2050
+ const childNodes = getChildren(elem);
2051
+ const literal = childNodes[0];
2052
+ const value = literal ? transformNode(literal, ctx) : getNodeText(elem, ctx.source);
2053
+ conditions.push(`${subject}[${idxStr}] === ${value}`);
2054
+ }
2055
+ });
2056
+ return { condition: conditions.join(" && "), bindings };
2057
+ }
2058
+ function transformMappingPattern(pattern, subject, ctx) {
2059
+ const children = getChildren(pattern);
2060
+ const conditions = [`typeof ${subject} === "object"`, `${subject} !== null`];
2061
+ const bindings = [];
2062
+ let i = 0;
2063
+ while (i < children.length) {
2064
+ const child = children[i];
2065
+ if (child?.name === "LiteralPattern" || child?.name === "String") {
2066
+ const keyNode = child.name === "LiteralPattern" ? getChildren(child)[0] : child;
2067
+ const key = keyNode ? transformNode(keyNode, ctx) : getNodeText(child, ctx.source);
2068
+ const valuePattern = children[i + 2];
2069
+ if (children[i + 1]?.name === ":" && valuePattern) {
2070
+ conditions.push(`${key} in ${subject}`);
2071
+ if (valuePattern.name === "CapturePattern") {
2072
+ const varName = getNodeText(valuePattern, ctx.source);
2073
+ if (varName !== "_") {
2074
+ bindings.push(`const ${varName} = ${subject}[${key}];`);
2075
+ }
2076
+ } else if (valuePattern.name === "LiteralPattern") {
2077
+ const valueChildren = getChildren(valuePattern);
2078
+ const literal = valueChildren[0];
2079
+ const value = literal ? transformNode(literal, ctx) : getNodeText(valuePattern, ctx.source);
2080
+ conditions.push(`${subject}[${key}] === ${value}`);
2081
+ }
2082
+ i += 3;
2083
+ continue;
2084
+ }
2085
+ }
2086
+ i++;
2087
+ }
2088
+ return { condition: conditions.join(" && "), bindings };
2089
+ }
2090
+ function transformClassPattern(pattern, subject, ctx) {
2091
+ const children = getChildren(pattern);
2092
+ const className = children.find((c) => c.name === "VariableName");
2093
+ const argList = children.find((c) => c.name === "PatternArgList");
2094
+ const conditions = [];
2095
+ const bindings = [];
2096
+ if (className) {
2097
+ const classNameText = getNodeText(className, ctx.source);
2098
+ conditions.push(`${subject} instanceof ${classNameText}`);
2099
+ }
2100
+ if (argList) {
2101
+ const argChildren = getChildren(argList);
2102
+ for (const arg of argChildren) {
2103
+ if (arg.name === "KeywordPattern") {
2104
+ const kwChildren = getChildren(arg);
2105
+ const attrName = kwChildren.find((c) => c.name === "VariableName");
2106
+ const valuePattern = kwChildren.find(
2107
+ (c) => c.name === "LiteralPattern" || c.name === "CapturePattern"
2108
+ );
2109
+ if (attrName && valuePattern) {
2110
+ const attrNameText = getNodeText(attrName, ctx.source);
2111
+ if (valuePattern.name === "LiteralPattern") {
2112
+ const litChildren = getChildren(valuePattern);
2113
+ const literal = litChildren[0];
2114
+ const value = literal ? transformNode(literal, ctx) : getNodeText(valuePattern, ctx.source);
2115
+ conditions.push(`${subject}.${attrNameText} === ${value}`);
2116
+ } else if (valuePattern.name === "CapturePattern") {
2117
+ const varName = getNodeText(valuePattern, ctx.source);
2118
+ if (varName !== "_") {
2119
+ bindings.push(`const ${varName} = ${subject}.${attrNameText};`);
2120
+ }
2121
+ }
2122
+ }
2123
+ }
2124
+ }
2125
+ }
2126
+ return { condition: conditions.length > 0 ? conditions.join(" && ") : "true", bindings };
2127
+ }
2128
+ function transformOrPattern(pattern, subject, ctx) {
2129
+ const children = getChildren(pattern);
2130
+ const subPatterns = children.filter((c) => c.name !== "LogicOp");
2131
+ const conditions = [];
2132
+ for (const subPattern of subPatterns) {
2133
+ const { condition } = transformComplexPattern(subPattern, subject, ctx);
2134
+ conditions.push(condition);
2135
+ }
2136
+ return { condition: conditions.join(" || "), bindings: [] };
2137
+ }
2138
+ function transformAsPattern(pattern, subject, ctx) {
2139
+ const children = getChildren(pattern);
2140
+ const innerPattern = children.find(
2141
+ (c) => c.name !== "as" && c.name !== "VariableName" && c.name !== "\u26A0"
2142
+ );
2143
+ const asName = children.find((c) => c.name === "VariableName");
2144
+ if (!innerPattern) {
2145
+ return { condition: "true", bindings: [] };
2146
+ }
2147
+ const { condition, bindings } = transformComplexPattern(innerPattern, subject, ctx);
2148
+ if (asName) {
2149
+ const varName = getNodeText(asName, ctx.source);
2150
+ bindings.push(`const ${varName} = ${subject};`);
2151
+ }
2152
+ return { condition, bindings };
2153
+ }
2154
+ function transformMatchBody(node, ctx) {
2155
+ const children = getChildren(node);
2156
+ const clauses = [];
2157
+ const indent = " ".repeat(ctx.indentLevel + 1);
2158
+ for (const child of children) {
2159
+ if (child.name === "MatchClause") {
2160
+ clauses.push(transformMatchClause(child, ctx, indent));
2161
+ }
2162
+ }
2163
+ return clauses.join("\n");
2164
+ }
2165
+ function transformMatchClause(node, ctx, indent) {
2166
+ const children = getChildren(node);
2167
+ let pattern = null;
2168
+ let body = null;
2169
+ for (const child of children) {
2170
+ if (child.name === "case" || child.name === ":") continue;
2171
+ if (child.name === "Body") {
2172
+ body = child;
2173
+ } else if (!pattern) {
2174
+ pattern = child;
2175
+ }
2176
+ }
2177
+ if (!pattern || !body) return "";
2178
+ const patternText = getNodeText(pattern, ctx.source);
2179
+ ctx.indentLevel++;
2180
+ const bodyCode = transformBody(body, ctx);
2181
+ ctx.indentLevel--;
2182
+ const bodyIndent = indent + " ";
2183
+ if (patternText === "_" || pattern.name === "CapturePattern") {
2184
+ const captureVar = getNodeText(pattern, ctx.source);
2185
+ if (captureVar === "_") {
2186
+ return `${indent}default:
2187
+ ${bodyIndent}${bodyCode.trim()}
2188
+ ${bodyIndent}break;`;
2189
+ }
2190
+ }
2191
+ if (pattern.name === "OrPattern") {
2192
+ const orChildren = getChildren(pattern);
2193
+ const subPatterns = orChildren.filter((c) => c.name !== "LogicOp");
2194
+ const caseLabels = subPatterns.map((p) => {
2195
+ const caseValue2 = transformMatchPatternSimple(p, ctx);
2196
+ return `${indent}case ${caseValue2}:`;
2197
+ });
2198
+ return `${caseLabels.join("\n")}
2199
+ ${bodyIndent}${bodyCode.trim()}
2200
+ ${bodyIndent}break;`;
2201
+ }
2202
+ const caseValue = transformMatchPatternSimple(pattern, ctx);
2203
+ return `${indent}case ${caseValue}:
2204
+ ${bodyIndent}${bodyCode.trim()}
2205
+ ${bodyIndent}break;`;
2206
+ }
2207
+ function transformMatchPatternSimple(node, ctx) {
2208
+ switch (node.name) {
2209
+ case "LiteralPattern": {
2210
+ const children = getChildren(node);
2211
+ const literal = children[0];
2212
+ if (literal) {
2213
+ return transformNode(literal, ctx);
2214
+ }
2215
+ return getNodeText(node, ctx.source);
2216
+ }
2217
+ case "CapturePattern":
2218
+ return getNodeText(node, ctx.source);
2219
+ /* v8 ignore next 2 -- fallback for unknown case patterns @preserve */
2220
+ default:
2221
+ return getNodeText(node, ctx.source);
2222
+ }
2223
+ }
2224
+ function transformForStatement(node, ctx) {
2225
+ const children = getChildren(node);
2226
+ const isAsync = children.some(
2227
+ (c) => c.name === "async" || c.name === "Keyword" && getNodeText(c, ctx.source) === "async"
2228
+ );
2229
+ const varNodes = [];
2230
+ let iterableNode = null;
2231
+ let bodyNode = null;
2232
+ let foundFor = false;
2233
+ let foundIn = false;
2234
+ for (const child of children) {
2235
+ if (child.name === "for" || child.name === "Keyword" && getNodeText(child, ctx.source) === "for") {
2236
+ foundFor = true;
2237
+ } else if (child.name === "in" || child.name === "Keyword" && getNodeText(child, ctx.source) === "in") {
2238
+ foundIn = true;
2239
+ } else if (child.name === "Body") {
2240
+ bodyNode = child;
2241
+ } else if (child.name !== ":" && child.name !== "Keyword" && child.name !== "," && child.name !== "async") {
2242
+ if (foundFor && !foundIn) {
2243
+ varNodes.push(child);
2244
+ } else if (foundIn && !bodyNode) {
2245
+ iterableNode = child;
2246
+ }
2247
+ }
2248
+ }
2249
+ if (varNodes.length === 0 || !iterableNode || !bodyNode) {
2250
+ return getNodeText(node, ctx.source);
2251
+ }
2252
+ let varCode;
2253
+ if (varNodes.length === 1 && varNodes[0]) {
2254
+ varCode = transformNode(varNodes[0], ctx);
2255
+ } else {
2256
+ varCode = "[" + varNodes.map((v) => transformForLoopVar(v, ctx)).join(", ") + "]";
2257
+ }
2258
+ let iterableCode = transformNode(iterableNode, ctx);
2259
+ const bodyCode = transformBody(bodyNode, ctx);
2260
+ if (iterableNode.name === "VariableName" && !isAsync) {
2261
+ ctx.usesRuntime.add("iter");
2262
+ iterableCode = `iter(${iterableCode})`;
2263
+ }
2264
+ const forKeyword = isAsync ? "for await" : "for";
2265
+ return `${forKeyword} (const ${varCode} of ${iterableCode}) {
2266
+ ${bodyCode}
2267
+ }`;
2268
+ }
2269
+ function transformForLoopVar(node, ctx) {
2270
+ if (node.name === "VariableName") {
2271
+ return getNodeText(node, ctx.source);
2272
+ } else if (node.name === "TupleExpression") {
2273
+ const children = getChildren(node);
2274
+ const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
2275
+ return "[" + elements.map((e) => transformForLoopVar(e, ctx)).join(", ") + "]";
2276
+ }
2277
+ return transformNode(node, ctx);
2278
+ }
2279
+ function transformTryStatement(node, ctx) {
2280
+ const children = getChildren(node);
2281
+ const baseIndent = " ".repeat(ctx.indentLevel);
2282
+ let tryBody = null;
2283
+ const exceptBodies = [];
2284
+ let finallyBody = null;
2285
+ let i = 0;
2286
+ while (i < children.length) {
2287
+ const child = children[i];
2288
+ if (!child) {
2289
+ i++;
2290
+ continue;
2291
+ }
2292
+ if (child.name === "try") {
2293
+ const nextBody = children[i + 1];
2294
+ if (nextBody && nextBody.name === "Body") {
2295
+ tryBody = nextBody;
2296
+ i += 2;
2297
+ continue;
2298
+ }
2299
+ }
2300
+ if (child.name === "except") {
2301
+ let exceptType = null;
2302
+ let exceptVar = null;
2303
+ let exceptBody = null;
2304
+ let j = i + 1;
2305
+ while (j < children.length) {
2306
+ const next = children[j];
2307
+ if (!next) break;
2308
+ if (next.name === "Body") {
2309
+ exceptBody = next;
2310
+ j++;
2311
+ break;
2312
+ } else if (next.name === "VariableName") {
2313
+ if (exceptType === null) {
2314
+ exceptType = getNodeText(next, ctx.source);
2315
+ } else {
2316
+ exceptVar = getNodeText(next, ctx.source);
2317
+ }
2318
+ } else if (next.name === "as") {
2319
+ } else if (next.name === "except" || next.name === "finally") {
2320
+ break;
2321
+ }
2322
+ j++;
2323
+ }
2324
+ if (exceptBody) {
2325
+ exceptBodies.push({ type: exceptType, varName: exceptVar, body: exceptBody });
2326
+ }
2327
+ i = j;
2328
+ continue;
2329
+ }
2330
+ if (child.name === "finally") {
2331
+ const nextBody = children[i + 1];
2332
+ if (nextBody && nextBody.name === "Body") {
2333
+ finallyBody = nextBody;
2334
+ i += 2;
2335
+ continue;
2336
+ }
2337
+ }
2338
+ i++;
2339
+ }
2340
+ if (!tryBody) {
2341
+ return getNodeText(node, ctx.source);
2342
+ }
2343
+ const tryCode = transformBody(tryBody, ctx);
2344
+ let result = `try {
2345
+ ${tryCode}
2346
+ ${baseIndent}}`;
2347
+ if (exceptBodies.length > 0) {
2348
+ const firstExcept = exceptBodies[0];
2349
+ if (firstExcept) {
2350
+ const catchVar = firstExcept.varName || "e";
2351
+ const catchBody = transformBody(firstExcept.body, ctx);
2352
+ if (exceptBodies.length === 1 && !firstExcept.type) {
2353
+ result += ` catch (${catchVar}) {
2354
+ ${catchBody}
2355
+ ${baseIndent}}`;
2356
+ } else if (exceptBodies.length === 1) {
2357
+ result += ` catch (${catchVar}) {
2358
+ ${catchBody}
2359
+ ${baseIndent}}`;
2360
+ } else {
2361
+ const innerIndent = " ".repeat(ctx.indentLevel + 1);
2362
+ let catchBodyCode = "";
2363
+ for (let idx = 0; idx < exceptBodies.length; idx++) {
2364
+ const exc = exceptBodies[idx];
2365
+ if (!exc) continue;
2366
+ const excBodyCode = transformBody(exc.body, ctx);
2367
+ const excVar = exc.varName || catchVar;
2368
+ if (exc.type) {
2369
+ const condition = idx === 0 ? "if" : "} else if";
2370
+ const mappedType = mapExceptionType(exc.type);
2371
+ catchBodyCode += `${innerIndent}${condition} (${catchVar} instanceof ${mappedType}) {
2372
+ `;
2373
+ if (excVar !== catchVar) {
2374
+ catchBodyCode += `${innerIndent} const ${excVar} = ${catchVar};
2375
+ `;
2376
+ }
2377
+ catchBodyCode += excBodyCode.split("\n").map((line) => " " + line).join("\n");
2378
+ catchBodyCode += "\n";
2379
+ } else {
2380
+ if (idx > 0) {
2381
+ catchBodyCode += `${innerIndent}} else {
2382
+ `;
2383
+ }
2384
+ catchBodyCode += excBodyCode.split("\n").map((line) => " " + line).join("\n");
2385
+ catchBodyCode += "\n";
2386
+ }
2387
+ }
2388
+ if (exceptBodies.some((e) => e.type)) {
2389
+ catchBodyCode += `${innerIndent}}`;
2390
+ }
2391
+ result += ` catch (${catchVar}) {
2392
+ ${catchBodyCode}${baseIndent}}`;
2393
+ }
2394
+ }
2395
+ }
2396
+ if (finallyBody) {
2397
+ const finallyCode = transformBody(finallyBody, ctx);
2398
+ result += ` finally {
2399
+ ${finallyCode}
2400
+ ${baseIndent}}`;
2401
+ }
2402
+ return result;
2403
+ }
2404
+ function mapExceptionType(pythonType) {
2405
+ const mapping = {
2406
+ Exception: "Error",
2407
+ BaseException: "Error",
2408
+ ValueError: "Error",
2409
+ TypeError: "TypeError",
2410
+ KeyError: "Error",
2411
+ IndexError: "RangeError",
2412
+ AttributeError: "Error",
2413
+ RuntimeError: "Error",
2414
+ StopIteration: "Error",
2415
+ ZeroDivisionError: "Error",
2416
+ FileNotFoundError: "Error",
2417
+ IOError: "Error",
2418
+ OSError: "Error",
2419
+ NameError: "ReferenceError",
2420
+ SyntaxError: "SyntaxError"
2421
+ };
2422
+ return mapping[pythonType] || "Error";
2423
+ }
2424
+ function transformRaiseStatement(node, ctx) {
2425
+ const children = getChildren(node);
2426
+ const exprNode = children.find((c) => c.name !== "raise");
2427
+ if (!exprNode) {
2428
+ return "throw";
2429
+ }
2430
+ const expr = transformNode(exprNode, ctx);
2431
+ if (exprNode.name === "CallExpression") {
2432
+ const callChildren = getChildren(exprNode);
2433
+ const funcName = callChildren.find((c) => c.name === "VariableName");
2434
+ if (funcName) {
2435
+ const name = getNodeText(funcName, ctx.source);
2436
+ if (isExceptionType(name)) {
2437
+ const argList = callChildren.find((c) => c.name === "ArgList");
2438
+ if (argList) {
2439
+ const args = getChildren(argList).filter(
2440
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== ","
2441
+ );
2442
+ if (args.length > 0 && args[0]) {
2443
+ const message = transformNode(args[0], ctx);
2444
+ return `throw new Error(${message})`;
2445
+ }
2446
+ }
2447
+ return "throw new Error()";
2448
+ }
2449
+ }
2450
+ }
2451
+ if (exprNode.name === "String") {
2452
+ return `throw new Error(${expr})`;
2453
+ }
2454
+ return `throw ${expr}`;
2455
+ }
2456
+ function isExceptionType(name) {
2457
+ const exceptionTypes = [
2458
+ "Exception",
2459
+ "BaseException",
2460
+ "ValueError",
2461
+ "TypeError",
2462
+ "KeyError",
2463
+ "IndexError",
2464
+ "AttributeError",
2465
+ "RuntimeError",
2466
+ "StopIteration",
2467
+ "ZeroDivisionError",
2468
+ "FileNotFoundError",
2469
+ "IOError",
2470
+ "OSError",
2471
+ "NameError",
2472
+ "SyntaxError",
2473
+ "AssertionError",
2474
+ "NotImplementedError",
2475
+ "ImportError",
2476
+ "ModuleNotFoundError"
2477
+ ];
2478
+ return exceptionTypes.includes(name);
2479
+ }
2480
+ function transformImportStatement(node, ctx) {
2481
+ const children = getChildren(node);
2482
+ const hasFrom = children.some((c) => c.name === "from");
2483
+ if (hasFrom) {
2484
+ return transformFromImport(children, ctx);
2485
+ } else {
2486
+ return transformSimpleImport(children, ctx);
2487
+ }
2488
+ }
2489
+ function transformSimpleImport(children, ctx) {
2490
+ const names = [];
2491
+ let i = 0;
2492
+ while (i < children.length) {
2493
+ const child = children[i];
2494
+ if (!child) {
2495
+ i++;
2496
+ continue;
2497
+ }
2498
+ if (child.name === "import" || child.name === ",") {
2499
+ i++;
2500
+ continue;
2501
+ }
2502
+ if (child.name === "VariableName") {
2503
+ const moduleName = getNodeText(child, ctx.source);
2504
+ let alias = null;
2505
+ const nextChild = children[i + 1];
2506
+ if (nextChild && nextChild.name === "as") {
2507
+ const aliasChild = children[i + 2];
2508
+ if (aliasChild && aliasChild.name === "VariableName") {
2509
+ alias = getNodeText(aliasChild, ctx.source);
2510
+ i += 3;
2511
+ names.push({ module: moduleName, alias });
2512
+ continue;
2513
+ }
2514
+ }
2515
+ names.push({ module: moduleName, alias: null });
2516
+ i++;
2517
+ continue;
2518
+ }
2519
+ i++;
2520
+ }
2521
+ const filteredNames = names.filter(
2522
+ ({ module }) => !RUNTIME_MODULES.has(module) && !TYPING_MODULES.has(module)
2523
+ );
2524
+ if (filteredNames.length === 0) {
2525
+ return "";
2526
+ }
2527
+ return filteredNames.map(({ module, alias }) => {
2528
+ const importName = alias || module;
2529
+ return `import * as ${importName} from "${module}"`;
2530
+ }).join("\n");
2531
+ }
2532
+ var TYPING_MODULES = /* @__PURE__ */ new Set([
2533
+ "typing",
2534
+ "typing_extensions",
2535
+ "collections.abc",
2536
+ "__future__",
2537
+ "abc"
2538
+ ]);
2539
+ var RUNTIME_MODULES = /* @__PURE__ */ new Set([
2540
+ "itertools",
2541
+ "collections",
2542
+ "math",
2543
+ "random",
2544
+ "json",
2545
+ "os",
2546
+ "datetime",
2547
+ "re",
2548
+ "string",
2549
+ "functools"
2550
+ ]);
2551
+ function transformFromImport(children, ctx) {
2552
+ let preCheckModule = "";
2553
+ for (const child of children) {
2554
+ if (child.name === "VariableName") {
2555
+ const prevChild = children[children.indexOf(child) - 1];
2556
+ if (prevChild?.name === "from" || prevChild?.name === ".") {
2557
+ preCheckModule = getNodeText(child, ctx.source);
2558
+ break;
2559
+ }
2560
+ }
2561
+ }
2562
+ if (TYPING_MODULES.has(preCheckModule)) {
2563
+ return "";
2564
+ }
2565
+ if (RUNTIME_MODULES.has(preCheckModule)) {
2566
+ return "";
2567
+ }
2568
+ let moduleName = "";
2569
+ let relativeDots = 0;
2570
+ const imports = [];
2571
+ let hasStar = false;
2572
+ let phase = "from";
2573
+ for (let i = 0; i < children.length; i++) {
2574
+ const child = children[i];
2575
+ if (!child) continue;
2576
+ if (child.name === "from") {
2577
+ phase = "module";
2578
+ continue;
2579
+ }
2580
+ if (child.name === ".") {
2581
+ relativeDots++;
2582
+ continue;
2583
+ }
2584
+ if (child.name === "Ellipsis") {
2585
+ relativeDots += 3;
2586
+ continue;
2587
+ }
2588
+ if (child.name === "import") {
2589
+ phase = "names";
2590
+ continue;
2591
+ }
2592
+ if (phase === "module" && child.name === "VariableName") {
2593
+ moduleName = getNodeText(child, ctx.source);
2594
+ continue;
2595
+ }
2596
+ if (phase === "names") {
2597
+ if (child.name === "*") {
2598
+ hasStar = true;
2599
+ continue;
2600
+ }
2601
+ if (child.name === "VariableName") {
2602
+ const name = getNodeText(child, ctx.source);
2603
+ const nextChild = children[i + 1];
2604
+ if (nextChild && nextChild.name === "as") {
2605
+ const aliasChild = children[i + 2];
2606
+ if (aliasChild && aliasChild.name === "VariableName") {
2607
+ imports.push({ name, alias: getNodeText(aliasChild, ctx.source) });
2608
+ i += 2;
2609
+ continue;
2610
+ }
2611
+ }
2612
+ imports.push({ name, alias: null });
2613
+ continue;
2614
+ }
2615
+ if (child.name === ",") {
2616
+ continue;
2617
+ }
2618
+ }
2619
+ }
2620
+ let modulePath = "";
2621
+ if (relativeDots > 0) {
2622
+ if (relativeDots === 1) {
2623
+ modulePath = "./";
2624
+ } else {
2625
+ modulePath = "../".repeat(relativeDots - 1);
2626
+ }
2627
+ if (moduleName) {
2628
+ modulePath += moduleName;
2629
+ } else if (imports.length > 0) {
2630
+ modulePath += imports[0]?.name || "";
2631
+ }
2632
+ } else {
2633
+ modulePath = moduleName;
2634
+ }
2635
+ if (hasStar) {
2636
+ return `import * as ${moduleName || "_"} from "${modulePath}"`;
2637
+ }
2638
+ if (relativeDots > 0 && !moduleName && imports.length === 1) {
2639
+ const imp = imports[0];
2640
+ if (imp) {
2641
+ const importName = imp.alias || imp.name;
2642
+ return `import * as ${importName} from "${modulePath}"`;
2643
+ }
2644
+ }
2645
+ const importNames = imports.map(({ name, alias }) => alias ? `${name} as ${alias}` : name).join(", ");
2646
+ return `import { ${importNames} } from "${modulePath}"`;
2647
+ }
2648
+ function transformAwaitExpression(node, ctx) {
2649
+ const children = getChildren(node);
2650
+ const exprNode = children.find((c) => c.name !== "await");
2651
+ if (!exprNode) {
2652
+ return "await";
2653
+ }
2654
+ return `await ${transformNode(exprNode, ctx)}`;
2655
+ }
2656
+ function transformWithStatement(node, ctx) {
2657
+ const children = getChildren(node);
2658
+ const isAsync = children.some((c) => c.name === "async");
2659
+ const contextManagers = [];
2660
+ let body = null;
2661
+ let i = 0;
2662
+ while (i < children.length) {
2663
+ const child = children[i];
2664
+ if (!child) {
2665
+ i++;
2666
+ continue;
2667
+ }
2668
+ if (child.name === "with" || child.name === "async" || child.name === ",") {
2669
+ i++;
2670
+ continue;
2671
+ }
2672
+ if (child.name === "Body") {
2673
+ body = child;
2674
+ break;
2675
+ }
2676
+ if (child.name !== "as" && child.name !== ":" && child.name !== "VariableName") {
2677
+ const expr = child;
2678
+ let varName = null;
2679
+ const nextChild = children[i + 1];
2680
+ if (nextChild && nextChild.name === "as") {
2681
+ const varChild = children[i + 2];
2682
+ if (varChild && varChild.name === "VariableName") {
2683
+ varName = getNodeText(varChild, ctx.source);
2684
+ i += 3;
2685
+ } else {
2686
+ i++;
2687
+ }
2688
+ } else {
2689
+ i++;
2690
+ }
2691
+ contextManagers.push({ expr, varName });
2692
+ continue;
2693
+ }
2694
+ i++;
2695
+ }
2696
+ if (contextManagers.length === 0 || !body) {
2697
+ return getNodeText(node, ctx.source);
2698
+ }
2699
+ const bodyCode = transformBody(body, ctx);
2700
+ let result = bodyCode;
2701
+ for (let j = contextManagers.length - 1; j >= 0; j--) {
2702
+ const cm = contextManagers[j];
2703
+ if (!cm) continue;
2704
+ const exprCode = transformNode(cm.expr, ctx);
2705
+ const innerIndent = " ".repeat(ctx.indentLevel + j);
2706
+ const innerIndent2 = " ".repeat(ctx.indentLevel + j + 1);
2707
+ if (cm.varName) {
2708
+ const assignment = `${innerIndent}const ${cm.varName} = ${exprCode};
2709
+ `;
2710
+ const tryBlock = `${innerIndent}try {
2711
+ ${result}
2712
+ ${innerIndent}}`;
2713
+ const finallyBlock = ` finally {
2714
+ ${innerIndent2}${cm.varName}[Symbol.dispose]?.() ?? ${cm.varName}.close?.();
2715
+ ${innerIndent}}`;
2716
+ if (isAsync && j === 0) {
2717
+ const asyncFinallyBlock = ` finally {
2718
+ ${innerIndent2}await (${cm.varName}[Symbol.asyncDispose]?.() ?? ${cm.varName}[Symbol.dispose]?.() ?? ${cm.varName}.close?.());
2719
+ ${innerIndent}}`;
2720
+ result = assignment + tryBlock + asyncFinallyBlock;
2721
+ } else {
2722
+ result = assignment + tryBlock + finallyBlock;
2723
+ }
2724
+ } else {
2725
+ const tempVar = j > 0 ? `_resource${String(j)}` : "_resource";
2726
+ const assignment = `${innerIndent}const ${tempVar} = ${exprCode};
2727
+ `;
2728
+ const tryBlock = `${innerIndent}try {
2729
+ ${result}
2730
+ ${innerIndent}}`;
2731
+ const finallyBlock = ` finally {
2732
+ ${innerIndent2}${tempVar}[Symbol.dispose]?.() ?? ${tempVar}.close?.();
2733
+ ${innerIndent}}`;
2734
+ result = assignment + tryBlock + finallyBlock;
2735
+ }
2736
+ }
2737
+ return result;
2738
+ }
2739
+ function transformBody(node, ctx, skipFirst = false) {
2740
+ ctx.indentLevel++;
2741
+ pushScope(ctx);
2742
+ const children = getChildren(node);
2743
+ const indent = " ".repeat(ctx.indentLevel);
2744
+ let filteredChildren = children.filter((child) => child.name !== ":");
2745
+ if (skipFirst && filteredChildren.length > 0) {
2746
+ filteredChildren = filteredChildren.slice(1);
2747
+ }
2748
+ const statements = filteredChildren.map((child) => {
2749
+ const transformed = transformNode(child, ctx);
2750
+ if (transformed === "") {
2751
+ return "";
2752
+ }
2753
+ if (child.name === "ExpressionStatement" || child.name === "AssignStatement" || child.name === "PassStatement" || child.name === "BreakStatement" || child.name === "ContinueStatement" || child.name === "ReturnStatement" || child.name === "RaiseStatement") {
2754
+ return indent + transformed + ";";
2755
+ }
2756
+ return indent + transformed;
2757
+ }).filter((s) => s.trim() !== "");
2758
+ popScope(ctx);
2759
+ ctx.indentLevel--;
2760
+ return statements.join("\n");
2761
+ }
2762
+ function transformReturnStatement(node, ctx) {
2763
+ const children = getChildren(node);
2764
+ const value = children.find((c) => c.name !== "return" && c.name !== "Keyword");
2765
+ if (!value) {
2766
+ return "return";
2767
+ }
2768
+ return `return ${transformNode(value, ctx)}`;
2769
+ }
2770
+ function transformFunctionDefinition(node, ctx) {
2771
+ const children = getChildren(node);
2772
+ let isAsync = false;
2773
+ let funcName = "";
2774
+ let paramList = null;
2775
+ let body = null;
2776
+ let returnTypeDef = null;
2777
+ for (const child of children) {
2778
+ if (child.name === "async") {
2779
+ isAsync = true;
2780
+ } else if (child.name === "VariableName") {
2781
+ funcName = getNodeText(child, ctx.source);
2782
+ } else if (child.name === "ParamList") {
2783
+ paramList = child;
2784
+ } else if (child.name === "Body") {
2785
+ body = child;
2786
+ } else if (child.name === "TypeDef") {
2787
+ returnTypeDef = child;
2788
+ }
2789
+ }
2790
+ const indent = " ".repeat(ctx.indentLevel);
2791
+ const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
2792
+ const params = paramList ? transformParamList(paramList, ctx) : "";
2793
+ const bodyCode = body ? transformBody(body, ctx, skipFirstStatement) : "";
2794
+ const isGenerator = body ? containsYield(body) : false;
2795
+ let returnType = "";
2796
+ if (returnTypeDef) {
2797
+ const typeChildren = getChildren(returnTypeDef);
2798
+ const typeNode = typeChildren.find((c) => c.name !== ":" && c.name !== "->");
2799
+ if (typeNode) {
2800
+ const tsType = transformPythonType(typeNode, ctx);
2801
+ returnType = `: ${tsType === "null" ? "void" : tsType}`;
2802
+ }
2803
+ }
2804
+ const asyncPrefix = isAsync ? "async " : "";
2805
+ const generatorStar = isGenerator ? "*" : "";
2806
+ const funcDecl = `${asyncPrefix}function${generatorStar} ${funcName}(${params})${returnType} {
2807
+ ${bodyCode}
2808
+ }`;
2809
+ return jsdoc ? `${jsdoc}
2810
+ ${funcDecl}` : funcDecl;
2811
+ }
2812
+ function transformClassDefinition(node, ctx) {
2813
+ const children = getChildren(node);
2814
+ let className = "";
2815
+ const parentClasses = [];
2816
+ let body = null;
2817
+ for (const child of children) {
2818
+ if (child.name === "VariableName") {
2819
+ className = getNodeText(child, ctx.source);
2820
+ ctx.definedClasses.add(className);
2821
+ } else if (child.name === "ArgList") {
2822
+ const argChildren = getChildren(child);
2823
+ for (const argChild of argChildren) {
2824
+ if (argChild.name === "VariableName") {
2825
+ parentClasses.push(getNodeText(argChild, ctx.source));
2826
+ } else if (argChild.name === "MemberExpression") {
2827
+ parentClasses.push(getNodeText(argChild, ctx.source));
2828
+ }
2829
+ }
2830
+ } else if (child.name === "Body") {
2831
+ body = child;
2832
+ }
2833
+ }
2834
+ const indent = " ".repeat(ctx.indentLevel);
2835
+ const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
2836
+ let classHeader = `class ${className}`;
2837
+ let multipleInheritanceWarning = "";
2838
+ const firstParent = parentClasses[0];
2839
+ if (firstParent) {
2840
+ if (firstParent === "NamedTuple") {
2841
+ return transformNamedTuple(className, body, ctx);
2842
+ }
2843
+ if (firstParent === "Enum" || firstParent === "IntEnum" || firstParent === "StrEnum") {
2844
+ return transformEnum(className, firstParent, body, ctx);
2845
+ }
2846
+ if (firstParent === "TypedDict") {
2847
+ const totalFalse = checkTypedDictTotalFalse(node, ctx);
2848
+ return transformTypedDict(className, body, totalFalse, ctx);
2849
+ }
2850
+ if (firstParent === "Protocol") {
2851
+ return transformProtocol(className, parentClasses, body, ctx);
2852
+ }
2853
+ if (firstParent === "ABC" || parentClasses.includes("ABC")) {
2854
+ return transformAbstractClass(className, parentClasses, body, ctx, jsdoc);
2855
+ }
2856
+ const genericParams = extractGenericParams(parentClasses);
2857
+ if (genericParams.length > 0) {
2858
+ const filteredParents = parentClasses.filter((p) => !p.startsWith("Generic["));
2859
+ return transformGenericClass(className, genericParams, filteredParents, body, ctx, jsdoc);
2860
+ }
2861
+ classHeader += ` extends ${firstParent}`;
2862
+ if (parentClasses.length > 1) {
2863
+ const ignoredParents = parentClasses.slice(1).join(", ");
2864
+ multipleInheritanceWarning = `/* WARNING: Multiple inheritance not fully supported. Only extends ${firstParent}. Mixins needed for: ${ignoredParents} */
2865
+ `;
2866
+ }
2867
+ }
2868
+ const bodyCode = body ? transformClassBody(body, ctx, skipFirstStatement) : "";
2869
+ const classDecl = `${multipleInheritanceWarning}${classHeader} {
2870
+ ${bodyCode}
2871
+ }`;
2872
+ return jsdoc ? `${jsdoc}
2873
+ ${classDecl}` : classDecl;
2874
+ }
2875
+ function transformClassBody(node, ctx, skipFirst = false) {
2876
+ ctx.indentLevel++;
2877
+ const children = getChildren(node);
2878
+ const indent = " ".repeat(ctx.indentLevel);
2879
+ const members = [];
2880
+ let filteredChildren = children.filter((c) => c.name !== ":");
2881
+ if (skipFirst && filteredChildren.length > 0) {
2882
+ filteredChildren = filteredChildren.slice(1);
2883
+ }
2884
+ for (const child of filteredChildren) {
2885
+ if (child.name === "FunctionDefinition") {
2886
+ members.push(transformClassMethod(child, ctx, null));
2887
+ } else if (child.name === "DecoratedStatement") {
2888
+ members.push(transformClassDecoratedMethod(child, ctx));
2889
+ } else if (child.name === "AssignStatement") {
2890
+ const transformed = transformClassProperty(child, ctx, indent);
2891
+ if (transformed) {
2892
+ members.push(transformed);
2893
+ }
2894
+ } else if (child.name === "ExpressionStatement") {
2895
+ if (!isDocstringNode(child, ctx)) {
2896
+ const transformed = transformNode(child, ctx);
2897
+ if (transformed.trim()) {
2898
+ members.push(indent + transformed + ";");
2899
+ }
2900
+ }
2901
+ } else if (child.name === "PassStatement") {
2902
+ }
2903
+ }
2904
+ ctx.indentLevel--;
2905
+ return members.filter((m) => m.trim()).join("\n\n");
2906
+ }
2907
+ function transformClassProperty(node, ctx, indent) {
2908
+ const children = getChildren(node);
2909
+ if (children.length < 2) return "";
2910
+ const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
2911
+ const typeDef = children.find((c) => c.name === "TypeDef");
2912
+ if (assignOpIndex === -1) {
2913
+ const targets2 = children.filter((c) => c.name !== "," && c.name !== "TypeDef" && c.name !== ":");
2914
+ const target2 = targets2[0];
2915
+ if (!target2) return "";
2916
+ const targetCode2 = transformNode(target2, ctx);
2917
+ const tsType2 = extractTypeAnnotation(typeDef, ctx);
2918
+ const typeAnnotation2 = tsType2 ? `: ${tsType2}` : "";
2919
+ const modifiers2 = extractTypeModifiers(typeDef, ctx);
2920
+ let prefix2 = "";
2921
+ if (modifiers2.isClassVar) {
2922
+ prefix2 = "static ";
2923
+ }
2924
+ if (modifiers2.isFinal) {
2925
+ prefix2 += "readonly ";
2926
+ }
2927
+ return `${indent}${prefix2}${targetCode2}${typeAnnotation2};`;
2928
+ }
2929
+ const typeDefBeforeAssign = children.slice(0, assignOpIndex).find((c) => c.name === "TypeDef");
2930
+ const targets = children.slice(0, assignOpIndex).filter((c) => c.name !== "," && c.name !== "TypeDef");
2931
+ const values = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
2932
+ if (targets.length === 0 || values.length === 0) return "";
2933
+ const target = targets[0];
2934
+ if (!target) return "";
2935
+ const targetCode = transformNode(target, ctx);
2936
+ const tsType = extractTypeAnnotation(typeDefBeforeAssign, ctx);
2937
+ const typeAnnotation = tsType ? `: ${tsType}` : "";
2938
+ const modifiers = extractTypeModifiers(typeDefBeforeAssign, ctx);
2939
+ let prefix = "";
2940
+ if (modifiers.isClassVar) {
2941
+ prefix = "static ";
2942
+ }
2943
+ if (modifiers.isFinal) {
2944
+ prefix += "readonly ";
2945
+ }
2946
+ const value = values[0];
2947
+ const valueCode = value ? transformNode(value, ctx) : "";
2948
+ return `${indent}${prefix}${targetCode}${typeAnnotation} = ${valueCode};`;
2949
+ }
2950
+ function transformClassMethod(node, ctx, decorator) {
2951
+ const children = getChildren(node);
2952
+ const indent = " ".repeat(ctx.indentLevel);
2953
+ let methodName = "";
2954
+ let paramList = null;
2955
+ let body = null;
2956
+ for (const child of children) {
2957
+ if (child.name === "VariableName") {
2958
+ methodName = getNodeText(child, ctx.source);
2959
+ } else if (child.name === "ParamList") {
2960
+ paramList = child;
2961
+ } else if (child.name === "Body") {
2962
+ body = child;
2963
+ }
2964
+ }
2965
+ const { jsdoc, skipFirstStatement } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null, skipFirstStatement: false };
2966
+ const params = paramList ? transformMethodParamList(paramList, ctx) : "";
2967
+ const bodyCode = body ? transformClassMethodBody(body, ctx, skipFirstStatement) : "";
2968
+ const isGenerator = body ? containsYield(body) : false;
2969
+ const generatorStar = isGenerator ? "*" : "";
2970
+ if (methodName === "__init__") {
2971
+ const methodDecl2 = `${indent}constructor(${params}) {
2972
+ ${bodyCode}
2973
+ ${indent}}`;
2974
+ return jsdoc ? `${jsdoc}
2975
+ ${methodDecl2}` : methodDecl2;
2976
+ }
2977
+ if (methodName === "__str__" || methodName === "__repr__") {
2978
+ const methodDecl2 = `${indent}toString() {
2979
+ ${bodyCode}
2980
+ ${indent}}`;
2981
+ return jsdoc ? `${jsdoc}
2982
+ ${methodDecl2}` : methodDecl2;
2983
+ }
2984
+ let prefix = "";
2985
+ if (decorator === "staticmethod" || decorator === "classmethod") {
2986
+ prefix = "static ";
2987
+ } else if (decorator === "property") {
2988
+ prefix = "get ";
2989
+ } else if (decorator === "setter") {
2990
+ prefix = "set ";
2991
+ } else if (decorator === "abstractmethod") {
2992
+ const typedParams = paramList ? transformMethodParamListWithTypes(paramList, ctx) : "";
2993
+ const returnType = extractMethodReturnType(node, ctx);
2994
+ const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
2995
+ const methodDecl2 = `${indent}abstract ${methodName}(${typedParams})${returnTypeStr}`;
2996
+ return jsdoc ? `${jsdoc}
2997
+ ${methodDecl2}` : methodDecl2;
2998
+ }
2999
+ const methodDecl = `${indent}${prefix}${generatorStar}${methodName}(${params}) {
3000
+ ${bodyCode}
3001
+ ${indent}}`;
3002
+ return jsdoc ? `${jsdoc}
3003
+ ${methodDecl}` : methodDecl;
3004
+ }
3005
+ function extractMethodReturnType(node, ctx) {
3006
+ const children = getChildren(node);
3007
+ for (const child of children) {
3008
+ if (child.name === "TypeDef") {
3009
+ return extractTypeAnnotation(child, ctx);
3010
+ }
3011
+ }
3012
+ return null;
3013
+ }
3014
+ function transformClassDecoratedMethod(node, ctx) {
3015
+ const children = getChildren(node);
3016
+ let decorator = null;
3017
+ let funcDef = null;
3018
+ for (const child of children) {
3019
+ if (child.name === "Decorator") {
3020
+ const decChildren = getChildren(child);
3021
+ const varNames = decChildren.filter((c) => c.name === "VariableName");
3022
+ const hasDot = decChildren.some((c) => c.name === ".");
3023
+ if (varNames.length >= 2 && hasDot) {
3024
+ const propName = varNames[varNames.length - 1];
3025
+ if (propName) {
3026
+ decorator = getNodeText(propName, ctx.source);
3027
+ }
3028
+ } else {
3029
+ const nameNode = varNames[0];
3030
+ if (nameNode) {
3031
+ decorator = getNodeText(nameNode, ctx.source);
3032
+ }
3033
+ }
3034
+ } else if (child.name === "FunctionDefinition") {
3035
+ funcDef = child;
3036
+ }
3037
+ }
3038
+ if (!funcDef) {
3039
+ return getNodeText(node, ctx.source);
3040
+ }
3041
+ return transformClassMethod(funcDef, ctx, decorator);
3042
+ }
3043
+ function transformMethodParamList(node, ctx) {
3044
+ return transformMethodParamListImpl(node, ctx, false);
3045
+ }
3046
+ function transformMethodParamListWithTypes(node, ctx) {
3047
+ return transformMethodParamListImpl(node, ctx, true);
3048
+ }
3049
+ function transformMethodParamListImpl(node, ctx, includeTypes) {
3050
+ const children = getChildren(node);
3051
+ const params = [];
3052
+ let i = 0;
3053
+ let isFirstParam = true;
3054
+ while (i < children.length) {
3055
+ const child = children[i];
3056
+ if (!child) {
3057
+ i++;
3058
+ continue;
3059
+ }
3060
+ if (child.name === "(" || child.name === ")" || child.name === ",") {
3061
+ i++;
3062
+ continue;
3063
+ }
3064
+ if (child.name === "VariableName" && isFirstParam) {
3065
+ const name = getNodeText(child, ctx.source);
3066
+ if (name === "self" || name === "cls") {
3067
+ i++;
3068
+ isFirstParam = false;
3069
+ continue;
3070
+ }
3071
+ }
3072
+ isFirstParam = false;
3073
+ if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
3074
+ const nextChild = children[i + 1];
3075
+ if (nextChild && nextChild.name === "VariableName") {
3076
+ const name = getNodeText(nextChild, ctx.source);
3077
+ params.push(`...${name}`);
3078
+ i += 2;
3079
+ continue;
3080
+ }
3081
+ i++;
3082
+ continue;
3083
+ }
3084
+ if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
3085
+ const nextChild = children[i + 1];
3086
+ if (nextChild && nextChild.name === "VariableName") {
3087
+ const name = getNodeText(nextChild, ctx.source);
3088
+ params.push(name);
3089
+ i += 2;
3090
+ continue;
3091
+ }
3092
+ i++;
3093
+ continue;
3094
+ }
3095
+ if (child.name === "VariableName") {
3096
+ const nameCode = getNodeText(child, ctx.source);
3097
+ let typeStr = "";
3098
+ let defaultStr = "";
3099
+ let consumed = 1;
3100
+ const nextChild = children[i + 1];
3101
+ if (nextChild?.name === "TypeDef" && includeTypes) {
3102
+ const t = extractTypeAnnotation(nextChild, ctx);
3103
+ if (t) typeStr = `: ${t}`;
3104
+ consumed++;
3105
+ const afterType = children[i + 2];
3106
+ if (afterType?.name === "AssignOp") {
3107
+ const defaultVal = children[i + 3];
3108
+ if (defaultVal) {
3109
+ defaultStr = ` = ${transformNode(defaultVal, ctx)}`;
3110
+ consumed += 2;
3111
+ }
3112
+ }
3113
+ } else if (nextChild?.name === "AssignOp") {
3114
+ const defaultValChild = children[i + 2];
3115
+ if (defaultValChild) {
3116
+ defaultStr = ` = ${transformNode(defaultValChild, ctx)}`;
3117
+ consumed += 2;
3118
+ }
3119
+ }
3120
+ params.push(`${nameCode}${typeStr}${defaultStr}`);
3121
+ i += consumed;
3122
+ continue;
3123
+ }
3124
+ i++;
3125
+ }
3126
+ return params.join(", ");
3127
+ }
3128
+ function transformClassMethodBody(node, ctx, skipFirst = false) {
3129
+ ctx.indentLevel++;
3130
+ const children = getChildren(node);
3131
+ const indent = " ".repeat(ctx.indentLevel);
3132
+ let filteredChildren = children.filter((child) => child.name !== ":");
3133
+ if (skipFirst && filteredChildren.length > 0) {
3134
+ filteredChildren = filteredChildren.slice(1);
3135
+ }
3136
+ const statements = filteredChildren.map((child) => {
3137
+ let transformed;
3138
+ if (child.name === "AssignStatement") {
3139
+ transformed = transformClassAssignment(child, ctx);
3140
+ } else {
3141
+ transformed = transformNode(child, ctx);
3142
+ }
3143
+ if (transformed === "") {
3144
+ return "";
3145
+ }
3146
+ transformed = transformed.replace(/\bself\./g, "this.");
3147
+ transformed = transformed.replace(/\bcls\./g, "this.");
3148
+ transformed = transformed.replace(/super\(\)\.__init__\(/g, "super(");
3149
+ transformed = transformed.replace(/\bcls\(\)/g, "new this()");
3150
+ if (child.name === "ExpressionStatement" || child.name === "AssignStatement" || child.name === "PassStatement" || child.name === "BreakStatement" || child.name === "ContinueStatement" || child.name === "ReturnStatement") {
3151
+ return indent + transformed + ";";
3152
+ }
3153
+ return indent + transformed;
3154
+ }).filter((s) => s.trim() !== "");
3155
+ ctx.indentLevel--;
3156
+ return statements.join("\n");
3157
+ }
3158
+ function transformClassAssignment(node, ctx) {
3159
+ const children = getChildren(node);
3160
+ if (children.length < 3) return getNodeText(node, ctx.source);
3161
+ const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
3162
+ if (assignOpIndex === -1) return getNodeText(node, ctx.source);
3163
+ const targets = children.slice(0, assignOpIndex).filter((c) => c.name !== ",");
3164
+ const values = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
3165
+ if (targets.length === 0 || values.length === 0) {
3166
+ return getNodeText(node, ctx.source);
3167
+ }
3168
+ if (targets.length === 1) {
3169
+ const target = targets[0];
3170
+ if (!target) return getNodeText(node, ctx.source);
3171
+ const targetCode = transformNode(target, ctx);
3172
+ const isMemberAssignment = target.name === "MemberExpression";
3173
+ if (values.length === 1) {
3174
+ const value = values[0];
3175
+ if (!value) return getNodeText(node, ctx.source);
3176
+ if (isMemberAssignment) {
3177
+ return `${targetCode} = ${transformNode(value, ctx)}`;
3178
+ }
3179
+ return `let ${targetCode} = ${transformNode(value, ctx)}`;
3180
+ } else {
3181
+ const valuesCodes = values.map((v) => transformNode(v, ctx));
3182
+ if (isMemberAssignment) {
3183
+ return `${targetCode} = [${valuesCodes.join(", ")}]`;
3184
+ }
3185
+ return `let ${targetCode} = [${valuesCodes.join(", ")}]`;
3186
+ }
3187
+ }
3188
+ const targetCodes = targets.map((t) => transformAssignTarget(t, ctx));
3189
+ const targetPattern = `[${targetCodes.join(", ")}]`;
3190
+ if (values.length === 1) {
3191
+ const value = values[0];
3192
+ if (!value) return getNodeText(node, ctx.source);
3193
+ return `let ${targetPattern} = ${transformNode(value, ctx)}`;
3194
+ } else {
3195
+ const valuesCodes = values.map((v) => transformNode(v, ctx));
3196
+ return `let ${targetPattern} = [${valuesCodes.join(", ")}]`;
3197
+ }
3198
+ }
3199
+ function transformDecoratedStatement(node, ctx) {
3200
+ const children = getChildren(node);
3201
+ const decorators = [];
3202
+ let funcDef = null;
3203
+ let classDef = null;
3204
+ for (const child of children) {
3205
+ if (child.name === "Decorator") {
3206
+ const decChildren = getChildren(child);
3207
+ let decoratorName = "";
3208
+ let decoratorArgs = null;
3209
+ const nameParts = [];
3210
+ for (const decChild of decChildren) {
3211
+ if (decChild.name === "VariableName") {
3212
+ nameParts.push(getNodeText(decChild, ctx.source));
3213
+ } else if (decChild.name === "MemberExpression") {
3214
+ decoratorName = transformNode(decChild, ctx);
3215
+ } else if (decChild.name === "ArgList") {
3216
+ decoratorArgs = transformArgList(decChild, ctx);
3217
+ } else if (decChild.name === "CallExpression") {
3218
+ const callChildren = getChildren(decChild);
3219
+ for (const callChild of callChildren) {
3220
+ if (callChild.name === "VariableName") {
3221
+ nameParts.push(getNodeText(callChild, ctx.source));
3222
+ } else if (callChild.name === "MemberExpression") {
3223
+ decoratorName = transformNode(callChild, ctx);
3224
+ } else if (callChild.name === "ArgList") {
3225
+ decoratorArgs = transformArgList(callChild, ctx);
3226
+ }
3227
+ }
3228
+ }
3229
+ }
3230
+ if (nameParts.length > 0) {
3231
+ decoratorName = nameParts.join(".");
3232
+ }
3233
+ if (decoratorName) {
3234
+ decorators.push({ name: decoratorName, args: decoratorArgs });
3235
+ }
3236
+ } else if (child.name === "FunctionDefinition") {
3237
+ funcDef = child;
3238
+ } else if (child.name === "ClassDefinition") {
3239
+ classDef = child;
3240
+ }
3241
+ }
3242
+ if (classDef) {
3243
+ return transformDecoratedClass(classDef, decorators, ctx);
3244
+ }
3245
+ if (!funcDef) {
3246
+ return getNodeText(node, ctx.source);
3247
+ }
3248
+ const funcChildren = getChildren(funcDef);
3249
+ let funcName = "";
3250
+ let paramList = null;
3251
+ let body = null;
3252
+ for (const child of funcChildren) {
3253
+ if (child.name === "VariableName") {
3254
+ funcName = getNodeText(child, ctx.source);
3255
+ } else if (child.name === "ParamList") {
3256
+ paramList = child;
3257
+ } else if (child.name === "Body") {
3258
+ body = child;
3259
+ }
3260
+ }
3261
+ const params = paramList ? transformParamList(paramList, ctx) : "";
3262
+ if (decorators.length === 1 && decorators[0]?.name === "overload") {
3263
+ const returnType = extractMethodReturnType(funcDef, ctx);
3264
+ const returnTypeStr = returnType ? `: ${returnType === "null" ? "void" : returnType}` : "";
3265
+ return `function ${funcName}(${params})${returnTypeStr}`;
3266
+ }
3267
+ const bodyCode = body ? transformBody(body, ctx) : "";
3268
+ let funcExpr = `function ${funcName}(${params}) {
3269
+ ${bodyCode}
3270
+ }`;
3271
+ for (let i = decorators.length - 1; i >= 0; i--) {
3272
+ const dec = decorators[i];
3273
+ if (!dec) continue;
3274
+ if (dec.args !== null) {
3275
+ funcExpr = `${dec.name}(${dec.args})(${funcExpr})`;
3276
+ } else {
3277
+ funcExpr = `${dec.name}(${funcExpr})`;
3278
+ }
3279
+ }
3280
+ return `const ${funcName} = ${funcExpr}`;
3281
+ }
3282
+ function transformDecoratedClass(classDef, decorators, ctx) {
3283
+ const dataclassDecorator = decorators.find(
3284
+ (d) => d.name === "dataclass" || d.name === "dataclasses.dataclass"
3285
+ );
3286
+ if (dataclassDecorator) {
3287
+ const otherDecorators = decorators.filter((d) => d !== dataclassDecorator);
3288
+ const dataclassCode = transformDataclass(classDef, dataclassDecorator, ctx);
3289
+ if (otherDecorators.length === 0) {
3290
+ return dataclassCode;
3291
+ }
3292
+ const children = getChildren(classDef);
3293
+ let className = "";
3294
+ for (const child of children) {
3295
+ if (child.name === "VariableName") {
3296
+ className = getNodeText(child, ctx.source);
3297
+ break;
3298
+ }
3299
+ }
3300
+ let expr = dataclassCode;
3301
+ for (let i = otherDecorators.length - 1; i >= 0; i--) {
3302
+ const dec = otherDecorators[i];
3303
+ if (!dec) continue;
3304
+ if (dec.args !== null) {
3305
+ expr = `${dec.name}(${dec.args})(${expr})`;
3306
+ } else {
3307
+ expr = `${dec.name}(${expr})`;
3308
+ }
3309
+ }
3310
+ return `const ${className} = ${expr}`;
3311
+ }
3312
+ return transformGenericDecoratedClass(classDef, decorators, ctx);
3313
+ }
3314
+ function transformGenericDecoratedClass(classDef, decorators, ctx) {
3315
+ const children = getChildren(classDef);
3316
+ let className = "";
3317
+ for (const child of children) {
3318
+ if (child.name === "VariableName") {
3319
+ className = getNodeText(child, ctx.source);
3320
+ ctx.definedClasses.add(className);
3321
+ break;
3322
+ }
3323
+ }
3324
+ let classExpr = transformClassDefinition(classDef, ctx);
3325
+ for (let i = decorators.length - 1; i >= 0; i--) {
3326
+ const dec = decorators[i];
3327
+ if (!dec) continue;
3328
+ if (dec.args !== null) {
3329
+ classExpr = `${dec.name}(${dec.args})(${classExpr})`;
3330
+ } else {
3331
+ classExpr = `${dec.name}(${classExpr})`;
3332
+ }
3333
+ }
3334
+ return `const ${className} = ${classExpr}`;
3335
+ }
3336
+ function parseDataclassOptions(args) {
3337
+ const options = {
3338
+ frozen: false
3339
+ };
3340
+ if (!args) return options;
3341
+ if (args.includes("frozen: true") || args.includes("frozen=True") || args.includes("frozen=true")) {
3342
+ options.frozen = true;
3343
+ }
3344
+ return options;
3345
+ }
3346
+ function extractDataclassFields(body, ctx) {
3347
+ const fields = [];
3348
+ const children = getChildren(body);
3349
+ for (const child of children) {
3350
+ if (child.name === ":") continue;
3351
+ if (child.name === "AssignStatement") {
3352
+ const field = parseDataclassFieldFromAssignment(child, ctx);
3353
+ if (field) fields.push(field);
3354
+ } else if (child.name === "ExpressionStatement") {
3355
+ const field = parseDataclassFieldFromExpression(child, ctx);
3356
+ if (field) fields.push(field);
3357
+ }
3358
+ }
3359
+ return fields;
3360
+ }
3361
+ function parseDataclassFieldFromAssignment(node, ctx) {
3362
+ const children = getChildren(node);
3363
+ let varName = null;
3364
+ let typeDef = null;
3365
+ let assignOpIndex = -1;
3366
+ for (let i = 0; i < children.length; i++) {
3367
+ const child = children[i];
3368
+ if (!child) continue;
3369
+ if (child.name === "VariableName" && !varName) {
3370
+ varName = child;
3371
+ } else if (child.name === "TypeDef") {
3372
+ typeDef = child;
3373
+ } else if (child.name === "AssignOp" || child.name === "=") {
3374
+ assignOpIndex = i;
3375
+ }
3376
+ }
3377
+ if (!varName || !typeDef) {
3378
+ return null;
3379
+ }
3380
+ const name = getNodeText(varName, ctx.source);
3381
+ const tsType = extractTypeAnnotation(typeDef, ctx) ?? "unknown";
3382
+ let hasDefault = false;
3383
+ let defaultValue = null;
3384
+ let isFieldFactory = false;
3385
+ if (assignOpIndex !== -1) {
3386
+ hasDefault = true;
3387
+ const valueNodes = children.slice(assignOpIndex + 1).filter((c) => c.name !== ",");
3388
+ const firstValue = valueNodes[0];
3389
+ if (firstValue) {
3390
+ if (firstValue.name === "CallExpression") {
3391
+ const callText = getNodeText(firstValue, ctx.source);
3392
+ if (callText.startsWith("field(")) {
3393
+ isFieldFactory = true;
3394
+ defaultValue = parseFieldDefaultFactory(firstValue, ctx);
3395
+ } else {
3396
+ defaultValue = transformNode(firstValue, ctx);
3397
+ }
3398
+ } else {
3399
+ defaultValue = transformNode(firstValue, ctx);
3400
+ }
3401
+ }
3402
+ }
3403
+ return { name, tsType, hasDefault, defaultValue, isFieldFactory };
3404
+ }
3405
+ function parseDataclassFieldFromExpression(node, ctx) {
3406
+ const children = getChildren(node);
3407
+ for (const child of children) {
3408
+ if (child.name === "VariableName") {
3409
+ const typeDef = children.find((c) => c.name === "TypeDef");
3410
+ if (typeDef) {
3411
+ const name = getNodeText(child, ctx.source);
3412
+ const tsType = extractTypeAnnotation(typeDef, ctx) ?? "unknown";
3413
+ return { name, tsType, hasDefault: false, defaultValue: null, isFieldFactory: false };
3414
+ }
3415
+ }
3416
+ }
3417
+ return null;
3418
+ }
3419
+ function parseFieldDefaultFactory(callNode, ctx) {
3420
+ const text = getNodeText(callNode, ctx.source);
3421
+ const factoryMatch = text.match(/default_factory\s*=\s*(\w+)/);
3422
+ if (factoryMatch) {
3423
+ const factory = factoryMatch[1];
3424
+ if (factory === "list") return "[]";
3425
+ if (factory === "dict") return "{}";
3426
+ if (factory === "set") return "new Set()";
3427
+ if (factory) return `${factory}()`;
3428
+ }
3429
+ const defaultMatch = text.match(/default\s*=\s*([^,)]+)/);
3430
+ if (defaultMatch) {
3431
+ return defaultMatch[1]?.trim() ?? "undefined";
3432
+ }
3433
+ return "undefined";
3434
+ }
3435
+ function transformDataclass(classDef, decorator, ctx) {
3436
+ const children = getChildren(classDef);
3437
+ let className = "";
3438
+ let body = null;
3439
+ const parentClasses = [];
3440
+ for (const child of children) {
3441
+ if (child.name === "VariableName") {
3442
+ className = getNodeText(child, ctx.source);
3443
+ ctx.definedClasses.add(className);
3444
+ } else if (child.name === "ArgList") {
3445
+ const argChildren = getChildren(child);
3446
+ const parentNodes = argChildren.filter((c) => c.name === "VariableName");
3447
+ for (const parentNode of parentNodes) {
3448
+ parentClasses.push(getNodeText(parentNode, ctx.source));
3449
+ }
3450
+ } else if (child.name === "Body") {
3451
+ body = child;
3452
+ }
3453
+ }
3454
+ const options = parseDataclassOptions(decorator.args);
3455
+ const fields = body ? extractDataclassFields(body, ctx) : [];
3456
+ const indent = " ".repeat(ctx.indentLevel);
3457
+ const { jsdoc } = body ? extractDocstringFromBody(body, ctx, indent) : { jsdoc: null };
3458
+ return generateDataclassCode(className, parentClasses, fields, options, body, ctx, jsdoc);
3459
+ }
3460
+ function generateDataclassCode(className, parentClasses, fields, options, body, ctx, jsdoc) {
3461
+ const memberIndent = " ";
3462
+ const bodyIndent = " ";
3463
+ const members = [];
3464
+ for (const field of fields) {
3465
+ const readonly = options.frozen ? "readonly " : "";
3466
+ if (field.hasDefault && field.defaultValue !== null && !field.isFieldFactory) {
3467
+ members.push(
3468
+ `${memberIndent}${readonly}${field.name}: ${field.tsType} = ${field.defaultValue};`
3469
+ );
3470
+ } else {
3471
+ members.push(`${memberIndent}${readonly}${field.name}: ${field.tsType};`);
3472
+ }
3473
+ }
3474
+ const constructorParams = fields.map((f) => {
3475
+ if (f.hasDefault && f.defaultValue !== null) {
3476
+ return `${f.name}: ${f.tsType} = ${f.defaultValue}`;
3477
+ }
3478
+ return `${f.name}: ${f.tsType}`;
3479
+ }).join(", ");
3480
+ const constructorAssignments = fields.map((f) => `${bodyIndent}this.${f.name} = ${f.name};`).join("\n");
3481
+ let constructorBody = "";
3482
+ if (parentClasses.length > 0) {
3483
+ constructorBody += `${bodyIndent}super();
3484
+ `;
3485
+ }
3486
+ constructorBody += constructorAssignments;
3487
+ if (options.frozen) {
3488
+ constructorBody += `
3489
+ ${bodyIndent}Object.freeze(this);`;
3490
+ }
3491
+ members.push(
3492
+ `
3493
+ ${memberIndent}constructor(${constructorParams}) {
3494
+ ${constructorBody}
3495
+ ${memberIndent}}`
3496
+ );
3497
+ if (body) {
3498
+ const methodMembers = extractNonFieldMembers(body, ctx, fields);
3499
+ if (methodMembers.length > 0) {
3500
+ members.push(...methodMembers);
3501
+ }
3502
+ }
3503
+ let classHeader = `class ${className}`;
3504
+ const firstParent = parentClasses[0];
3505
+ if (firstParent) {
3506
+ classHeader += ` extends ${firstParent}`;
3507
+ }
3508
+ const classCode = `${classHeader} {
3509
+ ${members.join("\n")}
3510
+ }`;
3511
+ if (jsdoc) {
3512
+ return `${jsdoc}
3513
+ ${classCode}`;
3514
+ }
3515
+ return classCode;
3516
+ }
3517
+ function extractNonFieldMembers(body, ctx, fields) {
3518
+ const members = [];
3519
+ const children = getChildren(body);
3520
+ const fieldNames = new Set(fields.map((f) => f.name));
3521
+ let skipFirst = false;
3522
+ const firstChild = children.find((c) => c.name !== ":");
3523
+ if (firstChild && isDocstringNode(firstChild, ctx)) {
3524
+ skipFirst = true;
3525
+ }
3526
+ let isFirst = true;
3527
+ for (const child of children) {
3528
+ if (child.name === ":") continue;
3529
+ if (isFirst && skipFirst) {
3530
+ isFirst = false;
3531
+ continue;
3532
+ }
3533
+ isFirst = false;
3534
+ if (child.name === "AssignStatement" || child.name === "ExpressionStatement") {
3535
+ const childChildren = getChildren(child);
3536
+ const varName = childChildren.find((c) => c.name === "VariableName");
3537
+ if (varName && fieldNames.has(getNodeText(varName, ctx.source))) {
3538
+ continue;
3539
+ }
3540
+ const hasTypeDef = childChildren.some((c) => c.name === "TypeDef");
3541
+ if (hasTypeDef) {
3542
+ continue;
3543
+ }
3544
+ }
3545
+ if (child.name === "FunctionDefinition") {
3546
+ const methodName = getMethodName(child, ctx);
3547
+ if (methodName !== "__init__") {
3548
+ ctx.indentLevel++;
3549
+ members.push("\n" + transformClassMethod(child, ctx, null));
3550
+ ctx.indentLevel--;
3551
+ }
3552
+ } else if (child.name === "DecoratedStatement") {
3553
+ ctx.indentLevel++;
3554
+ members.push("\n" + transformClassDecoratedMethod(child, ctx));
3555
+ ctx.indentLevel--;
3556
+ }
3557
+ }
3558
+ return members;
3559
+ }
3560
+ function getMethodName(node, ctx) {
3561
+ const children = getChildren(node);
3562
+ for (const child of children) {
3563
+ if (child.name === "VariableName") {
3564
+ return getNodeText(child, ctx.source);
3565
+ }
3566
+ }
3567
+ return "";
3568
+ }
3569
+ function transformNamedTuple(className, body, ctx) {
3570
+ const fields = body ? extractDataclassFields(body, ctx) : [];
3571
+ return generateDataclassCode(
3572
+ className,
3573
+ [],
3574
+ // no parent classes in output
3575
+ fields,
3576
+ { frozen: true },
3577
+ // always frozen
3578
+ body,
3579
+ ctx,
3580
+ null
3581
+ // jsdoc
3582
+ );
3583
+ }
3584
+ function transformEnum(className, enumType, body, ctx) {
3585
+ const members = extractEnumMembers(body, ctx);
3586
+ if (members.length === 0) {
3587
+ return `type ${className} = never`;
3588
+ }
3589
+ const allStrings = members.every((m) => m.isString);
3590
+ if (enumType === "StrEnum" || allStrings) {
3591
+ return generateStringUnionEnum(className, members);
3592
+ }
3593
+ const isSequential = checkSequentialEnum(members);
3594
+ if (isSequential) {
3595
+ return generateNameUnionEnum(className, members);
3596
+ }
3597
+ return generateConstObjectEnum(className, members);
3598
+ }
3599
+ function extractEnumMembers(body, ctx) {
3600
+ if (!body) return [];
3601
+ const members = [];
3602
+ const children = getChildren(body);
3603
+ for (const child of children) {
3604
+ if (child.name === "AssignStatement") {
3605
+ const member = parseEnumMember(child, ctx);
3606
+ if (member) {
3607
+ members.push(member);
3608
+ }
3609
+ }
3610
+ }
3611
+ return members;
3612
+ }
3613
+ function parseEnumMember(node, ctx) {
3614
+ const children = getChildren(node);
3615
+ let name = "";
3616
+ let value = "";
3617
+ let isString = false;
3618
+ let numericValue = null;
3619
+ for (const child of children) {
3620
+ if (child.name === "VariableName" && !name) {
3621
+ name = getNodeText(child, ctx.source);
3622
+ } else if (child.name === "Number") {
3623
+ value = getNodeText(child, ctx.source);
3624
+ numericValue = parseFloat(value);
3625
+ isString = false;
3626
+ } else if (child.name === "String") {
3627
+ const rawValue = getNodeText(child, ctx.source);
3628
+ value = rawValue.slice(1, -1);
3629
+ isString = true;
3630
+ } else if (child.name === "CallExpression") {
3631
+ const callText = getNodeText(child, ctx.source);
3632
+ if (callText === "auto()") {
3633
+ value = "auto";
3634
+ numericValue = null;
3635
+ isString = false;
3636
+ } else {
3637
+ value = callText;
3638
+ isString = false;
3639
+ }
3640
+ }
3641
+ }
3642
+ if (!name) return null;
3643
+ return { name, value, isString, numericValue };
3644
+ }
3645
+ function checkSequentialEnum(members) {
3646
+ if (members.length === 0) return true;
3647
+ if (members.some((m) => m.value === "auto")) return true;
3648
+ const numericMembers = members.filter((m) => m.numericValue !== null);
3649
+ if (numericMembers.length !== members.length) return false;
3650
+ const values = numericMembers.map((m) => m.numericValue);
3651
+ const firstValue = values[0];
3652
+ if (firstValue === void 0) return false;
3653
+ for (let i = 1; i < values.length; i++) {
3654
+ if (values[i] !== firstValue + i) return false;
3655
+ }
3656
+ return true;
3657
+ }
3658
+ function generateStringUnionEnum(className, members) {
3659
+ const values = members.map((m) => `"${m.value}"`).join(" | ");
3660
+ return `type ${className} = ${values}`;
3661
+ }
3662
+ function generateNameUnionEnum(className, members) {
3663
+ const names = members.map((m) => `"${m.name}"`).join(" | ");
3664
+ return `type ${className} = ${names}`;
3665
+ }
3666
+ function generateConstObjectEnum(className, members) {
3667
+ const entries = members.map((m) => ` ${m.name}: ${m.value}`).join(",\n");
3668
+ return `const ${className} = {
3669
+ ${entries}
3670
+ } as const
3671
+ type ${className} = typeof ${className}[keyof typeof ${className}]`;
3672
+ }
3673
+ function checkTypedDictTotalFalse(node, ctx) {
3674
+ const children = getChildren(node);
3675
+ for (const child of children) {
3676
+ if (child.name === "ArgList") {
3677
+ const argText = getNodeText(child, ctx.source);
3678
+ if (argText.includes("total=False") || argText.includes("total: False")) {
3679
+ return true;
3680
+ }
3681
+ }
3682
+ }
3683
+ return false;
3684
+ }
3685
+ function transformTypedDict(className, body, totalFalse, ctx) {
3686
+ const fields = body ? extractDataclassFields(body, ctx) : [];
3687
+ const memberIndent = " ";
3688
+ const members = fields.map((f) => {
3689
+ const optional = totalFalse ? "?" : "";
3690
+ return `${memberIndent}${f.name}${optional}: ${f.tsType}`;
3691
+ });
3692
+ if (members.length === 0) {
3693
+ return `interface ${className} {}`;
3694
+ }
3695
+ return `interface ${className} {
3696
+ ${members.join("\n")}
3697
+ }`;
3698
+ }
3699
+ function transformAbstractClass(className, parentClasses, body, ctx, jsdoc) {
3700
+ const filteredParents = parentClasses.filter((p) => p !== "ABC");
3701
+ let classHeader = `abstract class ${className}`;
3702
+ const firstParent = filteredParents[0];
3703
+ if (firstParent) {
3704
+ classHeader += ` extends ${firstParent}`;
3705
+ }
3706
+ ctx.isAbstractClass = true;
3707
+ const bodyCode = body ? transformClassBody(body, ctx, false) : "";
3708
+ ctx.isAbstractClass = false;
3709
+ const classDecl = `${classHeader} {
3710
+ ${bodyCode}
3711
+ }`;
3712
+ return jsdoc ? `${jsdoc}
3713
+ ${classDecl}` : classDecl;
3714
+ }
3715
+ function transformProtocol(className, parentClasses, body, ctx) {
3716
+ const memberIndent = " ";
3717
+ const members = [];
3718
+ const genericParams = extractGenericParams(parentClasses);
3719
+ const genericStr = genericParams.length > 0 ? `<${genericParams.join(", ")}>` : "";
3720
+ const otherParents = parentClasses.filter((p) => p !== "Protocol" && !p.startsWith("Generic["));
3721
+ if (body) {
3722
+ const children = getChildren(body);
3723
+ for (const child of children) {
3724
+ if (child.name === ":") continue;
3725
+ if (child.name === "PassStatement") continue;
3726
+ if (child.name === "FunctionDefinition") {
3727
+ const sig = extractProtocolMethodSignature(child, ctx);
3728
+ if (sig) {
3729
+ members.push(`${memberIndent}${sig}`);
3730
+ }
3731
+ } else if (child.name === "ExpressionStatement" || child.name === "AssignStatement") {
3732
+ const field = parseDataclassFieldFromExpression(child, ctx) || parseDataclassFieldFromAssignment(child, ctx);
3733
+ if (field) {
3734
+ members.push(`${memberIndent}${field.name}: ${field.tsType}`);
3735
+ }
3736
+ }
3737
+ }
3738
+ }
3739
+ let header = `interface ${className}${genericStr}`;
3740
+ if (otherParents.length > 0) {
3741
+ header += ` extends ${otherParents.join(", ")}`;
3742
+ }
3743
+ if (members.length === 0) {
3744
+ return `${header} {}`;
3745
+ }
3746
+ return `${header} {
3747
+ ${members.join("\n")}
3748
+ }`;
3749
+ }
3750
+ function extractProtocolMethodSignature(node, ctx) {
3751
+ const children = getChildren(node);
3752
+ let methodName = "";
3753
+ let params = [];
3754
+ let returnType = "void";
3755
+ for (const child of children) {
3756
+ if (child.name === "def") continue;
3757
+ if (child.name === "VariableName" && !methodName) {
3758
+ methodName = getNodeText(child, ctx.source);
3759
+ } else if (child.name === "ParamList") {
3760
+ params = extractProtocolParams(child, ctx);
3761
+ } else if (child.name === "TypeDef") {
3762
+ const rt = extractTypeAnnotation(child, ctx);
3763
+ if (rt) returnType = rt === "null" ? "void" : rt;
3764
+ }
3765
+ }
3766
+ if (!methodName || methodName === "__init__") return null;
3767
+ return `${methodName}(${params.join(", ")}): ${returnType}`;
3768
+ }
3769
+ function extractProtocolParams(node, ctx) {
3770
+ const children = getChildren(node);
3771
+ const params = [];
3772
+ let i = 0;
3773
+ while (i < children.length) {
3774
+ const child = children[i];
3775
+ if (!child || child.name === "(" || child.name === ")" || child.name === ",") {
3776
+ i++;
3777
+ continue;
3778
+ }
3779
+ if (child.name === "VariableName") {
3780
+ const paramName = getNodeText(child, ctx.source);
3781
+ if (paramName === "self" || paramName === "cls") {
3782
+ i++;
3783
+ continue;
3784
+ }
3785
+ let paramType = "unknown";
3786
+ const nextChild = children[i + 1];
3787
+ if (nextChild?.name === "TypeDef") {
3788
+ const t = extractTypeAnnotation(nextChild, ctx);
3789
+ if (t) paramType = t;
3790
+ i++;
3791
+ }
3792
+ params.push(`${paramName}: ${paramType}`);
3793
+ }
3794
+ i++;
3795
+ }
3796
+ return params;
3797
+ }
3798
+ function extractGenericParams(parentClasses) {
3799
+ for (const parent of parentClasses) {
3800
+ if (parent.startsWith("Generic[") && parent.endsWith("]")) {
3801
+ const inner = parent.slice(8, -1);
3802
+ return inner.split(",").map((p) => p.trim());
3803
+ }
3804
+ }
3805
+ return [];
3806
+ }
3807
+ function transformGenericClass(className, genericParams, parentClasses, body, ctx, jsdoc) {
3808
+ const genericStr = `<${genericParams.join(", ")}>`;
3809
+ let classHeader = `class ${className}${genericStr}`;
3810
+ const firstParent = parentClasses[0];
3811
+ if (firstParent) {
3812
+ classHeader += ` extends ${firstParent}`;
3813
+ }
3814
+ const bodyCode = body ? transformClassBody(body, ctx, false) : "";
3815
+ const classDecl = `${classHeader} {
3816
+ ${bodyCode}
3817
+ }`;
3818
+ return jsdoc ? `${jsdoc}
3819
+ ${classDecl}` : classDecl;
3820
+ }
3821
+ function transformParamList(node, ctx) {
3822
+ const children = getChildren(node);
3823
+ const params = [];
3824
+ let restParam = null;
3825
+ let kwargsParam = null;
3826
+ let i = 0;
3827
+ while (i < children.length) {
3828
+ const child = children[i];
3829
+ if (!child) {
3830
+ i++;
3831
+ continue;
3832
+ }
3833
+ if (child.name === "(" || child.name === ")" || child.name === ",") {
3834
+ i++;
3835
+ continue;
3836
+ }
3837
+ if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
3838
+ const nextChild = children[i + 1];
3839
+ if (nextChild && nextChild.name === "VariableName") {
3840
+ const name = getNodeText(nextChild, ctx.source);
3841
+ const typeChild = children[i + 2];
3842
+ if (typeChild && typeChild.name === "TypeDef") {
3843
+ const tsType = extractTypeAnnotation(typeChild, ctx);
3844
+ restParam = tsType ? `...${name}: ${tsType}[]` : `...${name}`;
3845
+ i += 3;
3846
+ } else {
3847
+ restParam = `...${name}`;
3848
+ i += 2;
3849
+ }
3850
+ continue;
3851
+ }
3852
+ i++;
3853
+ continue;
3854
+ }
3855
+ if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
3856
+ const nextChild = children[i + 1];
3857
+ if (nextChild && nextChild.name === "VariableName") {
3858
+ const name = getNodeText(nextChild, ctx.source);
3859
+ kwargsParam = name;
3860
+ i += 2;
3861
+ continue;
3862
+ }
3863
+ i++;
3864
+ continue;
3865
+ }
3866
+ if (child.name === "VariableName") {
3867
+ const nameCode = getNodeText(child, ctx.source);
3868
+ let typeAnnotation = "";
3869
+ let offset = 1;
3870
+ const nextChild = children[i + 1];
3871
+ if (nextChild && nextChild.name === "TypeDef") {
3872
+ const tsType = extractTypeAnnotation(nextChild, ctx);
3873
+ if (tsType) {
3874
+ typeAnnotation = `: ${tsType}`;
3875
+ }
3876
+ offset = 2;
3877
+ }
3878
+ const afterType = children[i + offset];
3879
+ if (afterType && afterType.name === "AssignOp") {
3880
+ const defaultValChild = children[i + offset + 1];
3881
+ if (defaultValChild) {
3882
+ const defaultCode = transformNode(defaultValChild, ctx);
3883
+ params.push(`${nameCode}${typeAnnotation} = ${defaultCode}`);
3884
+ i += offset + 2;
3885
+ continue;
3886
+ }
3887
+ }
3888
+ params.push(`${nameCode}${typeAnnotation}`);
3889
+ i += offset;
3890
+ continue;
3891
+ }
3892
+ if (child.name === "AssignParam" || child.name === "DefaultParam") {
3893
+ const paramChildren = getChildren(child);
3894
+ const name = paramChildren.find((c) => c.name === "VariableName");
3895
+ const typeDef = paramChildren.find((c) => c.name === "TypeDef");
3896
+ const defaultVal = paramChildren[paramChildren.length - 1];
3897
+ if (name) {
3898
+ const nameCode = getNodeText(name, ctx.source);
3899
+ const tsType = extractTypeAnnotation(typeDef, ctx);
3900
+ const typeAnnotation = tsType ? `: ${tsType}` : "";
3901
+ if (defaultVal && name !== defaultVal && defaultVal.name !== "TypeDef") {
3902
+ const defaultCode = transformNode(defaultVal, ctx);
3903
+ params.push(`${nameCode}${typeAnnotation} = ${defaultCode}`);
3904
+ } else {
3905
+ params.push(`${nameCode}${typeAnnotation}`);
3906
+ }
3907
+ }
3908
+ i++;
3909
+ continue;
3910
+ }
3911
+ i++;
3912
+ }
3913
+ if (kwargsParam && !restParam) {
3914
+ const kwargsType = ": Record<string, unknown>";
3915
+ params.push(`${kwargsParam}${kwargsType} = {}`);
3916
+ }
3917
+ if (restParam) {
3918
+ params.push(restParam);
3919
+ }
3920
+ return params.join(", ");
3921
+ }
3922
+ function transformComment(node, ctx) {
3923
+ const text = getNodeText(node, ctx.source);
3924
+ return "//" + text.slice(1);
3925
+ }
3926
+ function transformLambdaExpression(node, ctx) {
3927
+ const children = getChildren(node);
3928
+ let params = "";
3929
+ let body = null;
3930
+ let foundLambda = false;
3931
+ let foundColon = false;
3932
+ const paramNodes = [];
3933
+ for (const child of children) {
3934
+ const text = getNodeText(child, ctx.source);
3935
+ if (child.name === "lambda" || child.name === "Keyword" && text === "lambda") {
3936
+ foundLambda = true;
3937
+ continue;
3938
+ }
3939
+ if (child.name === ":") {
3940
+ foundColon = true;
3941
+ continue;
3942
+ }
3943
+ if (foundLambda && !foundColon) {
3944
+ if (child.name === "ParamList") {
3945
+ params = transformParamList(child, ctx);
3946
+ } else if (child.name === "VariableName") {
3947
+ paramNodes.push(child);
3948
+ } else if (child.name !== ",") {
3949
+ paramNodes.push(child);
3950
+ }
3951
+ } else if (foundColon) {
3952
+ body = child;
3953
+ break;
3954
+ }
3955
+ }
3956
+ if (!params && paramNodes.length > 0) {
3957
+ params = paramNodes.map((p) => getNodeText(p, ctx.source)).join(", ");
3958
+ }
3959
+ const bodyCode = body ? transformNode(body, ctx) : "";
3960
+ if (params) {
3961
+ return `(${params}) => ${bodyCode}`;
3962
+ }
3963
+ return `() => ${bodyCode}`;
3964
+ }
3965
+ function parseComprehensionClauses(children, ctx) {
3966
+ const items = children.filter(
3967
+ (c) => c.name !== "[" && c.name !== "]" && c.name !== "{" && c.name !== "}"
3968
+ );
3969
+ if (items.length === 0) {
3970
+ return { outputExpr: "", clauses: [] };
3971
+ }
3972
+ const outputNode = items[0];
3973
+ if (!outputNode) {
3974
+ return { outputExpr: "", clauses: [] };
3975
+ }
3976
+ const outputExpr = transformNode(outputNode, ctx);
3977
+ const clauses = [];
3978
+ let i = 1;
3979
+ while (i < items.length) {
3980
+ const item = items[i];
3981
+ if (!item) {
3982
+ i++;
3983
+ continue;
3984
+ }
3985
+ if (item.name === "for" || item.name === "Keyword" && getNodeText(item, ctx.source) === "for") {
3986
+ const varNode = items[i + 1];
3987
+ const iterableNode = items[i + 3];
3988
+ if (varNode && iterableNode) {
3989
+ clauses.push({
3990
+ type: "for",
3991
+ variable: transformNode(varNode, ctx),
3992
+ iterable: transformNode(iterableNode, ctx)
3993
+ });
3994
+ i += 4;
3995
+ } else {
3996
+ i++;
3997
+ }
3998
+ } else if (item.name === "if" || item.name === "Keyword" && getNodeText(item, ctx.source) === "if") {
3999
+ const conditionNode = items[i + 1];
4000
+ if (conditionNode) {
4001
+ clauses.push({
4002
+ type: "if",
4003
+ condition: transformNode(conditionNode, ctx)
4004
+ });
4005
+ i += 2;
4006
+ } else {
4007
+ i++;
4008
+ }
4009
+ } else if (item.name === "in" || item.name === "Keyword" && getNodeText(item, ctx.source) === "in") {
4010
+ i++;
4011
+ } else {
4012
+ i++;
4013
+ }
4014
+ }
4015
+ return { outputExpr, clauses };
4016
+ }
4017
+ function transformArrayComprehension(node, ctx) {
4018
+ const children = getChildren(node);
4019
+ const { outputExpr, clauses } = parseComprehensionClauses(children, ctx);
4020
+ if (clauses.length === 0) {
4021
+ return `[${outputExpr}]`;
4022
+ }
4023
+ return buildComprehensionChain(outputExpr, clauses, "array");
4024
+ }
4025
+ function transformDictComprehension(node, ctx) {
4026
+ const children = getChildren(node);
4027
+ const items = children.filter((c) => c.name !== "{" && c.name !== "}");
4028
+ let keyExpr = "";
4029
+ let valueExpr = "";
4030
+ let colonIndex = -1;
4031
+ for (let i2 = 0; i2 < items.length; i2++) {
4032
+ const item = items[i2];
4033
+ if (item?.name === ":") {
4034
+ colonIndex = i2;
4035
+ break;
4036
+ }
4037
+ }
4038
+ const keyNode = items[0];
4039
+ const valueNode = items[colonIndex + 1];
4040
+ if (colonIndex > 0 && keyNode && valueNode) {
4041
+ keyExpr = transformNode(keyNode, ctx);
4042
+ valueExpr = transformNode(valueNode, ctx);
4043
+ }
4044
+ const clauseItems = items.slice(colonIndex + 2);
4045
+ const clauses = [];
4046
+ let i = 0;
4047
+ while (i < clauseItems.length) {
4048
+ const item = clauseItems[i];
4049
+ if (!item) {
4050
+ i++;
4051
+ continue;
4052
+ }
4053
+ if (item.name === "for" || item.name === "Keyword" && getNodeText(item, ctx.source) === "for") {
4054
+ const varNode = clauseItems[i + 1];
4055
+ const iterableNode = clauseItems[i + 3];
4056
+ if (varNode && iterableNode) {
4057
+ clauses.push({
4058
+ type: "for",
4059
+ variable: transformNode(varNode, ctx),
4060
+ iterable: transformNode(iterableNode, ctx)
4061
+ });
4062
+ i += 4;
4063
+ } else {
4064
+ i++;
4065
+ }
4066
+ } else if (item.name === "if" || item.name === "Keyword" && getNodeText(item, ctx.source) === "if") {
4067
+ const conditionNode = clauseItems[i + 1];
4068
+ if (conditionNode) {
4069
+ clauses.push({
4070
+ type: "if",
4071
+ condition: transformNode(conditionNode, ctx)
4072
+ });
4073
+ i += 2;
4074
+ } else {
4075
+ i++;
4076
+ }
4077
+ } else {
4078
+ i++;
4079
+ }
4080
+ }
4081
+ ctx.usesRuntime.add("dict");
4082
+ return buildDictComprehensionChain(keyExpr, valueExpr, clauses);
4083
+ }
4084
+ function transformSetExpression(node, ctx) {
4085
+ const children = getChildren(node);
4086
+ const elements = children.filter((c) => c.name !== "{" && c.name !== "}" && c.name !== ",");
4087
+ ctx.usesRuntime.add("set");
4088
+ const elementCodes = elements.map((el) => transformNode(el, ctx));
4089
+ return `set([${elementCodes.join(", ")}])`;
4090
+ }
4091
+ function transformSetComprehension(node, ctx) {
4092
+ const children = getChildren(node);
4093
+ const { outputExpr, clauses } = parseComprehensionClauses(children, ctx);
4094
+ if (clauses.length === 0) {
4095
+ ctx.usesRuntime.add("set");
4096
+ return `set([${outputExpr}])`;
4097
+ }
4098
+ ctx.usesRuntime.add("set");
4099
+ const arrayComp = buildComprehensionChain(outputExpr, clauses, "array");
4100
+ return `set(${arrayComp})`;
4101
+ }
4102
+ function transformGeneratorExpression(node, ctx) {
4103
+ const children = getChildren(node);
4104
+ const items = children.filter((c) => c.name !== "(" && c.name !== ")");
4105
+ const { outputExpr, clauses } = parseComprehensionClauses(items, ctx);
4106
+ if (clauses.length === 0) {
4107
+ return outputExpr;
4108
+ }
4109
+ return buildGeneratorChain(outputExpr, clauses);
4110
+ }
4111
+ function buildGeneratorChain(outputExpr, clauses) {
4112
+ const forClauses = [];
4113
+ for (const clause of clauses) {
4114
+ if (clause.type === "for" && clause.variable && clause.iterable) {
4115
+ forClauses.push({
4116
+ variable: clause.variable,
4117
+ iterable: clause.iterable,
4118
+ conditions: []
4119
+ });
4120
+ } else if (clause.type === "if" && clause.condition && forClauses.length > 0) {
4121
+ const lastFor = forClauses[forClauses.length - 1];
4122
+ if (lastFor) {
4123
+ lastFor.conditions.push(clause.condition);
4124
+ }
4125
+ }
4126
+ }
4127
+ if (forClauses.length === 0) {
4128
+ return `(function*() { yield ${outputExpr}; })()`;
4129
+ }
4130
+ let body = "";
4131
+ let indent = "";
4132
+ const indentStep = " ";
4133
+ for (const fc of forClauses) {
4134
+ body += `${indent}for (const ${fc.variable} of ${fc.iterable}) `;
4135
+ if (fc.conditions.length > 0) {
4136
+ const combinedCond = fc.conditions.join(" && ");
4137
+ body += `if (${combinedCond}) `;
4138
+ }
4139
+ indent += indentStep;
4140
+ }
4141
+ body += `yield ${outputExpr};`;
4142
+ return `(function*() { ${body} })()`;
4143
+ }
4144
+ function wrapIterableIfNeeded(iterable) {
4145
+ if (iterable.startsWith("range(") || iterable.startsWith("enumerate(") || iterable.startsWith("zip(") || iterable.startsWith("reversed(") || iterable.startsWith("filter(") || iterable.startsWith("map(")) {
4146
+ return `[...${iterable}]`;
4147
+ }
4148
+ return iterable;
4149
+ }
4150
+ function buildComprehensionChain(outputExpr, clauses, type) {
4151
+ if (clauses.length === 0) {
4152
+ return type === "array" ? `[${outputExpr}]` : outputExpr;
4153
+ }
4154
+ const forClauses = [];
4155
+ for (const clause of clauses) {
4156
+ if (clause.type === "for" && clause.variable && clause.iterable) {
4157
+ forClauses.push({
4158
+ variable: clause.variable,
4159
+ iterable: clause.iterable,
4160
+ conditions: []
4161
+ });
4162
+ } else if (clause.type === "if" && clause.condition && forClauses.length > 0) {
4163
+ const lastFor = forClauses[forClauses.length - 1];
4164
+ if (lastFor) {
4165
+ lastFor.conditions.push(clause.condition);
4166
+ }
4167
+ }
4168
+ }
4169
+ if (forClauses.length === 0) {
4170
+ return type === "array" ? `[${outputExpr}]` : outputExpr;
4171
+ }
4172
+ if (forClauses.length === 1) {
4173
+ const fc = forClauses[0];
4174
+ if (!fc) return `[${outputExpr}]`;
4175
+ let chain = wrapIterableIfNeeded(fc.iterable);
4176
+ for (const cond of fc.conditions) {
4177
+ chain = `${chain}.filter((${fc.variable}) => ${cond})`;
4178
+ }
4179
+ chain = `${chain}.map((${fc.variable}) => ${outputExpr})`;
4180
+ return chain;
4181
+ }
4182
+ let result = "";
4183
+ for (let i = 0; i < forClauses.length; i++) {
4184
+ const fc = forClauses[i];
4185
+ if (!fc) continue;
4186
+ const isLast = i === forClauses.length - 1;
4187
+ let inner = wrapIterableIfNeeded(fc.iterable);
4188
+ for (const cond of fc.conditions) {
4189
+ inner = `${inner}.filter((${fc.variable}) => ${cond})`;
4190
+ }
4191
+ if (isLast) {
4192
+ inner = `${inner}.map((${fc.variable}) => ${outputExpr})`;
4193
+ } else {
4194
+ result = inner;
4195
+ continue;
4196
+ }
4197
+ for (let j = forClauses.length - 2; j >= 0; j--) {
4198
+ const outerFc = forClauses[j];
4199
+ if (!outerFc) continue;
4200
+ let outerChain = wrapIterableIfNeeded(outerFc.iterable);
4201
+ for (const cond of outerFc.conditions) {
4202
+ outerChain = `${outerChain}.filter((${outerFc.variable}) => ${cond})`;
4203
+ }
4204
+ inner = `${outerChain}.flatMap((${outerFc.variable}) => ${inner})`;
4205
+ }
4206
+ result = inner;
4207
+ break;
4208
+ }
4209
+ return result;
4210
+ }
4211
+ function buildDictComprehensionChain(keyExpr, valueExpr, clauses) {
4212
+ if (clauses.length === 0) {
4213
+ return `dict([[${keyExpr}, ${valueExpr}]])`;
4214
+ }
4215
+ const forClauses = [];
4216
+ for (const clause of clauses) {
4217
+ if (clause.type === "for" && clause.variable && clause.iterable) {
4218
+ forClauses.push({
4219
+ variable: clause.variable,
4220
+ iterable: clause.iterable,
4221
+ conditions: []
4222
+ });
4223
+ } else if (clause.type === "if" && clause.condition && forClauses.length > 0) {
4224
+ const lastFor = forClauses[forClauses.length - 1];
4225
+ if (lastFor) {
4226
+ lastFor.conditions.push(clause.condition);
4227
+ }
4228
+ }
4229
+ }
4230
+ if (forClauses.length === 0) {
4231
+ return `dict([[${keyExpr}, ${valueExpr}]])`;
4232
+ }
4233
+ const pairExpr = `[${keyExpr}, ${valueExpr}]`;
4234
+ const arrayComp = buildComprehensionChain(pairExpr, clauses, "array");
4235
+ return `dict(${arrayComp})`;
4236
+ }
4237
+ function transformScopeStatement(node, ctx) {
4238
+ const children = getChildren(node);
4239
+ const keyword = children.find((c) => c.name === "global" || c.name === "nonlocal");
4240
+ const keywordText = keyword ? getNodeText(keyword, ctx.source) : "scope";
4241
+ const vars = children.filter((c) => c.name === "VariableName").map((c) => getNodeText(c, ctx.source));
4242
+ return `/* ${keywordText} ${vars.join(", ")} */`;
4243
+ }
4244
+ function transformDeleteStatement(node, ctx) {
4245
+ const children = getChildren(node);
4246
+ const targets = children.filter((c) => c.name !== "del" && c.name !== ",");
4247
+ const deletions = targets.map((target) => {
4248
+ if (target.name === "MemberExpression") {
4249
+ const memberChildren = getChildren(target);
4250
+ const obj = memberChildren[0];
4251
+ const bracket = memberChildren.find((c) => c.name === "[");
4252
+ if (bracket) {
4253
+ const objCode = obj ? transformNode(obj, ctx) : "";
4254
+ const indexNode = memberChildren.find((c) => c.name !== "[" && c.name !== "]" && c !== obj);
4255
+ const indexCode = indexNode ? transformNode(indexNode, ctx) : "0";
4256
+ if (indexNode?.name === "Number") {
4257
+ return `${objCode}.splice(${indexCode}, 1)`;
4258
+ }
4259
+ return `delete ${objCode}[${indexCode}]`;
4260
+ } else {
4261
+ return `delete ${transformNode(target, ctx)}`;
4262
+ }
4263
+ } else if (target.name === "VariableName") {
4264
+ const varName = getNodeText(target, ctx.source);
4265
+ return `${varName} = undefined`;
4266
+ }
4267
+ return `delete ${transformNode(target, ctx)}`;
4268
+ });
4269
+ return deletions.join(";\n");
4270
+ }
4271
+ function transformAssertStatement(node, ctx) {
4272
+ const children = getChildren(node);
4273
+ const expressions = children.filter((c) => c.name !== "assert" && c.name !== ",");
4274
+ const condition = expressions[0];
4275
+ const message = expressions[1];
4276
+ const conditionCode = condition ? transformNode(condition, ctx) : "true";
4277
+ const messageCode = message ? transformNode(message, ctx) : '"Assertion failed"';
4278
+ return `if (!(${conditionCode})) throw new Error(${messageCode})`;
4279
+ }
4280
+ function transformYieldStatement(node, ctx) {
4281
+ const children = getChildren(node);
4282
+ const hasFrom = children.some((c) => c.name === "from");
4283
+ const valueNode = children.find((c) => c.name !== "yield" && c.name !== "from");
4284
+ if (hasFrom && valueNode) {
4285
+ return `yield* ${transformNode(valueNode, ctx)}`;
4286
+ } else if (valueNode) {
4287
+ return `yield ${transformNode(valueNode, ctx)}`;
4288
+ }
4289
+ return "yield";
4290
+ }
4291
+
4292
+ // src/generator/index.ts
4293
+ import * as prettier from "prettier";
4294
+ var defaultOptions = {
4295
+ includeRuntime: true,
4296
+ runtimeImportPath: "pythonlib"
4297
+ };
4298
+ var prettierOptions = {
4299
+ parser: "typescript",
4300
+ singleQuote: false,
4301
+ tabWidth: 2,
4302
+ trailingComma: "none",
4303
+ printWidth: 100,
4304
+ semi: false,
4305
+ bracketSpacing: true,
4306
+ arrowParens: "always",
4307
+ endOfLine: "lf"
4308
+ };
4309
+ var BUILTINS = /* @__PURE__ */ new Set([
4310
+ // Core operations
4311
+ "floorDiv",
4312
+ "pow",
4313
+ "mod",
4314
+ "sprintf",
4315
+ "slice",
4316
+ "at",
4317
+ "contains",
4318
+ "repeatValue",
4319
+ "strFormat",
4320
+ "divMod",
4321
+ // Collection constructors
4322
+ "list",
4323
+ "dict",
4324
+ "set",
4325
+ "tuple",
4326
+ // Iteration
4327
+ "len",
4328
+ "range",
4329
+ "enumerate",
4330
+ "zip",
4331
+ "sorted",
4332
+ "reversed",
4333
+ "iter",
4334
+ "map",
4335
+ "filter",
4336
+ // Aggregation
4337
+ "abs",
4338
+ "min",
4339
+ "max",
4340
+ "sum",
4341
+ "all",
4342
+ "any",
4343
+ "round",
4344
+ // Character/number conversion
4345
+ "ord",
4346
+ "chr",
4347
+ "hex",
4348
+ "oct",
4349
+ "bin",
4350
+ // Type conversion
4351
+ "int",
4352
+ "float",
4353
+ "str",
4354
+ "bool",
4355
+ "repr",
4356
+ "ascii",
4357
+ // Type checking & misc
4358
+ "isinstance",
4359
+ "type",
4360
+ "input",
4361
+ "format"
4362
+ ]);
4363
+ var MODULE_NAMESPACES = /* @__PURE__ */ new Set(["string", "list", "dict", "set"]);
4364
+ function generate(input, options = {}) {
4365
+ const opts = { ...defaultOptions, ...options };
4366
+ const result = transform(input);
4367
+ const usedRuntimeFunctions = Array.from(result.usesRuntime).sort();
4368
+ let runtimeImport = null;
4369
+ if (opts.includeRuntime && usedRuntimeFunctions.length > 0) {
4370
+ const basePath = opts.runtimeImportPath ?? "pythonlib";
4371
+ runtimeImport = buildRuntimeImports(usedRuntimeFunctions, basePath);
4372
+ }
4373
+ let code = result.code;
4374
+ if (runtimeImport) {
4375
+ code = runtimeImport + "\n\n" + code;
4376
+ }
4377
+ return {
4378
+ code,
4379
+ runtimeImport,
4380
+ usedRuntimeFunctions
4381
+ };
4382
+ }
4383
+ function buildRuntimeImports(usedFunctions, basePath) {
4384
+ const mainImports = /* @__PURE__ */ new Set();
4385
+ const moduleImports = /* @__PURE__ */ new Map();
4386
+ for (const func of usedFunctions) {
4387
+ if (func.includes("/")) {
4388
+ const [moduleName, funcName] = func.split("/");
4389
+ if (moduleName && funcName) {
4390
+ let funcs = moduleImports.get(moduleName);
4391
+ if (!funcs) {
4392
+ funcs = /* @__PURE__ */ new Set();
4393
+ moduleImports.set(moduleName, funcs);
4394
+ }
4395
+ funcs.add(funcName);
4396
+ }
4397
+ continue;
4398
+ }
4399
+ if (func.includes(".")) {
4400
+ const [namespace] = func.split(".");
4401
+ if (namespace && MODULE_NAMESPACES.has(namespace)) {
4402
+ mainImports.add(namespace);
4403
+ }
4404
+ continue;
4405
+ }
4406
+ if (MODULE_NAMESPACES.has(func)) {
4407
+ mainImports.add(func);
4408
+ continue;
4409
+ }
4410
+ if (BUILTINS.has(func)) {
4411
+ mainImports.add(func);
4412
+ continue;
4413
+ }
4414
+ mainImports.add(func);
4415
+ }
4416
+ const importStatements = [];
4417
+ if (mainImports.size > 0) {
4418
+ const sorted = Array.from(mainImports).sort();
4419
+ importStatements.push(`import { ${sorted.join(", ")} } from "${basePath}"`);
4420
+ }
4421
+ const sortedModules = Array.from(moduleImports.keys()).sort();
4422
+ for (const moduleName of sortedModules) {
4423
+ const funcSet = moduleImports.get(moduleName);
4424
+ if (funcSet) {
4425
+ const funcs = Array.from(funcSet).sort();
4426
+ importStatements.push(`import { ${funcs.join(", ")} } from "${basePath}/${moduleName}"`);
4427
+ }
4428
+ }
4429
+ return importStatements.join("\n");
4430
+ }
4431
+ function transpile(python, options = {}) {
4432
+ return generate(python, options).code;
4433
+ }
4434
+ async function formatCode(code) {
4435
+ try {
4436
+ return await prettier.format(code, prettierOptions);
4437
+ } catch {
4438
+ return code;
4439
+ }
4440
+ }
4441
+ async function generateAsync(input, options = {}) {
4442
+ const result = generate(input, options);
4443
+ const formattedCode = await formatCode(result.code);
4444
+ return {
4445
+ ...result,
4446
+ code: formattedCode
4447
+ };
4448
+ }
4449
+ async function transpileAsync(python, options = {}) {
4450
+ const result = await generateAsync(python, options);
4451
+ return result.code;
4452
+ }
4453
+
4454
+ export {
4455
+ parse,
4456
+ getNodeText,
4457
+ getChildren,
4458
+ getChildByType,
4459
+ getChildrenByType,
4460
+ walkTree,
4461
+ debugTree,
4462
+ transform,
4463
+ generate,
4464
+ transpile,
4465
+ formatCode,
4466
+ generateAsync,
4467
+ transpileAsync
4468
+ };
4469
+ /* v8 ignore next 3 -- malformed type annotation fallback @preserve */
4470
+ /* v8 ignore next 3 -- defensive: empty targets/values can't occur with valid Python @preserve */
4471
+ /* v8 ignore next -- @preserve */
4472
+ /* v8 ignore next 3 -- defensive: walrus operator always has name and value @preserve */
4473
+ /* v8 ignore next -- defensive: checked exprs.length >= 3 above @preserve */
4474
+ /* v8 ignore next 4 -- defensive: items from parser are never null @preserve */
4475
+ /* v8 ignore next 3 -- defensive: subscript always has brackets @preserve */
4476
+ /* v8 ignore next 3 -- defensive: slice detection already checked for colons @preserve */
4477
+ /* v8 ignore next 2 -- del obj.attr edge case @preserve */
4478
+ /* v8 ignore next -- fallback for complex del targets @preserve */
4479
+ /* v8 ignore next -- bare yield statement @preserve */
4480
+ /* v8 ignore next 2 -- fallback for future/unknown runtime functions @preserve */
4481
+ /* v8 ignore start -- async wrappers tested via CLI @preserve */
4482
+ //# sourceMappingURL=chunk-VYG6GDWU.js.map