depyo 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/depyo.js +43 -2
- package/lib/OpCodes.js +22 -5
- package/lib/PycDecompiler.js +1050 -40
- package/lib/PycDisassembler.js +1 -1
- package/lib/PycReader.js +22 -3
- package/lib/PythonObject.js +42 -6
- package/lib/ast/ast_node.js +381 -88
- package/lib/bytecode/python_3_0.js +1 -1
- package/lib/bytecode/python_3_12.js +1 -1
- package/lib/bytecode/python_3_13.js +13 -13
- package/lib/bytecode/python_3_14.js +13 -13
- package/lib/bytecode/python_3_15.js +183 -0
- package/lib/code_reader.js +107 -146
- package/lib/handlers/collections_update.js +50 -1
- package/lib/handlers/comparisons.js +3 -10
- package/lib/handlers/context_managers.js +202 -13
- package/lib/handlers/control_flow_jumps.js +516 -23
- package/lib/handlers/exceptions_blocks.js +92 -24
- package/lib/handlers/formatting.js +60 -17
- package/lib/handlers/function_calls.js +474 -58
- package/lib/handlers/function_class_build.js +170 -65
- package/lib/handlers/generators_async.js +67 -0
- package/lib/handlers/load_store_names.js +190 -57
- package/lib/handlers/loop_iterator.js +162 -6
- package/lib/handlers/misc_other.js +253 -44
- package/lib/handlers/stack_ops.js +81 -19
- package/lib/handlers/subscript_slice.js +103 -1
- package/lib/handlers/unpack.js +18 -16
- package/package.json +2 -2
package/lib/ast/ast_node.js
CHANGED
|
@@ -7,6 +7,19 @@ function indent(level = 1) {
|
|
|
7
7
|
return Buffer.alloc(level * SPACES_PER_LEVEL, ' ').toString('ascii');
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function renderTypeParam(tp) {
|
|
11
|
+
// PEP 696: a typeparam may be {name, default: ASTNode} instead of a string.
|
|
12
|
+
if (tp && typeof tp === 'object' && tp.name && tp.default != null && !tp.codeFragment) {
|
|
13
|
+
const defFrag = tp.default?.codeFragment
|
|
14
|
+
? tp.default.codeFragment()
|
|
15
|
+
: tp.default?.toString?.() ?? String(tp.default);
|
|
16
|
+
return `${tp.name} = ${defFrag}`;
|
|
17
|
+
}
|
|
18
|
+
if (tp?.codeFragment) return tp.codeFragment();
|
|
19
|
+
if (tp?.toString) return tp.toString();
|
|
20
|
+
return tp;
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
class ASTNode {
|
|
11
24
|
|
|
12
25
|
m_lineNo = -1;
|
|
@@ -51,11 +64,7 @@ class ASTNode {
|
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
codeFragment() {
|
|
54
|
-
|
|
55
|
-
if (result === null || result === undefined) {
|
|
56
|
-
return `${result}`;
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
67
|
+
throw new Error(`${this.constructor.name}.codeFragment() not implemented — subclass must override`);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
static calculateSpacing(prevNode, node) {
|
|
@@ -249,7 +258,19 @@ class ASTNodeList extends ASTNode {
|
|
|
249
258
|
}
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
|
|
261
|
+
// Only join with `; ` between two simple (non-compound) statements
|
|
262
|
+
// that also happen to be on the same source line. A compound block
|
|
263
|
+
// (if/while/for/try/function/class) must never be semicolon-joined
|
|
264
|
+
// to a following sibling — doing so swallows the sibling into the
|
|
265
|
+
// block's body and breaks structure.
|
|
266
|
+
const prevIsCompound = prevNode instanceof ASTBlock ||
|
|
267
|
+
prevNode instanceof ASTFunction ||
|
|
268
|
+
prevNode instanceof ASTClass;
|
|
269
|
+
const nodeIsCompound = node instanceof ASTBlock ||
|
|
270
|
+
node instanceof ASTFunction ||
|
|
271
|
+
node instanceof ASTClass;
|
|
272
|
+
if (prevNode && spacing == 0 && sourceFragment.length == 1 &&
|
|
273
|
+
!prevIsCompound && !nodeIsCompound) {
|
|
253
274
|
result.lastLineAppend((prevNode ? "; " : "") + sourceFragment.toString(), false);
|
|
254
275
|
} else {
|
|
255
276
|
const blankCount = rawSpacing ? Math.max(0, spacing - 1) : (spacing > 1 ? 1 : 0);
|
|
@@ -446,19 +467,33 @@ class ASTObject extends ASTNode {
|
|
|
446
467
|
}
|
|
447
468
|
}
|
|
448
469
|
|
|
449
|
-
let result = this.object.toString();
|
|
450
470
|
if (["Py_String", "Py_Unicode"].includes(this.object.ClassName)) {
|
|
471
|
+
let raw = this.object.Value;
|
|
472
|
+
if (raw == null || raw.length === 0) {
|
|
473
|
+
return '""';
|
|
474
|
+
}
|
|
475
|
+
raw = raw.toString();
|
|
476
|
+
let escaped = raw
|
|
477
|
+
.replace(/\\/g, '\\\\')
|
|
478
|
+
.replace(/\n/g, '\\n')
|
|
479
|
+
.replace(/\r/g, '\\r')
|
|
480
|
+
.replace(/\t/g, '\\t')
|
|
481
|
+
// Any remaining C0/DEL byte must be hex-escaped; leaving a raw
|
|
482
|
+
// NUL or BEL in the source makes the output unparseable.
|
|
483
|
+
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, c =>
|
|
484
|
+
'\\x' + c.charCodeAt(0).toString(16).padStart(2, '0'));
|
|
451
485
|
let quote = '"';
|
|
452
|
-
if (
|
|
453
|
-
if (!
|
|
486
|
+
if (escaped.includes('"')) {
|
|
487
|
+
if (!escaped.includes("'")) {
|
|
454
488
|
quote = "'";
|
|
455
489
|
} else {
|
|
456
|
-
|
|
457
|
-
result = result.replace(/"/g, '\\"');
|
|
490
|
+
escaped = escaped.replace(/"/g, '\\"');
|
|
458
491
|
}
|
|
459
492
|
}
|
|
460
|
-
return quote +
|
|
493
|
+
return quote + escaped + quote;
|
|
461
494
|
}
|
|
495
|
+
|
|
496
|
+
let result = this.object.toString();
|
|
462
497
|
if (result === null || result === undefined) {
|
|
463
498
|
return `${result}`;
|
|
464
499
|
}
|
|
@@ -498,9 +533,8 @@ class ASTUnary extends ASTNode {
|
|
|
498
533
|
}
|
|
499
534
|
|
|
500
535
|
codeFragment() {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return result;
|
|
536
|
+
const operand = this.operand?.codeFragment ? this.operand.codeFragment() : '##ERROR##';
|
|
537
|
+
return `${ASTUnary.UnaryOpString[this.op]}${operand}`;
|
|
504
538
|
}
|
|
505
539
|
|
|
506
540
|
toString() {
|
|
@@ -568,11 +602,11 @@ class ASTBinary extends ASTNode {
|
|
|
568
602
|
}
|
|
569
603
|
|
|
570
604
|
get line() {
|
|
571
|
-
return this.m_left.line;
|
|
605
|
+
return this.m_left ? this.m_left.line : this.m_lineNo;
|
|
572
606
|
}
|
|
573
607
|
|
|
574
608
|
get lastLine() {
|
|
575
|
-
return this.m_right.line;
|
|
609
|
+
return this.m_right ? this.m_right.line : this.m_lineNo;
|
|
576
610
|
}
|
|
577
611
|
|
|
578
612
|
get isInplace() {
|
|
@@ -730,6 +764,13 @@ class ASTBinary extends ASTNode {
|
|
|
730
764
|
case ASTBinary.BinOp.LeftShift:
|
|
731
765
|
case ASTBinary.BinOp.RightShift:
|
|
732
766
|
return 1;
|
|
767
|
+
// Python `and` binds tighter than `or`; ranking them so that
|
|
768
|
+
// AND wrapping OR forces parens keeps `(b or c) and d` from
|
|
769
|
+
// rendering as `b or c and d` (which parses as `b or (c and d)`).
|
|
770
|
+
case ASTBinary.BinOp.LogicalAnd:
|
|
771
|
+
return -1;
|
|
772
|
+
case ASTBinary.BinOp.LogicalOr:
|
|
773
|
+
return -2;
|
|
733
774
|
default:
|
|
734
775
|
return 0;
|
|
735
776
|
}
|
|
@@ -744,6 +785,13 @@ class ASTBinary extends ASTNode {
|
|
|
744
785
|
fragment = `(${fragment})`;
|
|
745
786
|
}
|
|
746
787
|
}
|
|
788
|
+
// `0.bit_length()` parses as a bad float literal; wrap integer
|
|
789
|
+
// literal targets of attribute access in parens.
|
|
790
|
+
if (this.op === ASTBinary.BinOp.Attr && position === 'left' &&
|
|
791
|
+
child instanceof ASTObject &&
|
|
792
|
+
child.object?.ClassName === 'Py_Int') {
|
|
793
|
+
fragment = `(${fragment})`;
|
|
794
|
+
}
|
|
747
795
|
return fragment;
|
|
748
796
|
};
|
|
749
797
|
|
|
@@ -944,15 +992,7 @@ class ASTStore extends ASTNode {
|
|
|
944
992
|
let isAsync = (codeObject.Flags & ASTFunction.CodeFlags.CO_COROUTINE) ||
|
|
945
993
|
(codeObject.Flags & ASTFunction.CodeFlags.CO_ASYNC_GENERATOR);
|
|
946
994
|
result.add(isAsync ? "async ": "");
|
|
947
|
-
const typeParams = (this.src.typeParams || []).map(tp =>
|
|
948
|
-
if (tp?.codeFragment) {
|
|
949
|
-
return tp.codeFragment();
|
|
950
|
-
}
|
|
951
|
-
if (tp?.toString) {
|
|
952
|
-
return tp.toString();
|
|
953
|
-
}
|
|
954
|
-
return tp;
|
|
955
|
-
});
|
|
995
|
+
const typeParams = (this.src.typeParams || []).map(tp => renderTypeParam(tp));
|
|
956
996
|
const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
|
|
957
997
|
result.lastLineAppend(`def ${destName}${typeParamsStr}(`);
|
|
958
998
|
}
|
|
@@ -1004,29 +1044,58 @@ class ASTStore extends ASTNode {
|
|
|
1004
1044
|
const rawKwName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1005
1045
|
let argName = rawKwName;
|
|
1006
1046
|
|
|
1007
|
-
//
|
|
1047
|
+
// Python syntax is `name: annotation = default`, so append the
|
|
1048
|
+
// annotation first and the default afterwards.
|
|
1049
|
+
const kwAnn = this.src.annotations?.[rawKwName];
|
|
1050
|
+
if (kwAnn) {
|
|
1051
|
+
argName = `${argName}: ${kwAnn.codeFragment?.() || kwAnn.toString?.() || '##ERROR##'}`;
|
|
1052
|
+
}
|
|
1008
1053
|
if (default_params && default_params.length > 0) {
|
|
1009
1054
|
let defaultIdx = kwIdx - (codeObject.KWOnlyArgCount - default_params.length);
|
|
1010
1055
|
if (defaultIdx >= 0 && default_params[defaultIdx]) {
|
|
1011
1056
|
argName += "=" + default_params[defaultIdx].value.codeFragment();
|
|
1012
1057
|
}
|
|
1013
1058
|
}
|
|
1014
|
-
const kwAnn = this.src.annotations?.[rawKwName];
|
|
1015
|
-
if (kwAnn) {
|
|
1016
|
-
argName = `${argName}: ${kwAnn.codeFragment?.() || kwAnn.toString?.() || '##ERROR##'}`;
|
|
1017
|
-
}
|
|
1018
1059
|
argNames.push(argName);
|
|
1019
1060
|
}
|
|
1020
1061
|
}
|
|
1021
1062
|
}
|
|
1022
1063
|
if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARARGS) {
|
|
1023
|
-
|
|
1064
|
+
const vaName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1065
|
+
let entry = '*' + vaName;
|
|
1066
|
+
const vaAnn = this.src.annotations?.[vaName];
|
|
1067
|
+
if (vaAnn) {
|
|
1068
|
+
entry += `: ${vaAnn.codeFragment?.() || vaAnn.toString?.() || '##ERROR##'}`;
|
|
1069
|
+
}
|
|
1070
|
+
if (codeObject.KWOnlyArgCount) {
|
|
1071
|
+
// Replace the placeholder "*" we already pushed with the named varargs.
|
|
1072
|
+
const starIdx = argNames.indexOf('*');
|
|
1073
|
+
if (starIdx >= 0) {
|
|
1074
|
+
argNames[starIdx] = entry;
|
|
1075
|
+
} else {
|
|
1076
|
+
argNames.push(entry);
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
argNames.push(entry);
|
|
1080
|
+
}
|
|
1024
1081
|
}
|
|
1025
1082
|
if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARKEYWORDS) {
|
|
1026
|
-
|
|
1083
|
+
const vkName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1084
|
+
let entry = '**' + vkName;
|
|
1085
|
+
const vkAnn = this.src.annotations?.[vkName];
|
|
1086
|
+
if (vkAnn) {
|
|
1087
|
+
entry += `: ${vkAnn.codeFragment?.() || vkAnn.toString?.() || '##ERROR##'}`;
|
|
1088
|
+
}
|
|
1089
|
+
argNames.push(entry);
|
|
1027
1090
|
}
|
|
1028
1091
|
|
|
1029
|
-
|
|
1092
|
+
// Lambda needs a space after the `lambda` keyword before params (shouldTrim would
|
|
1093
|
+
// eat it); `def X(` already has the opening paren and wants no leading space.
|
|
1094
|
+
if (inLambda) {
|
|
1095
|
+
result.lastLineAppend((argNames.length > 0 ? " " : "") + argNames.join(", "), false);
|
|
1096
|
+
} else {
|
|
1097
|
+
result.lastLineAppend(argNames.join(", "));
|
|
1098
|
+
}
|
|
1030
1099
|
|
|
1031
1100
|
if (inLambda) {
|
|
1032
1101
|
result.lastLineAppend(": ", false);
|
|
@@ -1050,32 +1119,42 @@ class ASTStore extends ASTNode {
|
|
|
1050
1119
|
if (inLambda) {
|
|
1051
1120
|
result.lastLineAppend(methodBody);
|
|
1052
1121
|
} else {
|
|
1053
|
-
|
|
1122
|
+
const bodyHasContent = methodBody && methodBody.toString().trim().length;
|
|
1123
|
+
const hasGlobals = codeObject.Globals && codeObject.Globals.size > 0;
|
|
1124
|
+
if (bodyHasContent) {
|
|
1125
|
+
result.add(methodBody);
|
|
1126
|
+
} else if (!hasGlobals) {
|
|
1127
|
+
result.add("pass");
|
|
1128
|
+
}
|
|
1054
1129
|
}
|
|
1055
1130
|
result.decreaseIndent();
|
|
1056
1131
|
|
|
1057
1132
|
} else if (this.src instanceof ASTClass) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1133
|
+
const allDecorators = [
|
|
1134
|
+
...(this.src.decorators || []),
|
|
1135
|
+
...(this.decorators || []),
|
|
1136
|
+
];
|
|
1137
|
+
for (let decorator of allDecorators) {
|
|
1138
|
+
const decoText = decorator?.codeFragment ? decorator.codeFragment() : decorator;
|
|
1139
|
+
result.add('@' + decoText);
|
|
1063
1140
|
}
|
|
1064
1141
|
let classNode = this.src;
|
|
1065
|
-
const typeParams = (classNode.typeParams || []).map(tp =>
|
|
1066
|
-
if (tp?.codeFragment) {
|
|
1067
|
-
return tp.codeFragment();
|
|
1068
|
-
}
|
|
1069
|
-
if (tp?.toString) {
|
|
1070
|
-
return tp.toString();
|
|
1071
|
-
}
|
|
1072
|
-
return tp;
|
|
1073
|
-
});
|
|
1142
|
+
const typeParams = (classNode.typeParams || []).map(tp => renderTypeParam(tp));
|
|
1074
1143
|
const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
|
|
1075
1144
|
result.add(`class ${this.dest.codeFragment()}${typeParamsStr}`);
|
|
1076
1145
|
const bases = classNode?.bases?.values || [];
|
|
1077
|
-
|
|
1078
|
-
|
|
1146
|
+
const kwargs = classNode?.kwargs || [];
|
|
1147
|
+
const baseStrs = bases.map(node => node?.codeFragment() || "##ERROR##");
|
|
1148
|
+
const kwargStrs = kwargs.map(({key, value}) => {
|
|
1149
|
+
const keyStr = key?.object?.Value?.toString?.() ??
|
|
1150
|
+
key?.name ??
|
|
1151
|
+
(typeof key === 'string' ? key : key?.codeFragment?.()?.toString?.());
|
|
1152
|
+
const valStr = value?.codeFragment?.()?.toString?.() ?? "##ERROR##";
|
|
1153
|
+
return `${keyStr}=${valStr}`;
|
|
1154
|
+
});
|
|
1155
|
+
const headerArgs = [...baseStrs, ...kwargStrs];
|
|
1156
|
+
if (headerArgs.length > 0) {
|
|
1157
|
+
result.lastLineAppend(`(${headerArgs.join(", ")})`, false);
|
|
1079
1158
|
}
|
|
1080
1159
|
result.lastLineAppend(":");
|
|
1081
1160
|
let codeObject = classNode.code?.func?.code?.object || classNode.code?.code?.object || {};
|
|
@@ -1097,16 +1176,9 @@ class ASTStore extends ASTNode {
|
|
|
1097
1176
|
if (global.g_cliArgs?.debug) {
|
|
1098
1177
|
console.log(`[ASTStore class render] name=${this.dest?.name}, len=${classBodyNodeList.length}, first=${classBodyNodeList[0]?.constructor?.name}, retVal=${classBodyNodeList[0]?.value?.constructor?.name}, isSyntheticEmptyReturn=${isSyntheticEmptyReturn}, hasContent=${hasContent}`);
|
|
1099
1178
|
}
|
|
1100
|
-
const hasMetaTypeBase = Array.isArray(classNode?.bases?.values) &&
|
|
1101
|
-
classNode.bases.values.some(b => {
|
|
1102
|
-
const frag = b?.codeFragment?.();
|
|
1103
|
-
const str = frag?.toString?.();
|
|
1104
|
-
return typeof str === 'string' && str.startsWith('type(');
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
1179
|
if (hasContent && !isSyntheticEmptyReturn) {
|
|
1108
1180
|
result.add(classBody);
|
|
1109
|
-
} else if (!isSyntheticEmptyReturn
|
|
1181
|
+
} else if (!isSyntheticEmptyReturn) {
|
|
1110
1182
|
result.add("pass");
|
|
1111
1183
|
}
|
|
1112
1184
|
result.decreaseIndent();
|
|
@@ -1271,6 +1343,23 @@ class ASTReturn extends ASTNode {
|
|
|
1271
1343
|
return result;
|
|
1272
1344
|
}
|
|
1273
1345
|
|
|
1346
|
+
switch (this.rettype) {
|
|
1347
|
+
case ASTReturn.RetType.Yield: {
|
|
1348
|
+
if (!this.value || this.value instanceof ASTNone) {
|
|
1349
|
+
return new PycResult("(yield)", true);
|
|
1350
|
+
}
|
|
1351
|
+
let yres = new PycResult("(yield ", true);
|
|
1352
|
+
yres.lastLineAppend(this.value.codeFragment());
|
|
1353
|
+
yres.lastLineAppend(")");
|
|
1354
|
+
return yres;
|
|
1355
|
+
}
|
|
1356
|
+
case ASTReturn.RetType.YieldFrom: {
|
|
1357
|
+
let yfres = new PycResult("(yield from ", true);
|
|
1358
|
+
yfres.lastLineAppend(this.value.codeFragment());
|
|
1359
|
+
yfres.lastLineAppend(")");
|
|
1360
|
+
return yfres;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1274
1363
|
return this.value?.codeFragment() || "";
|
|
1275
1364
|
}
|
|
1276
1365
|
|
|
@@ -1398,12 +1487,11 @@ class ASTFunction extends ASTNode {
|
|
|
1398
1487
|
m_annotations = {};
|
|
1399
1488
|
m_typeParams = [];
|
|
1400
1489
|
|
|
1401
|
-
constructor(code, defargs = [], kwdefargs = []
|
|
1490
|
+
constructor(code, defargs = [], kwdefargs = []) {
|
|
1402
1491
|
super();
|
|
1403
1492
|
this.m_code = code;
|
|
1404
1493
|
this.m_defargs = defargs;
|
|
1405
1494
|
this.m_kwdefargs = kwdefargs;
|
|
1406
|
-
this.m_decorators = annotations;
|
|
1407
1495
|
}
|
|
1408
1496
|
|
|
1409
1497
|
get code() {
|
|
@@ -1509,6 +1597,8 @@ class ASTClass extends ASTNode {
|
|
|
1509
1597
|
m_bases = null;
|
|
1510
1598
|
m_name = null;
|
|
1511
1599
|
m_typeParams = [];
|
|
1600
|
+
m_kwargs = [];
|
|
1601
|
+
m_decorators = [];
|
|
1512
1602
|
|
|
1513
1603
|
constructor(code, bases, name) {
|
|
1514
1604
|
super();
|
|
@@ -1517,6 +1607,14 @@ class ASTClass extends ASTNode {
|
|
|
1517
1607
|
this.m_name = name;
|
|
1518
1608
|
}
|
|
1519
1609
|
|
|
1610
|
+
add_decorator(decorator) {
|
|
1611
|
+
this.m_decorators.unshift(decorator);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
get decorators() {
|
|
1615
|
+
return this.m_decorators;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1520
1618
|
get code() {
|
|
1521
1619
|
return this.m_code;
|
|
1522
1620
|
}
|
|
@@ -1529,6 +1627,14 @@ class ASTClass extends ASTNode {
|
|
|
1529
1627
|
return this.m_name;
|
|
1530
1628
|
}
|
|
1531
1629
|
|
|
1630
|
+
get kwargs() {
|
|
1631
|
+
return this.m_kwargs || [];
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
set kwargs(value) {
|
|
1635
|
+
this.m_kwargs = value || [];
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1532
1638
|
get typeParams() {
|
|
1533
1639
|
return this.m_typeParams || [];
|
|
1534
1640
|
}
|
|
@@ -1631,13 +1737,31 @@ class ASTCall extends ASTNode {
|
|
|
1631
1737
|
params.push(this.pparams.map(formatParam).join(', ').trim());
|
|
1632
1738
|
}
|
|
1633
1739
|
if (this.kwparams?.length > 0) {
|
|
1634
|
-
params.push(this.kwparams.map(node =>
|
|
1740
|
+
params.push(this.kwparams.map(node => {
|
|
1741
|
+
// Keyword key is stored as a Py_String/Py_Unicode; extract raw identifier
|
|
1742
|
+
// rather than stripping quotes from the rendered literal (which misses
|
|
1743
|
+
// double quotes when the name contains a single quote, etc).
|
|
1744
|
+
let keyStr;
|
|
1745
|
+
const keyObj = node?.key;
|
|
1746
|
+
if (keyObj && keyObj.object && ['Py_String', 'Py_Unicode'].includes(keyObj.object.ClassName)) {
|
|
1747
|
+
keyStr = keyObj.object.toString();
|
|
1748
|
+
} else {
|
|
1749
|
+
keyStr = (keyObj?.codeFragment()?.toString() || '').replace(/^['"]|['"]$/g, '');
|
|
1750
|
+
}
|
|
1751
|
+
return `${keyStr}=${formatParam(node?.value)}`;
|
|
1752
|
+
}).join(', ').trim());
|
|
1635
1753
|
}
|
|
1636
1754
|
if (this.hasVar) {
|
|
1637
1755
|
params.push('*' + this.var.codeFragment());
|
|
1638
1756
|
}
|
|
1639
1757
|
if (this.hasKw) {
|
|
1640
|
-
|
|
1758
|
+
if (this.kw instanceof ASTMapUnpack) {
|
|
1759
|
+
for (const item of this.kw.items) {
|
|
1760
|
+
params.push('**' + (item?.codeFragment?.() ?? '##ERROR##'));
|
|
1761
|
+
}
|
|
1762
|
+
} else {
|
|
1763
|
+
params.push('**' + this.kw.codeFragment());
|
|
1764
|
+
}
|
|
1641
1765
|
}
|
|
1642
1766
|
result.lastLineAppend(params.join(', ') + ')');
|
|
1643
1767
|
return result;
|
|
@@ -1858,6 +1982,31 @@ class ASTMap extends ASTNode {
|
|
|
1858
1982
|
}
|
|
1859
1983
|
}
|
|
1860
1984
|
|
|
1985
|
+
// Py 3.5: BUILD_MAP_UNPACK / BUILD_MAP_UNPACK_WITH_CALL push this. Each item is
|
|
1986
|
+
// a mapping expression prefixed with ** at the source level. Items may be
|
|
1987
|
+
// literal ASTMap or arbitrary expressions (names, subscripts, calls).
|
|
1988
|
+
class ASTMapUnpack extends ASTNode {
|
|
1989
|
+
m_items = [];
|
|
1990
|
+
|
|
1991
|
+
constructor(items) {
|
|
1992
|
+
super();
|
|
1993
|
+
this.m_items = items || [];
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
get items() {
|
|
1997
|
+
return this.m_items;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
codeFragment() {
|
|
2001
|
+
const parts = this.m_items.map(item => '**' + (item?.codeFragment?.() ?? '##ERROR##'));
|
|
2002
|
+
return '{' + parts.join(', ') + '}';
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
toString() {
|
|
2006
|
+
return `ASTMapUnpack: line=${this.line}, ${this.codeFragment()}`;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
1861
2010
|
class ASTKwNamesMap extends ASTNode {
|
|
1862
2011
|
m_values = [];
|
|
1863
2012
|
|
|
@@ -2105,10 +2254,12 @@ class ASTKeyword extends ASTNode {
|
|
|
2105
2254
|
|
|
2106
2255
|
class ASTRaise extends ASTNode {
|
|
2107
2256
|
m_params = [];
|
|
2257
|
+
m_fromClause = false;
|
|
2108
2258
|
|
|
2109
|
-
constructor(params) {
|
|
2259
|
+
constructor(params, fromClause = false) {
|
|
2110
2260
|
super();
|
|
2111
2261
|
this.m_params = params;
|
|
2262
|
+
this.m_fromClause = fromClause;
|
|
2112
2263
|
}
|
|
2113
2264
|
|
|
2114
2265
|
get params() {
|
|
@@ -2123,6 +2274,9 @@ class ASTRaise extends ASTNode {
|
|
|
2123
2274
|
if (!this.params || this.params.length === 0) {
|
|
2124
2275
|
return 'raise';
|
|
2125
2276
|
}
|
|
2277
|
+
if (this.m_fromClause && this.params.length === 2) {
|
|
2278
|
+
return 'raise ' + this.params[0].codeFragment() + ' from ' + this.params[1].codeFragment();
|
|
2279
|
+
}
|
|
2126
2280
|
return 'raise ' + this.params.map(node => node.codeFragment()).join(', ');
|
|
2127
2281
|
}
|
|
2128
2282
|
|
|
@@ -2200,7 +2354,8 @@ class ASTBlock extends ASTNode {
|
|
|
2200
2354
|
While: 8,
|
|
2201
2355
|
For: 9,
|
|
2202
2356
|
With: 10,
|
|
2203
|
-
AsyncFor: 11
|
|
2357
|
+
AsyncFor: 11,
|
|
2358
|
+
AsyncWith: 12
|
|
2204
2359
|
}
|
|
2205
2360
|
|
|
2206
2361
|
m_blockType = ASTBlock.BlockType.Main;
|
|
@@ -2277,7 +2432,7 @@ class ASTBlock extends ASTNode {
|
|
|
2277
2432
|
get type_str() {
|
|
2278
2433
|
return [
|
|
2279
2434
|
"", "if", "else", "elif", "try", "CONTAINER", "except",
|
|
2280
|
-
"finally", "while", "for", "with", "async for"
|
|
2435
|
+
"finally", "while", "for", "with", "async for", "async with"
|
|
2281
2436
|
][this.blockType];
|
|
2282
2437
|
}
|
|
2283
2438
|
|
|
@@ -2406,19 +2561,33 @@ class ASTBlock extends ASTNode {
|
|
|
2406
2561
|
result.add("pass");
|
|
2407
2562
|
}
|
|
2408
2563
|
} else {
|
|
2564
|
+
// Emit body nodes first, then trailing dedented handlers. If
|
|
2565
|
+
// every body node was `skip`-ed (e.g. Try body whose sole
|
|
2566
|
+
// child was consumed by ternary collapse), the block would
|
|
2567
|
+
// print `try:` with no body — inject a `pass` placeholder so
|
|
2568
|
+
// the resulting source stays parseable.
|
|
2569
|
+
let emittedBodyNode = false;
|
|
2570
|
+
const handlerTail = [];
|
|
2409
2571
|
renderNodes.map(node => {
|
|
2410
2572
|
if (!node || node.skip) {
|
|
2411
2573
|
return;
|
|
2412
2574
|
}
|
|
2413
2575
|
const isHandler = node.blockType == ASTBlock.BlockType.Except || node.blockType == ASTBlock.BlockType.Finally;
|
|
2414
2576
|
if (isHandler) {
|
|
2415
|
-
|
|
2416
|
-
result.add(node.codeFragment());
|
|
2417
|
-
result.increaseIndent();
|
|
2577
|
+
handlerTail.push(node);
|
|
2418
2578
|
} else {
|
|
2419
2579
|
result.add(node.codeFragment());
|
|
2580
|
+
emittedBodyNode = true;
|
|
2420
2581
|
}
|
|
2421
2582
|
});
|
|
2583
|
+
if (!emittedBodyNode) {
|
|
2584
|
+
result.add("pass");
|
|
2585
|
+
}
|
|
2586
|
+
for (const h of handlerTail) {
|
|
2587
|
+
result.decreaseIndent();
|
|
2588
|
+
result.add(h.codeFragment());
|
|
2589
|
+
result.increaseIndent();
|
|
2590
|
+
}
|
|
2422
2591
|
}
|
|
2423
2592
|
} else {
|
|
2424
2593
|
result.add("pass");
|
|
@@ -2556,6 +2725,15 @@ class ASTCondBlock extends ASTBlock {
|
|
|
2556
2725
|
node.name == '__exception__') {
|
|
2557
2726
|
return false;
|
|
2558
2727
|
}
|
|
2728
|
+
// Inside an except body, an empty else block is a control-flow
|
|
2729
|
+
// artifact (JUMP_ABSOLUTE leaving the handler with no body);
|
|
2730
|
+
// keeping it leaves the handler visually empty without `pass`.
|
|
2731
|
+
if (this.blockType == ASTBlock.BlockType.Except &&
|
|
2732
|
+
node instanceof ASTBlock &&
|
|
2733
|
+
node.blockType === ASTBlock.BlockType.Else &&
|
|
2734
|
+
node.empty()) {
|
|
2735
|
+
return false;
|
|
2736
|
+
}
|
|
2559
2737
|
return node && !node.skip && node.codeFragment;
|
|
2560
2738
|
});
|
|
2561
2739
|
|
|
@@ -2654,7 +2832,12 @@ class ASTIterBlock extends ASTBlock {
|
|
|
2654
2832
|
result.lastLineAppend(this.iter?.codeFragment() || "##ERROR##");
|
|
2655
2833
|
result.lastLineAppend(":");
|
|
2656
2834
|
result.increaseIndent();
|
|
2657
|
-
|
|
2835
|
+
const body = this.nodes.filter(Boolean);
|
|
2836
|
+
if (body.length === 0) {
|
|
2837
|
+
result.add("pass");
|
|
2838
|
+
} else {
|
|
2839
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
2840
|
+
}
|
|
2658
2841
|
result.decreaseIndent();
|
|
2659
2842
|
|
|
2660
2843
|
return result;
|
|
@@ -2753,9 +2936,70 @@ class ASTWithBlock extends ASTBlock {
|
|
|
2753
2936
|
|
|
2754
2937
|
result.lastLineAppend(":");
|
|
2755
2938
|
result.increaseIndent();
|
|
2756
|
-
this.nodes.filter(Boolean)
|
|
2939
|
+
const body = this.nodes.filter(Boolean);
|
|
2940
|
+
if (body.length === 0) {
|
|
2941
|
+
result.add("pass");
|
|
2942
|
+
} else {
|
|
2943
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
2944
|
+
}
|
|
2757
2945
|
result.decreaseIndent();
|
|
2758
|
-
|
|
2946
|
+
|
|
2947
|
+
return result;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
toString() {
|
|
2951
|
+
return `${this.type_str} block: {${this.start} - ${this.end}}`;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
class ASTAsyncWithBlock extends ASTBlock {
|
|
2956
|
+
|
|
2957
|
+
m_expr = null;
|
|
2958
|
+
m_var = null;
|
|
2959
|
+
|
|
2960
|
+
constructor(start = 0, end = 0) {
|
|
2961
|
+
super(ASTBlock.BlockType.AsyncWith, start, end);
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2964
|
+
get expr() {
|
|
2965
|
+
return this.m_expr;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
set expr(value) {
|
|
2969
|
+
this.m_expr = value;
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
get var() {
|
|
2973
|
+
return this.m_var;
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
set var(value) {
|
|
2977
|
+
this.m_var = value;
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
codeFragment() {
|
|
2981
|
+
let result = new PycResult();
|
|
2982
|
+
result.doNotIndent = true;
|
|
2983
|
+
|
|
2984
|
+
result.lastLineAppend("async with ", false);
|
|
2985
|
+
const exprCode = this.expr?.codeFragment ? this.expr.codeFragment() : "None";
|
|
2986
|
+
result.lastLineAppend(exprCode);
|
|
2987
|
+
|
|
2988
|
+
if (this.var) {
|
|
2989
|
+
result.lastLineAppend(" as ", false);
|
|
2990
|
+
result.lastLineAppend(this.var.codeFragment());
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
result.lastLineAppend(":");
|
|
2994
|
+
result.increaseIndent();
|
|
2995
|
+
const body = this.nodes.filter(Boolean);
|
|
2996
|
+
if (body.length === 0) {
|
|
2997
|
+
result.add("pass");
|
|
2998
|
+
} else {
|
|
2999
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
3000
|
+
}
|
|
3001
|
+
result.decreaseIndent();
|
|
3002
|
+
|
|
2759
3003
|
return result;
|
|
2760
3004
|
}
|
|
2761
3005
|
|
|
@@ -2769,6 +3013,7 @@ class ASTComprehension extends ASTNode {
|
|
|
2769
3013
|
static LIST = 0;
|
|
2770
3014
|
static SET = 1;
|
|
2771
3015
|
static DICT = 2;
|
|
3016
|
+
static GENERATOR = 3;
|
|
2772
3017
|
|
|
2773
3018
|
m_kind = ASTComprehension.LIST;
|
|
2774
3019
|
m_key = null;
|
|
@@ -2820,6 +3065,9 @@ class ASTComprehension extends ASTNode {
|
|
|
2820
3065
|
if ([ASTComprehension.SET, ASTComprehension.DICT].includes(this.kind)) {
|
|
2821
3066
|
openingBracket = '{';
|
|
2822
3067
|
closingBracket = '}';
|
|
3068
|
+
} else if (this.kind == ASTComprehension.GENERATOR) {
|
|
3069
|
+
openingBracket = '(';
|
|
3070
|
+
closingBracket = ')';
|
|
2823
3071
|
}
|
|
2824
3072
|
|
|
2825
3073
|
if (!this.result) {
|
|
@@ -2829,7 +3077,8 @@ class ASTComprehension extends ASTNode {
|
|
|
2829
3077
|
let result = `${openingBracket}${this.kind == ASTComprehension.DICT ? (this.key?.codeFragment() || "##ERROR##") + ": " : ""}${this.result.codeFragment?.() || "##ERROR##"}`;
|
|
2830
3078
|
|
|
2831
3079
|
result += this.generators.map(gen => {
|
|
2832
|
-
let
|
|
3080
|
+
let asyncPrefix = gen.blockType == ASTBlock.BlockType.AsyncFor ? 'async ' : '';
|
|
3081
|
+
let genString = ` ${asyncPrefix}for ${gen.index?.codeFragment?.() || "##ERROR##"} in ${gen.iter?.codeFragment?.() || "##ERROR##"}`;
|
|
2833
3082
|
|
|
2834
3083
|
if (gen.condition) {
|
|
2835
3084
|
genString += ` if ${gen.condition.codeFragment?.() || "##ERROR##"}`;
|
|
@@ -2924,12 +3173,27 @@ class ASTFormattedValue extends ASTNode {
|
|
|
2924
3173
|
return this.m_format_spec;
|
|
2925
3174
|
}
|
|
2926
3175
|
|
|
2927
|
-
codeFragment() {
|
|
3176
|
+
codeFragment(outerQuote = null) {
|
|
2928
3177
|
// Format: {value} or {value!r} or {value:.2f}
|
|
3178
|
+
// `outerQuote` is set only when this FormattedValue is being rendered
|
|
3179
|
+
// inside an enclosing f-string; nested strings must then use the
|
|
3180
|
+
// opposite delimiter so the outer string does not close prematurely.
|
|
3181
|
+
const innerQuote = outerQuote === '"' ? "'" :
|
|
3182
|
+
outerQuote === "'" ? '"' : '"';
|
|
2929
3183
|
let result = "{";
|
|
2930
3184
|
|
|
2931
3185
|
if (this.m_val) {
|
|
2932
|
-
|
|
3186
|
+
if (this.m_val instanceof ASTJoinedStr) {
|
|
3187
|
+
// Only force a flipped quote when we actually have an outer
|
|
3188
|
+
// f-string; otherwise let the nested string keep its default.
|
|
3189
|
+
if (outerQuote) {
|
|
3190
|
+
result += this.m_val.codeFragment(innerQuote);
|
|
3191
|
+
} else {
|
|
3192
|
+
result += this.m_val.codeFragment();
|
|
3193
|
+
}
|
|
3194
|
+
} else {
|
|
3195
|
+
result += this.m_val.codeFragment();
|
|
3196
|
+
}
|
|
2933
3197
|
}
|
|
2934
3198
|
|
|
2935
3199
|
// Add conversion flag (!s, !r, !a)
|
|
@@ -2950,7 +3214,10 @@ class ASTFormattedValue extends ASTNode {
|
|
|
2950
3214
|
result += ":";
|
|
2951
3215
|
// Format spec can be ASTJoinedStr (nested f-string) or string constant
|
|
2952
3216
|
if (this.m_format_spec instanceof ASTJoinedStr) {
|
|
2953
|
-
|
|
3217
|
+
// Format-spec content is already inside the outer f-string's
|
|
3218
|
+
// braces, so its literal parts must not use the outer quote.
|
|
3219
|
+
const specQuote = outerQuote ? innerQuote : '"';
|
|
3220
|
+
result += this.m_format_spec.codeFragment(specQuote, true);
|
|
2954
3221
|
} else if (this.m_format_spec instanceof ASTObject) {
|
|
2955
3222
|
// String constant like ".2f"
|
|
2956
3223
|
result += this.m_format_spec.object.Value;
|
|
@@ -2983,9 +3250,14 @@ class ASTJoinedStr extends ASTNode {
|
|
|
2983
3250
|
get lastLine() {
|
|
2984
3251
|
return this.values[this.values.length - 1]?.lastLine;
|
|
2985
3252
|
}
|
|
2986
|
-
codeFragment() {
|
|
2987
|
-
//
|
|
2988
|
-
|
|
3253
|
+
codeFragment(quoteChar = '"', bareInnerForFormatSpec = false) {
|
|
3254
|
+
// PEP 750 t-strings use t"..." prefix; otherwise f-string.
|
|
3255
|
+
// `quoteChar` lets nested f-strings pick the opposite delimiter so
|
|
3256
|
+
// they don't collide with the enclosing string. `bareInnerForFormatSpec`
|
|
3257
|
+
// skips the f"..." wrapper entirely (used when this ASTJoinedStr is a
|
|
3258
|
+
// format spec nested inside {...}).
|
|
3259
|
+
const prefix = this.isTemplateString ? 't' : 'f';
|
|
3260
|
+
let result = bareInnerForFormatSpec ? "" : `${prefix}${quoteChar}`;
|
|
2989
3261
|
|
|
2990
3262
|
// Values are in reverse order (BUILD_STRING pops from stack)
|
|
2991
3263
|
// So we need to reverse them
|
|
@@ -2994,6 +3266,12 @@ class ASTJoinedStr extends ASTNode {
|
|
|
2994
3266
|
for (let i = 0; i < values.length; i++) {
|
|
2995
3267
|
let value = values[i];
|
|
2996
3268
|
|
|
3269
|
+
// Stack underflow during BUILD_STRING leaves undefined slots; skip them
|
|
3270
|
+
// rather than crashing post-decompile passes that re-render this node.
|
|
3271
|
+
if (value == null) {
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
|
|
2997
3275
|
if (value instanceof ASTFormattedValue) {
|
|
2998
3276
|
// Check for f-string = debugging pattern (Python 3.8+)
|
|
2999
3277
|
// Pattern: literal ending with "varname=" followed by {varname!r}
|
|
@@ -3013,8 +3291,10 @@ class ASTJoinedStr extends ASTNode {
|
|
|
3013
3291
|
let prefix = prevStr.substring(0, prevStr.length - match[0].length);
|
|
3014
3292
|
|
|
3015
3293
|
// Remove the previously added literal and replace with prefix + {var=}
|
|
3294
|
+
let quoteEsc = quoteChar === '"' ? /"/g : /'/g;
|
|
3295
|
+
let quoteReplace = quoteChar === '"' ? '\\"' : "\\'";
|
|
3016
3296
|
let beforeLiteral = result.lastIndexOf(prevStr.replace(/\\/g, '\\\\')
|
|
3017
|
-
.replace(
|
|
3297
|
+
.replace(quoteEsc, quoteReplace).replace(/\n/g, '\\n').replace(/\t/g, '\\t'));
|
|
3018
3298
|
|
|
3019
3299
|
if (beforeLiteral !== -1) {
|
|
3020
3300
|
result = result.substring(0, beforeLiteral);
|
|
@@ -3023,7 +3303,7 @@ class ASTJoinedStr extends ASTNode {
|
|
|
3023
3303
|
// Add prefix (escaped) if present
|
|
3024
3304
|
if (prefix) {
|
|
3025
3305
|
let escapedPrefix = prefix.replace(/\\/g, '\\\\');
|
|
3026
|
-
escapedPrefix = escapedPrefix.replace(
|
|
3306
|
+
escapedPrefix = escapedPrefix.replace(quoteEsc, quoteReplace);
|
|
3027
3307
|
escapedPrefix = escapedPrefix.replace(/\n/g, '\\n');
|
|
3028
3308
|
escapedPrefix = escapedPrefix.replace(/\t/g, '\\t');
|
|
3029
3309
|
result += escapedPrefix;
|
|
@@ -3035,16 +3315,25 @@ class ASTJoinedStr extends ASTNode {
|
|
|
3035
3315
|
}
|
|
3036
3316
|
}
|
|
3037
3317
|
|
|
3038
|
-
// {expression} part
|
|
3039
|
-
|
|
3040
|
-
|
|
3318
|
+
// {expression} part - pass our own quote char so nested
|
|
3319
|
+
// f-strings / strings inside pick a compatible delimiter.
|
|
3320
|
+
result += value.codeFragment(quoteChar);
|
|
3321
|
+
// result above calls ASTFormattedValue.codeFragment(outerQuote).
|
|
3322
|
+
} else if (value instanceof ASTObject && ['Py_String', 'Py_Unicode'].includes(value.object?.ClassName)) {
|
|
3041
3323
|
// Literal string part - need to escape special chars
|
|
3042
3324
|
let str = value.object.Value;
|
|
3043
|
-
// Escape backslashes and
|
|
3325
|
+
// Escape backslashes and the current quote char
|
|
3044
3326
|
str = str.replace(/\\/g, '\\\\');
|
|
3045
|
-
|
|
3327
|
+
if (quoteChar === '"') {
|
|
3328
|
+
str = str.replace(/"/g, '\\"');
|
|
3329
|
+
} else {
|
|
3330
|
+
str = str.replace(/'/g, "\\'");
|
|
3331
|
+
}
|
|
3046
3332
|
str = str.replace(/\n/g, '\\n');
|
|
3333
|
+
str = str.replace(/\r/g, '\\r');
|
|
3047
3334
|
str = str.replace(/\t/g, '\\t');
|
|
3335
|
+
str = str.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, c =>
|
|
3336
|
+
'\\x' + c.charCodeAt(0).toString(16).padStart(2, '0'));
|
|
3048
3337
|
result += str;
|
|
3049
3338
|
} else {
|
|
3050
3339
|
// Fallback for unexpected types
|
|
@@ -3052,7 +3341,9 @@ class ASTJoinedStr extends ASTNode {
|
|
|
3052
3341
|
}
|
|
3053
3342
|
}
|
|
3054
3343
|
|
|
3055
|
-
|
|
3344
|
+
if (!bareInnerForFormatSpec) {
|
|
3345
|
+
result += quoteChar;
|
|
3346
|
+
}
|
|
3056
3347
|
return result;
|
|
3057
3348
|
}
|
|
3058
3349
|
|
|
@@ -3379,7 +3670,7 @@ class ASTPattern extends ASTNode {
|
|
|
3379
3670
|
return '##ERROR##';
|
|
3380
3671
|
|
|
3381
3672
|
default:
|
|
3382
|
-
|
|
3673
|
+
throw new Error(`ASTPattern.codeFragment(): unsupported pattern type '${this.m_type}'`);
|
|
3383
3674
|
}
|
|
3384
3675
|
}
|
|
3385
3676
|
|
|
@@ -3412,6 +3703,7 @@ module.exports = {
|
|
|
3412
3703
|
ASTList,
|
|
3413
3704
|
ASTSet,
|
|
3414
3705
|
ASTMap,
|
|
3706
|
+
ASTMapUnpack,
|
|
3415
3707
|
ASTKwNamesMap,
|
|
3416
3708
|
ASTConstMap,
|
|
3417
3709
|
ASTSubscr,
|
|
@@ -3425,6 +3717,7 @@ module.exports = {
|
|
|
3425
3717
|
ASTIterBlock,
|
|
3426
3718
|
ASTContainerBlock,
|
|
3427
3719
|
ASTWithBlock,
|
|
3720
|
+
ASTAsyncWithBlock,
|
|
3428
3721
|
ASTComprehension,
|
|
3429
3722
|
ASTLoadBuildClass,
|
|
3430
3723
|
ASTAwaitable,
|