@lewin671/python-vm 0.1.2 → 0.1.3
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/README.md +58 -56
- package/README_zh-CN.md +58 -57
- package/dist/common/string-token.d.ts +5 -0
- package/dist/common/string-token.d.ts.map +1 -0
- package/dist/common/string-token.js +23 -0
- package/dist/common/string-token.js.map +1 -0
- package/dist/compiler/cfg_builder.d.ts +48 -0
- package/dist/compiler/cfg_builder.d.ts.map +1 -0
- package/dist/compiler/cfg_builder.js +1462 -0
- package/dist/compiler/cfg_builder.js.map +1 -0
- package/dist/compiler/compiler.d.ts.map +1 -0
- package/dist/compiler/compiler.js +27 -0
- package/dist/compiler/compiler.js.map +1 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/compiler/linearizer.d.ts +7 -0
- package/dist/compiler/linearizer.d.ts.map +1 -0
- package/dist/compiler/linearizer.js +112 -0
- package/dist/compiler/linearizer.js.map +1 -0
- package/dist/compiler/serializer.d.ts +17 -0
- package/dist/compiler/serializer.d.ts.map +1 -0
- package/dist/compiler/serializer.js +70 -0
- package/dist/compiler/serializer.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -8
- package/dist/index.js.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +25 -11
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/parser/expressions.d.ts.map +1 -1
- package/dist/parser/expressions.js +5 -2
- package/dist/parser/expressions.js.map +1 -1
- package/dist/parser/parser.js +3 -3
- package/dist/parser/parser.js.map +1 -1
- package/dist/parser/statements.d.ts.map +1 -1
- package/dist/parser/statements.js +60 -22
- package/dist/parser/statements.js.map +1 -1
- package/dist/python_compiler.d.ts +32 -0
- package/dist/python_compiler.d.ts.map +1 -0
- package/dist/{compiler.js → python_compiler.js} +19 -6
- package/dist/python_compiler.js.map +1 -0
- package/dist/types/ast.d.ts +286 -44
- package/dist/types/ast.d.ts.map +1 -1
- package/dist/types/ast.js +43 -36
- package/dist/types/ast.js.map +1 -1
- package/dist/types/bytecode.d.ts +106 -21
- package/dist/types/bytecode.d.ts.map +1 -1
- package/dist/types/bytecode.js +111 -23
- package/dist/types/bytecode.js.map +1 -1
- package/dist/types/cfg.d.ts +20 -0
- package/dist/types/cfg.d.ts.map +1 -0
- package/dist/types/cfg.js +3 -0
- package/dist/types/cfg.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/vm/builtins.d.ts.map +1 -1
- package/dist/vm/builtins.js +23 -7
- package/dist/vm/builtins.js.map +1 -1
- package/dist/vm/callable.d.ts +6 -6
- package/dist/vm/callable.d.ts.map +1 -1
- package/dist/vm/callable.js +28 -7
- package/dist/vm/callable.js.map +1 -1
- package/dist/vm/execution-helpers.d.ts +17 -0
- package/dist/vm/execution-helpers.d.ts.map +1 -0
- package/dist/vm/execution-helpers.js +334 -0
- package/dist/vm/execution-helpers.js.map +1 -0
- package/dist/vm/execution.d.ts +4 -12
- package/dist/vm/execution.d.ts.map +1 -1
- package/dist/vm/execution.js +764 -255
- package/dist/vm/execution.js.map +1 -1
- package/dist/vm/expression-generator.d.ts +3 -1
- package/dist/vm/expression-generator.d.ts.map +1 -1
- package/dist/vm/expression-generator.js.map +1 -1
- package/dist/vm/expressions.d.ts +6 -6
- package/dist/vm/expressions.d.ts.map +1 -1
- package/dist/vm/expressions.js +29 -54
- package/dist/vm/expressions.js.map +1 -1
- package/dist/vm/imports.d.ts +4 -4
- package/dist/vm/imports.d.ts.map +1 -1
- package/dist/vm/imports.js.map +1 -1
- package/dist/vm/operations.d.ts +11 -10
- package/dist/vm/operations.d.ts.map +1 -1
- package/dist/vm/operations.js +83 -22
- package/dist/vm/operations.js.map +1 -1
- package/dist/vm/runtime-types.d.ts +64 -26
- package/dist/vm/runtime-types.d.ts.map +1 -1
- package/dist/vm/runtime-types.js +166 -19
- package/dist/vm/runtime-types.js.map +1 -1
- package/dist/vm/statements.d.ts +5 -5
- package/dist/vm/statements.d.ts.map +1 -1
- package/dist/vm/statements.js +95 -10
- package/dist/vm/statements.js.map +1 -1
- package/dist/vm/truthy.d.ts +2 -2
- package/dist/vm/truthy.d.ts.map +1 -1
- package/dist/vm/truthy.js +1 -1
- package/dist/vm/truthy.js.map +1 -1
- package/dist/vm/value-utils.d.ts +23 -25
- package/dist/vm/value-utils.d.ts.map +1 -1
- package/dist/vm/value-utils.js +216 -47
- package/dist/vm/value-utils.js.map +1 -1
- package/dist/vm/vm.d.ts +7 -3
- package/dist/vm/vm.d.ts.map +1 -1
- package/dist/vm/vm.js +3 -0
- package/dist/vm/vm.js.map +1 -1
- package/package.json +3 -3
- package/dist/compiler.d.ts +0 -20
- package/dist/compiler.d.ts.map +0 -1
- package/dist/compiler.js.map +0 -1
- package/dist/compiler_module/compiler.d.ts.map +0 -1
- package/dist/compiler_module/compiler.js +0 -22
- package/dist/compiler_module/compiler.js.map +0 -1
- package/dist/compiler_module/index.d.ts.map +0 -1
- package/dist/compiler_module/index.js.map +0 -1
- /package/dist/{compiler_module → compiler}/compiler.d.ts +0 -0
- /package/dist/{compiler_module → compiler}/index.d.ts +0 -0
- /package/dist/{compiler_module → compiler}/index.js +0 -0
package/dist/vm/execution.js
CHANGED
|
@@ -1,283 +1,792 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeBlock = exports.executeStatementGenerator = exports.executeBlockGenerator = exports.executeStatement = exports.evaluateExpression = exports.matchPattern = exports.applyBindings = exports.matchValueEquals = exports.iterableToArray = exports.applyCompare = void 0;
|
|
3
4
|
exports.execute = execute;
|
|
4
|
-
exports.
|
|
5
|
-
exports.iterableToArray = iterableToArray;
|
|
6
|
-
exports.matchValueEquals = matchValueEquals;
|
|
7
|
-
exports.matchPattern = matchPattern;
|
|
8
|
-
exports.applyBindings = applyBindings;
|
|
9
|
-
exports.executeBlockGenerator = executeBlockGenerator;
|
|
10
|
-
exports.executeStatementGenerator = executeStatementGenerator;
|
|
5
|
+
exports.executeFrame = executeFrame;
|
|
11
6
|
const types_1 = require("../types");
|
|
12
7
|
const runtime_types_1 = require("./runtime-types");
|
|
13
|
-
const runtime_types_2 = require("./runtime-types");
|
|
14
8
|
function execute(bytecode) {
|
|
15
|
-
if (!bytecode.ast) {
|
|
16
|
-
throw new Error('Bytecode missing AST');
|
|
17
|
-
}
|
|
18
9
|
const globalScope = new runtime_types_1.Scope();
|
|
19
10
|
this.installBuiltins(globalScope);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function executeBlock(body, scope) {
|
|
23
|
-
let lastValue = null;
|
|
24
|
-
for (const stmt of body) {
|
|
25
|
-
lastValue = this.executeStatement(stmt, scope);
|
|
26
|
-
}
|
|
27
|
-
return lastValue;
|
|
28
|
-
}
|
|
29
|
-
function iterableToArray(iterable) {
|
|
30
|
-
if (iterable instanceof runtime_types_2.PyDict)
|
|
31
|
-
return Array.from(iterable.keys());
|
|
32
|
-
if (iterable instanceof Set)
|
|
33
|
-
return Array.from(iterable.values());
|
|
34
|
-
if (Array.isArray(iterable))
|
|
35
|
-
return iterable;
|
|
36
|
-
if (iterable && typeof iterable[Symbol.iterator] === 'function')
|
|
37
|
-
return Array.from(iterable);
|
|
38
|
-
throw new Error('Object is not iterable');
|
|
11
|
+
const frame = new runtime_types_1.Frame(bytecode, globalScope);
|
|
12
|
+
return this.executeFrame(frame);
|
|
39
13
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
if (left instanceof Map && right instanceof Map) {
|
|
51
|
-
if (left.size !== right.size)
|
|
52
|
-
return false;
|
|
53
|
-
for (const [k, v] of left.entries()) {
|
|
54
|
-
if (!right.has(k) || !matchValueEquals(v, right.get(k)))
|
|
55
|
-
return false;
|
|
14
|
+
function executeFrame(frame) {
|
|
15
|
+
const { instructions, constants, names, varnames } = frame.bytecode;
|
|
16
|
+
// console.log('Executing frame with names:', names);
|
|
17
|
+
// Populate scope with globals and nonlocals from bytecode
|
|
18
|
+
if (frame.bytecode.globals) {
|
|
19
|
+
for (const name of frame.bytecode.globals) {
|
|
20
|
+
frame.scope.globals.add(name);
|
|
56
21
|
}
|
|
57
|
-
return true;
|
|
58
22
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
switch (node.type) {
|
|
63
|
-
case types_1.ASTNodeType.MATCH_PATTERN_VALUE:
|
|
64
|
-
return { matched: matchValueEquals(value, this.evaluateExpression(node.value, scope)), bindings: new Map() };
|
|
65
|
-
case types_1.ASTNodeType.MATCH_PATTERN_WILDCARD:
|
|
66
|
-
return { matched: true, bindings: new Map() };
|
|
67
|
-
case types_1.ASTNodeType.MATCH_PATTERN_CAPTURE:
|
|
68
|
-
return { matched: true, bindings: new Map([[node.name, value]]) };
|
|
69
|
-
case types_1.ASTNodeType.MATCH_PATTERN_OR: {
|
|
70
|
-
for (const pattern of node.patterns || []) {
|
|
71
|
-
const result = this.matchPattern(pattern, value, scope);
|
|
72
|
-
if (result.matched)
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
return { matched: false, bindings: new Map() };
|
|
76
|
-
}
|
|
77
|
-
case types_1.ASTNodeType.MATCH_PATTERN_SEQUENCE: {
|
|
78
|
-
const elements = node.elements || node.patterns;
|
|
79
|
-
if (!Array.isArray(value) || !elements || elements.length !== value.length) {
|
|
80
|
-
return { matched: false, bindings: new Map() };
|
|
81
|
-
}
|
|
82
|
-
const bindings = new Map();
|
|
83
|
-
for (let i = 0; i < elements.length; i++) {
|
|
84
|
-
const result = this.matchPattern(elements[i], value[i], scope);
|
|
85
|
-
if (!result.matched)
|
|
86
|
-
return { matched: false, bindings: new Map() };
|
|
87
|
-
for (const [k, v] of result.bindings.entries())
|
|
88
|
-
bindings.set(k, v);
|
|
89
|
-
}
|
|
90
|
-
return { matched: true, bindings };
|
|
91
|
-
}
|
|
92
|
-
case 'MatchValue':
|
|
93
|
-
return { matched: matchValueEquals(value, node.value), bindings: new Map() };
|
|
94
|
-
case 'MatchSingleton':
|
|
95
|
-
return { matched: value === node.value, bindings: new Map() };
|
|
96
|
-
case 'MatchSequence': {
|
|
97
|
-
if (!Array.isArray(value))
|
|
98
|
-
return { matched: false, bindings: new Map() };
|
|
99
|
-
if (!node.patterns || node.patterns.length !== value.length)
|
|
100
|
-
return { matched: false, bindings: new Map() };
|
|
101
|
-
const bindings = new Map();
|
|
102
|
-
for (let i = 0; i < node.patterns.length; i++) {
|
|
103
|
-
const result = this.matchPattern(node.patterns[i], value[i], scope);
|
|
104
|
-
if (!result.matched)
|
|
105
|
-
return { matched: false, bindings: new Map() };
|
|
106
|
-
for (const [k, v] of result.bindings.entries())
|
|
107
|
-
bindings.set(k, v);
|
|
108
|
-
}
|
|
109
|
-
return { matched: true, bindings };
|
|
110
|
-
}
|
|
111
|
-
case 'MatchMapping': {
|
|
112
|
-
if (!(value instanceof runtime_types_2.PyDict))
|
|
113
|
-
return { matched: false, bindings: new Map() };
|
|
114
|
-
const bindings = new Map();
|
|
115
|
-
for (const { key, pattern } of node.keys) {
|
|
116
|
-
if (!value.has(key))
|
|
117
|
-
return { matched: false, bindings: new Map() };
|
|
118
|
-
const result = this.matchPattern(pattern, value.get(key), scope);
|
|
119
|
-
if (!result.matched)
|
|
120
|
-
return { matched: false, bindings: new Map() };
|
|
121
|
-
for (const [k, v] of result.bindings.entries())
|
|
122
|
-
bindings.set(k, v);
|
|
123
|
-
}
|
|
124
|
-
return { matched: true, bindings };
|
|
125
|
-
}
|
|
126
|
-
case 'MatchAs':
|
|
127
|
-
return {
|
|
128
|
-
matched: true,
|
|
129
|
-
bindings: node.name ? new Map([[node.name, value]]) : new Map(),
|
|
130
|
-
};
|
|
131
|
-
case 'MatchClass': {
|
|
132
|
-
if (!value || !value.klass || value.klass.name !== node.className)
|
|
133
|
-
return { matched: false, bindings: new Map() };
|
|
134
|
-
const bindings = new Map();
|
|
135
|
-
for (let i = 0; i < node.patterns.length; i++) {
|
|
136
|
-
const attrName = node.kwd_attrs[i] || node.patterns[i].name;
|
|
137
|
-
const attrValue = value.attributes.get(attrName);
|
|
138
|
-
const result = this.matchPattern(node.patterns[i], attrValue, scope);
|
|
139
|
-
if (!result.matched)
|
|
140
|
-
return { matched: false, bindings: new Map() };
|
|
141
|
-
for (const [k, v] of result.bindings.entries())
|
|
142
|
-
bindings.set(k, v);
|
|
143
|
-
}
|
|
144
|
-
return { matched: true, bindings };
|
|
23
|
+
if (frame.bytecode.nonlocals) {
|
|
24
|
+
for (const name of frame.bytecode.nonlocals) {
|
|
25
|
+
frame.scope.nonlocals.add(name);
|
|
145
26
|
}
|
|
146
|
-
default:
|
|
147
|
-
return { matched: false, bindings: new Map() };
|
|
148
27
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
: this.evaluateExpression(node.value, scope);
|
|
175
|
-
for (const target of node.targets) {
|
|
176
|
-
this.assignTarget(target, value, scope);
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
case types_1.ASTNodeType.IF_STATEMENT: {
|
|
181
|
-
const test = this.expressionHasYield(node.test)
|
|
182
|
-
? yield* this.evaluateExpressionGenerator(node.test, scope)
|
|
183
|
-
: this.evaluateExpression(node.test, scope);
|
|
184
|
-
if (this.isTruthy(test, scope)) {
|
|
185
|
-
yield* this.executeBlockGenerator(node.body, scope);
|
|
186
|
-
return null;
|
|
28
|
+
let lastValue = null;
|
|
29
|
+
const renderFString = (template, scope) => {
|
|
30
|
+
return template.replace(/\{([^}]+)\}/g, (_m, expr) => {
|
|
31
|
+
const { rawExpr, rawSpec } = this.splitFormatSpec(expr);
|
|
32
|
+
// Create a temporary scope that includes local variables for f-string evaluation
|
|
33
|
+
const evalScope = new runtime_types_1.Scope(scope);
|
|
34
|
+
if (varnames) {
|
|
35
|
+
for (let i = 0; i < varnames.length; i++) {
|
|
36
|
+
const varname = varnames[i];
|
|
37
|
+
if (varname === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
if (scope.values.has(varname)) {
|
|
40
|
+
const val = scope.values.get(varname);
|
|
41
|
+
if (process.env['DEBUG_NONLOCAL']) {
|
|
42
|
+
console.log(`renderFString: varname=${varname}, scope.values.get=${val}`);
|
|
43
|
+
}
|
|
44
|
+
evalScope.values.set(varname, val);
|
|
45
|
+
}
|
|
46
|
+
else if (frame.locals[i] !== undefined) {
|
|
47
|
+
if (process.env['DEBUG_NONLOCAL']) {
|
|
48
|
+
console.log(`renderFString: varname=${varname}, frame.locals[${i}]=${frame.locals[i]}`);
|
|
49
|
+
}
|
|
50
|
+
evalScope.values.set(varname, frame.locals[i]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
187
53
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
54
|
+
const inner = this.evaluateExpressionString(rawExpr.trim(), evalScope);
|
|
55
|
+
return this.applyFormatSpec(inner, rawSpec ? rawSpec.trim() : '');
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const normalizeThrown = (err) => {
|
|
59
|
+
if (err instanceof runtime_types_1.PyException) {
|
|
60
|
+
try {
|
|
61
|
+
const klass = frame.scope.get(err.pyType);
|
|
62
|
+
if (klass instanceof runtime_types_1.PyClass) {
|
|
63
|
+
const inst = new runtime_types_1.PyInstance(klass);
|
|
64
|
+
if (err.message)
|
|
65
|
+
inst.attributes.set('message', err.message);
|
|
66
|
+
return inst;
|
|
195
67
|
}
|
|
196
68
|
}
|
|
197
|
-
|
|
198
|
-
|
|
69
|
+
catch {
|
|
70
|
+
// Ignore normalization failures
|
|
199
71
|
}
|
|
200
|
-
return null;
|
|
201
72
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
73
|
+
return err;
|
|
74
|
+
};
|
|
75
|
+
const dispatchException = (err) => {
|
|
76
|
+
if (frame.blockStack.length === 0)
|
|
77
|
+
return false;
|
|
78
|
+
const block = frame.blockStack.pop();
|
|
79
|
+
frame.stack.length = block.stackHeight;
|
|
80
|
+
frame.stack.push(normalizeThrown(err));
|
|
81
|
+
frame.pc = block.handler;
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
while (frame.pc < instructions.length) {
|
|
85
|
+
const instr = instructions[frame.pc++];
|
|
86
|
+
if (!instr)
|
|
87
|
+
break;
|
|
88
|
+
const { opcode, arg } = instr;
|
|
89
|
+
try {
|
|
90
|
+
switch (opcode) {
|
|
91
|
+
case types_1.OpCode.LOAD_CONST:
|
|
92
|
+
{
|
|
93
|
+
const val = constants[arg];
|
|
94
|
+
if (val && typeof val === 'object' && typeof val.__fstring__ === 'string') {
|
|
95
|
+
frame.stack.push(renderFString(val.__fstring__, frame.scope));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
frame.stack.push(val);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case types_1.OpCode.LOAD_NAME: {
|
|
103
|
+
const name = names[arg];
|
|
104
|
+
const val = frame.scope.get(name);
|
|
105
|
+
// console.log(`LOAD_NAME ${name} -> ${val}`);
|
|
106
|
+
frame.stack.push(val);
|
|
107
|
+
break;
|
|
218
108
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
109
|
+
case types_1.OpCode.STORE_NAME:
|
|
110
|
+
frame.scope.set(names[arg], frame.stack.pop());
|
|
111
|
+
break;
|
|
112
|
+
case types_1.OpCode.LOAD_GLOBAL: {
|
|
113
|
+
const name = names[arg];
|
|
114
|
+
// Find the global (topmost) scope
|
|
115
|
+
let globalScope = frame.scope;
|
|
116
|
+
while (globalScope.parent !== null) {
|
|
117
|
+
globalScope = globalScope.parent;
|
|
118
|
+
}
|
|
119
|
+
const val = globalScope.get(name);
|
|
120
|
+
frame.stack.push(val);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case types_1.OpCode.STORE_GLOBAL: {
|
|
124
|
+
const name = names[arg];
|
|
125
|
+
// Find the global (topmost) scope
|
|
126
|
+
let globalScope = frame.scope;
|
|
127
|
+
while (globalScope.parent !== null) {
|
|
128
|
+
globalScope = globalScope.parent;
|
|
129
|
+
}
|
|
130
|
+
globalScope.set(name, frame.stack.pop());
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case types_1.OpCode.LOAD_FAST: {
|
|
134
|
+
const varname = varnames[arg];
|
|
135
|
+
if (varname !== undefined && frame.scope.values.has(varname)) {
|
|
136
|
+
const val = frame.scope.values.get(varname);
|
|
137
|
+
frame.locals[arg] = val;
|
|
138
|
+
frame.stack.push(val);
|
|
234
139
|
break;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
140
|
+
}
|
|
141
|
+
const val = frame.locals[arg];
|
|
142
|
+
if (val === undefined) {
|
|
143
|
+
throw new runtime_types_1.PyException('UnboundLocalError', `local variable '${varname}' referenced before assignment`);
|
|
144
|
+
}
|
|
145
|
+
frame.stack.push(val);
|
|
146
|
+
break;
|
|
238
147
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
148
|
+
case types_1.OpCode.STORE_FAST: {
|
|
149
|
+
const val = frame.stack.pop();
|
|
150
|
+
frame.locals[arg] = val;
|
|
151
|
+
if (varnames && varnames[arg] !== undefined) {
|
|
152
|
+
frame.scope.values.set(varnames[arg], val);
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case types_1.OpCode.UNPACK_SEQUENCE: {
|
|
157
|
+
const seq = frame.stack.pop();
|
|
158
|
+
const items = Array.isArray(seq) ? seq : Array.from(seq);
|
|
159
|
+
if (items.length !== arg) {
|
|
160
|
+
throw new runtime_types_1.PyException('ValueError', `not enough values to unpack (expected ${arg}, got ${items.length})`);
|
|
161
|
+
}
|
|
162
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
163
|
+
frame.stack.push(items[i]);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case types_1.OpCode.UNPACK_EX: {
|
|
168
|
+
const seq = frame.stack.pop();
|
|
169
|
+
const items = Array.isArray(seq) ? seq : Array.from(seq);
|
|
170
|
+
const beforeCount = (arg >> 8) & 0xff;
|
|
171
|
+
const afterCount = arg & 0xff;
|
|
172
|
+
if (items.length < beforeCount + afterCount) {
|
|
173
|
+
throw new runtime_types_1.PyException('ValueError', 'not enough values to unpack');
|
|
174
|
+
}
|
|
175
|
+
const middle = items.slice(beforeCount, items.length - afterCount);
|
|
176
|
+
// Push in reverse order of assignment popping:
|
|
177
|
+
// suffix values (last..first), then middle list, then prefix values (last..first).
|
|
178
|
+
for (let i = afterCount - 1; i >= 0; i--) {
|
|
179
|
+
frame.stack.push(items[items.length - afterCount + i]);
|
|
180
|
+
}
|
|
181
|
+
frame.stack.push(middle);
|
|
182
|
+
for (let i = beforeCount - 1; i >= 0; i--) {
|
|
183
|
+
frame.stack.push(items[i]);
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case types_1.OpCode.LOAD_ATTR: {
|
|
188
|
+
const obj = frame.stack.pop();
|
|
189
|
+
frame.stack.push(this.getAttribute(obj, names[arg], frame.scope));
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case types_1.OpCode.STORE_ATTR: {
|
|
193
|
+
const val = frame.stack.pop();
|
|
194
|
+
const obj = frame.stack.pop();
|
|
195
|
+
this.setAttribute(obj, names[arg], val);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case types_1.OpCode.LOAD_SUBSCR: {
|
|
199
|
+
const index = frame.stack.pop();
|
|
200
|
+
const obj = frame.stack.pop();
|
|
201
|
+
frame.stack.push(this.getSubscript(obj, index));
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case types_1.OpCode.STORE_SUBSCR: {
|
|
205
|
+
const index = frame.stack.pop();
|
|
206
|
+
const obj = frame.stack.pop();
|
|
207
|
+
const val = frame.stack.pop();
|
|
208
|
+
// Check if object is a tuple (immutable)
|
|
209
|
+
if (Array.isArray(obj) && obj.__tuple__) {
|
|
210
|
+
throw new runtime_types_1.PyException('TypeError', `'tuple' object does not support item assignment`);
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(obj)) {
|
|
213
|
+
// Handle slice assignment
|
|
214
|
+
if (index && (index.__slice__ || index.type === types_1.ASTNodeType.SLICE)) {
|
|
215
|
+
const start = index.start !== undefined ? index.start : null;
|
|
216
|
+
const end = index.end !== undefined ? index.end : null;
|
|
217
|
+
const step = index.step !== undefined ? index.step : 1;
|
|
218
|
+
const bounds = this.computeSliceBounds(obj.length, start, end, step);
|
|
219
|
+
const indices = this.computeSliceIndices(obj.length, start, end, step);
|
|
220
|
+
// For extended slices (step != 1), replacement must have same length
|
|
221
|
+
if (bounds.step !== 1) {
|
|
222
|
+
if (!Array.isArray(val) || val.length !== indices.length) {
|
|
223
|
+
throw new runtime_types_1.PyException('ValueError', `attempt to assign sequence of size ${Array.isArray(val) ? val.length : 1} to extended slice of size ${indices.length}`);
|
|
224
|
+
}
|
|
225
|
+
for (let i = 0; i < indices.length; i++) {
|
|
226
|
+
obj[indices[i]] = val[i];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Simple slice: replace the slice with the new values
|
|
231
|
+
const valArray = Array.isArray(val) ? val : [val];
|
|
232
|
+
obj.splice(bounds.start, indices.length, ...valArray);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
obj[index] = val;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else if (obj instanceof runtime_types_1.PyDict) {
|
|
240
|
+
obj.set(index, val);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
throw new runtime_types_1.PyException('TypeError', `'${typeof obj}' object does not support item assignment`);
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case types_1.OpCode.DELETE_SUBSCR: {
|
|
248
|
+
const index = frame.stack.pop();
|
|
249
|
+
const obj = frame.stack.pop();
|
|
250
|
+
if (Array.isArray(obj)) {
|
|
251
|
+
if (index && (index.__slice__ || index.type === types_1.ASTNodeType.SLICE)) {
|
|
252
|
+
const start = index.start !== undefined ? index.start : null;
|
|
253
|
+
const end = index.end !== undefined ? index.end : null;
|
|
254
|
+
const step = index.step !== undefined ? index.step : 1;
|
|
255
|
+
const bounds = this.computeSliceBounds(obj.length, start, end, step);
|
|
256
|
+
// Standard deletion of slice
|
|
257
|
+
if (bounds.step === 1) {
|
|
258
|
+
obj.splice(bounds.start, bounds.end - bounds.start);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Deleting with step != 1
|
|
262
|
+
const indices = this.computeSliceIndices(obj.length, start, end, step);
|
|
263
|
+
// We must delete from back to front to avoid index shifting problems
|
|
264
|
+
indices.sort((a, b) => b - a);
|
|
265
|
+
for (const idx of indices) {
|
|
266
|
+
obj.splice(idx, 1);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
if (typeof index !== 'number') {
|
|
272
|
+
throw new runtime_types_1.PyException('TypeError', 'list indices must be integers or slices');
|
|
273
|
+
}
|
|
274
|
+
obj.splice(index, 1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else if (obj instanceof runtime_types_1.PyDict) {
|
|
278
|
+
obj.delete(index);
|
|
279
|
+
}
|
|
280
|
+
else if (obj instanceof runtime_types_1.PySet) {
|
|
281
|
+
obj.delete(index);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
throw new runtime_types_1.PyException('TypeError', `'${typeof obj}' object does not support item deletion`);
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case types_1.OpCode.POP_TOP:
|
|
289
|
+
lastValue = frame.stack.pop();
|
|
290
|
+
break;
|
|
291
|
+
case types_1.OpCode.DUP_TOP: {
|
|
292
|
+
const val = frame.stack[frame.stack.length - 1];
|
|
293
|
+
frame.stack.push(val);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case types_1.OpCode.DUP_TOP_TWO: {
|
|
297
|
+
const top = frame.stack[frame.stack.length - 1];
|
|
298
|
+
const second = frame.stack[frame.stack.length - 2];
|
|
299
|
+
frame.stack.push(second);
|
|
300
|
+
frame.stack.push(top);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case types_1.OpCode.ROT_TWO: {
|
|
304
|
+
const a = frame.stack.pop();
|
|
305
|
+
const b = frame.stack.pop();
|
|
306
|
+
frame.stack.push(a);
|
|
307
|
+
frame.stack.push(b);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case types_1.OpCode.ROT_THREE: {
|
|
311
|
+
const a = frame.stack.pop();
|
|
312
|
+
const b = frame.stack.pop();
|
|
313
|
+
const c = frame.stack.pop();
|
|
314
|
+
frame.stack.push(a);
|
|
315
|
+
frame.stack.push(c);
|
|
316
|
+
frame.stack.push(b);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case types_1.OpCode.BINARY_ADD: {
|
|
320
|
+
const b = frame.stack.pop();
|
|
321
|
+
const a = frame.stack.pop();
|
|
322
|
+
frame.stack.push(this.applyBinary('+', a, b));
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case types_1.OpCode.BINARY_SUBTRACT: {
|
|
326
|
+
const b = frame.stack.pop();
|
|
327
|
+
const a = frame.stack.pop();
|
|
328
|
+
frame.stack.push(this.applyBinary('-', a, b));
|
|
329
|
+
break;
|
|
258
330
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
331
|
+
case types_1.OpCode.BINARY_MULTIPLY: {
|
|
332
|
+
const b = frame.stack.pop();
|
|
333
|
+
const a = frame.stack.pop();
|
|
334
|
+
frame.stack.push(this.applyBinary('*', a, b));
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case types_1.OpCode.BINARY_DIVIDE: {
|
|
338
|
+
const b = frame.stack.pop();
|
|
339
|
+
const a = frame.stack.pop();
|
|
340
|
+
frame.stack.push(this.applyBinary('/', a, b));
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
case types_1.OpCode.BINARY_FLOOR_DIVIDE: {
|
|
344
|
+
const b = frame.stack.pop();
|
|
345
|
+
const a = frame.stack.pop();
|
|
346
|
+
frame.stack.push(this.applyBinary('//', a, b));
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case types_1.OpCode.BINARY_MODULO: {
|
|
350
|
+
const b = frame.stack.pop();
|
|
351
|
+
const a = frame.stack.pop();
|
|
352
|
+
frame.stack.push(this.applyBinary('%', a, b));
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
case types_1.OpCode.BINARY_POWER: {
|
|
356
|
+
const b = frame.stack.pop();
|
|
357
|
+
const a = frame.stack.pop();
|
|
358
|
+
frame.stack.push(this.applyBinary('**', a, b));
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
case types_1.OpCode.BINARY_AND: {
|
|
362
|
+
const b = frame.stack.pop();
|
|
363
|
+
const a = frame.stack.pop();
|
|
364
|
+
frame.stack.push(this.applyBinary('&', a, b));
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case types_1.OpCode.BINARY_XOR: {
|
|
368
|
+
const b = frame.stack.pop();
|
|
369
|
+
const a = frame.stack.pop();
|
|
370
|
+
frame.stack.push(this.applyBinary('^', a, b));
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case types_1.OpCode.BINARY_OR: {
|
|
374
|
+
const b = frame.stack.pop();
|
|
375
|
+
const a = frame.stack.pop();
|
|
376
|
+
frame.stack.push(this.applyBinary('|', a, b));
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
case types_1.OpCode.BINARY_LSHIFT: {
|
|
380
|
+
const b = frame.stack.pop();
|
|
381
|
+
const a = frame.stack.pop();
|
|
382
|
+
frame.stack.push(this.applyBinary('<<', a, b));
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case types_1.OpCode.BINARY_RSHIFT: {
|
|
386
|
+
const b = frame.stack.pop();
|
|
387
|
+
const a = frame.stack.pop();
|
|
388
|
+
frame.stack.push(this.applyBinary('>>', a, b));
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case types_1.OpCode.INPLACE_ADD: {
|
|
392
|
+
const b = frame.stack.pop();
|
|
393
|
+
const a = frame.stack.pop();
|
|
394
|
+
frame.stack.push(this.applyInPlaceBinary('+', a, b));
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
case types_1.OpCode.INPLACE_SUBTRACT: {
|
|
398
|
+
const b = frame.stack.pop();
|
|
399
|
+
const a = frame.stack.pop();
|
|
400
|
+
frame.stack.push(this.applyInPlaceBinary('-', a, b));
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case types_1.OpCode.INPLACE_MULTIPLY: {
|
|
404
|
+
const b = frame.stack.pop();
|
|
405
|
+
const a = frame.stack.pop();
|
|
406
|
+
frame.stack.push(this.applyInPlaceBinary('*', a, b));
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case types_1.OpCode.INPLACE_DIVIDE: {
|
|
410
|
+
const b = frame.stack.pop();
|
|
411
|
+
const a = frame.stack.pop();
|
|
412
|
+
frame.stack.push(this.applyInPlaceBinary('/', a, b));
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case types_1.OpCode.INPLACE_FLOOR_DIVIDE: {
|
|
416
|
+
const b = frame.stack.pop();
|
|
417
|
+
const a = frame.stack.pop();
|
|
418
|
+
frame.stack.push(this.applyInPlaceBinary('//', a, b));
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
case types_1.OpCode.INPLACE_MODULO: {
|
|
422
|
+
const b = frame.stack.pop();
|
|
423
|
+
const a = frame.stack.pop();
|
|
424
|
+
frame.stack.push(this.applyInPlaceBinary('%', a, b));
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
case types_1.OpCode.INPLACE_POWER: {
|
|
428
|
+
const b = frame.stack.pop();
|
|
429
|
+
const a = frame.stack.pop();
|
|
430
|
+
frame.stack.push(this.applyInPlaceBinary('**', a, b));
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case types_1.OpCode.INPLACE_AND: {
|
|
434
|
+
const b = frame.stack.pop();
|
|
435
|
+
const a = frame.stack.pop();
|
|
436
|
+
frame.stack.push(this.applyInPlaceBinary('&', a, b));
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case types_1.OpCode.INPLACE_XOR: {
|
|
440
|
+
const b = frame.stack.pop();
|
|
441
|
+
const a = frame.stack.pop();
|
|
442
|
+
frame.stack.push(this.applyInPlaceBinary('^', a, b));
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case types_1.OpCode.INPLACE_OR: {
|
|
446
|
+
const b = frame.stack.pop();
|
|
447
|
+
const a = frame.stack.pop();
|
|
448
|
+
frame.stack.push(this.applyInPlaceBinary('|', a, b));
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case types_1.OpCode.INPLACE_LSHIFT: {
|
|
452
|
+
const b = frame.stack.pop();
|
|
453
|
+
const a = frame.stack.pop();
|
|
454
|
+
frame.stack.push(this.applyInPlaceBinary('<<', a, b));
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case types_1.OpCode.INPLACE_RSHIFT: {
|
|
458
|
+
const b = frame.stack.pop();
|
|
459
|
+
const a = frame.stack.pop();
|
|
460
|
+
frame.stack.push(this.applyInPlaceBinary('>>', a, b));
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case types_1.OpCode.UNARY_POSITIVE: {
|
|
464
|
+
const a = frame.stack.pop();
|
|
465
|
+
frame.stack.push(+a);
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case types_1.OpCode.UNARY_NEGATIVE: {
|
|
469
|
+
const a = frame.stack.pop();
|
|
470
|
+
if (typeof a === 'bigint') {
|
|
471
|
+
frame.stack.push(-a);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
frame.stack.push(-a);
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
case types_1.OpCode.UNARY_NOT: {
|
|
479
|
+
const a = frame.stack.pop();
|
|
480
|
+
frame.stack.push(!this.isTruthy(a, frame.scope));
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case types_1.OpCode.UNARY_INVERT: {
|
|
484
|
+
const a = frame.stack.pop();
|
|
485
|
+
if (typeof a === 'bigint') {
|
|
486
|
+
frame.stack.push(~a);
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
frame.stack.push(~Number(a));
|
|
490
|
+
}
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case types_1.OpCode.COMPARE_OP: {
|
|
494
|
+
const b = frame.stack.pop();
|
|
495
|
+
const a = frame.stack.pop();
|
|
496
|
+
frame.stack.push(this.applyCompare(arg, a, b));
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case types_1.OpCode.BUILD_LIST: {
|
|
500
|
+
const list = [];
|
|
501
|
+
for (let i = 0; i < arg; i++)
|
|
502
|
+
list.unshift(frame.stack.pop());
|
|
503
|
+
frame.stack.push(list);
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case types_1.OpCode.BUILD_TUPLE: {
|
|
507
|
+
const tuple = [];
|
|
508
|
+
for (let i = 0; i < arg; i++)
|
|
509
|
+
tuple.unshift(frame.stack.pop());
|
|
510
|
+
tuple.__tuple__ = true;
|
|
511
|
+
frame.stack.push(tuple);
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
case types_1.OpCode.BUILD_MAP: {
|
|
515
|
+
const dict = new runtime_types_1.PyDict();
|
|
516
|
+
const items = [];
|
|
517
|
+
for (let i = 0; i < arg; i++) {
|
|
518
|
+
const v = frame.stack.pop();
|
|
519
|
+
const k = frame.stack.pop();
|
|
520
|
+
items.push({ k, v });
|
|
521
|
+
}
|
|
522
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
523
|
+
dict.set(items[i].k, items[i].v);
|
|
524
|
+
}
|
|
525
|
+
frame.stack.push(dict);
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case types_1.OpCode.BUILD_SET: {
|
|
529
|
+
const set = new runtime_types_1.PySet();
|
|
530
|
+
for (let i = 0; i < arg; i++) {
|
|
531
|
+
set.add(frame.stack.pop());
|
|
532
|
+
}
|
|
533
|
+
frame.stack.push(set);
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
case types_1.OpCode.BUILD_SLICE: {
|
|
537
|
+
const step = arg === 3 ? frame.stack.pop() : null;
|
|
538
|
+
const end = frame.stack.pop();
|
|
539
|
+
const start = frame.stack.pop();
|
|
540
|
+
// Create a slice object with start/end/step to match parser AST nodes
|
|
541
|
+
frame.stack.push({ __slice__: true, start, end, step });
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
case types_1.OpCode.JUMP_ABSOLUTE:
|
|
545
|
+
frame.pc = arg;
|
|
546
|
+
break;
|
|
547
|
+
case types_1.OpCode.POP_JUMP_IF_FALSE: {
|
|
548
|
+
const val = frame.stack.pop();
|
|
549
|
+
if (!this.isTruthy(val, frame.scope)) {
|
|
550
|
+
frame.pc = arg;
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case types_1.OpCode.POP_JUMP_IF_TRUE: {
|
|
555
|
+
const val = frame.stack.pop();
|
|
556
|
+
if (this.isTruthy(val, frame.scope)) {
|
|
557
|
+
frame.pc = arg;
|
|
558
|
+
}
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
case types_1.OpCode.JUMP_IF_FALSE_OR_POP: {
|
|
562
|
+
const val = frame.stack[frame.stack.length - 1];
|
|
563
|
+
if (!this.isTruthy(val, frame.scope)) {
|
|
564
|
+
frame.pc = arg;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
frame.stack.pop();
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
case types_1.OpCode.JUMP_IF_TRUE_OR_POP: {
|
|
572
|
+
const val = frame.stack[frame.stack.length - 1];
|
|
573
|
+
if (this.isTruthy(val, frame.scope)) {
|
|
574
|
+
frame.pc = arg;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
frame.stack.pop();
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case types_1.OpCode.GET_ITER: {
|
|
582
|
+
const obj = frame.stack.pop();
|
|
583
|
+
if (obj && typeof obj[Symbol.iterator] === 'function') {
|
|
584
|
+
frame.stack.push(obj[Symbol.iterator]());
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
throw new runtime_types_1.PyException('TypeError', `'${typeof obj}' object is not iterable`);
|
|
588
|
+
}
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case types_1.OpCode.FOR_ITER: {
|
|
592
|
+
const iter = frame.stack[frame.stack.length - 1];
|
|
593
|
+
const next = iter.next();
|
|
594
|
+
if (next.done) {
|
|
595
|
+
frame.stack.pop();
|
|
596
|
+
frame.pc = arg;
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
frame.stack.push(next.value);
|
|
600
|
+
}
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
case types_1.OpCode.MAKE_FUNCTION: {
|
|
604
|
+
const defaultsCount = arg || 0;
|
|
605
|
+
const name = frame.stack.pop();
|
|
606
|
+
const bc = frame.stack.pop();
|
|
607
|
+
const defaults = [];
|
|
608
|
+
// Pop default values from stack in reverse order (last default on top)
|
|
609
|
+
for (let i = 0; i < defaultsCount; i++) {
|
|
610
|
+
defaults.unshift(frame.stack.pop());
|
|
611
|
+
}
|
|
612
|
+
// Create a copy of params with evaluated defaults
|
|
613
|
+
const params = (bc && bc.params) ? bc.params.map((p) => ({ ...p })) : [];
|
|
614
|
+
if (params.length > 0 && defaultsCount > 0) {
|
|
615
|
+
let defaultIndex = 0;
|
|
616
|
+
for (let i = params.length - defaultsCount; i < params.length; i++) {
|
|
617
|
+
if (params[i] && params[i].type === 'Param') {
|
|
618
|
+
params[i].defaultEvaluated = defaults[defaultIndex++];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const isGenerator = !!(bc && bc.isGenerator);
|
|
623
|
+
const body = (bc && bc.astBody) ? bc.astBody : [];
|
|
624
|
+
const func = new runtime_types_1.PyFunction(name, params, body, frame.scope, isGenerator, new Set(), bc);
|
|
625
|
+
func.closure_shared_values = frame.scope.values;
|
|
626
|
+
frame.stack.push(func);
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
case types_1.OpCode.LOAD_BUILD_CLASS: {
|
|
630
|
+
const enclosingScope = frame.scope;
|
|
631
|
+
frame.stack.push((bodyFn, name, ...bases) => {
|
|
632
|
+
const classScope = new runtime_types_1.Scope(enclosingScope, true);
|
|
633
|
+
if (bodyFn instanceof runtime_types_1.PyFunction && bodyFn.bytecode) {
|
|
634
|
+
const bodyFrame = new runtime_types_1.Frame(bodyFn.bytecode, classScope);
|
|
635
|
+
// Class bodies take no args; locals stay empty.
|
|
636
|
+
this.executeFrame(bodyFrame);
|
|
637
|
+
}
|
|
638
|
+
else if (typeof bodyFn === 'function') {
|
|
639
|
+
bodyFn();
|
|
640
|
+
}
|
|
641
|
+
const attributes = new Map(classScope.values.entries());
|
|
642
|
+
const isException = bases.some((b) => b instanceof runtime_types_1.PyClass && b.isException);
|
|
643
|
+
return new runtime_types_1.PyClass(String(name), bases, attributes, isException);
|
|
644
|
+
});
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
case types_1.OpCode.IMPORT_NAME: {
|
|
648
|
+
frame.stack.pop(); // level
|
|
649
|
+
frame.stack.pop(); // fromlist
|
|
650
|
+
const name = names[arg];
|
|
651
|
+
frame.stack.push(this.importModule(name, frame.scope));
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case types_1.OpCode.RAISE_VARARGS: {
|
|
655
|
+
if (arg >= 2)
|
|
656
|
+
frame.stack.pop(); // cause
|
|
657
|
+
const exc = arg >= 1 ? frame.stack.pop() : null;
|
|
658
|
+
// simplified raise
|
|
659
|
+
throw exc || new runtime_types_1.PyException('RuntimeError', 'No exception to raise');
|
|
660
|
+
}
|
|
661
|
+
case types_1.OpCode.SETUP_FINALLY: {
|
|
662
|
+
frame.blockStack.push({ handler: arg, stackHeight: frame.stack.length });
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
case types_1.OpCode.SETUP_WITH: {
|
|
666
|
+
const ctx = frame.stack.pop();
|
|
667
|
+
const enter = this.getAttribute(ctx, '__enter__', frame.scope);
|
|
668
|
+
const exit = this.getAttribute(ctx, '__exit__', frame.scope);
|
|
669
|
+
const result = this.callFunction(enter, [], frame.scope);
|
|
670
|
+
frame.stack.push(exit);
|
|
671
|
+
frame.stack.push(result);
|
|
672
|
+
// Block should assume exit is on stack, so stackHeight includes exit.
|
|
673
|
+
// When popping block, we leave exit on stack?
|
|
674
|
+
// No, standard behavior: SETUP_WITH pushes exit, enter_res.
|
|
675
|
+
// Handler expects [exit, exc...]?
|
|
676
|
+
// Let's rely on blockStack logic: stack returned to stackHeight on exception.
|
|
677
|
+
// If we set stackHeight to include exit, then on exception, we have [exit], then push exc.
|
|
678
|
+
frame.blockStack.push({ handler: arg, stackHeight: frame.stack.length - 1 });
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
case types_1.OpCode.WITH_EXCEPT_START: {
|
|
682
|
+
// Stack: [exit, exc_norm] (after normalizeThrown in dispatchException)
|
|
683
|
+
// Or [exit, exc]
|
|
684
|
+
// This opcode calls exit(type, val, tb)
|
|
685
|
+
const exc = frame.stack.pop();
|
|
686
|
+
const exit = frame.stack.pop();
|
|
687
|
+
// Call exit(type, val, tb)
|
|
688
|
+
// For our VM, we can pass (exc.type, exc, None)
|
|
689
|
+
const pyType = (exc instanceof runtime_types_1.PyInstance) ? exc.klass : (exc.pyType || exc);
|
|
690
|
+
const handled = this.callFunction(exit, [pyType, exc, null], frame.scope);
|
|
691
|
+
if (this.isTruthy(handled, frame.scope)) {
|
|
692
|
+
// Exception suppressed
|
|
693
|
+
// Pop exception from stack (we already popped it)
|
|
694
|
+
// Push nothing? or push suppression marker?
|
|
695
|
+
// CPython pushes nothing on suppression?
|
|
696
|
+
// "If __exit__ returns True, the exception is suppressed, and execution proceeds"
|
|
697
|
+
// We usually JUMP after this.
|
|
698
|
+
// But if we just consumed the exception, we need to ensure the stack is clean for the next block.
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
// Exception not suppressed, re-raise
|
|
702
|
+
throw exc;
|
|
703
|
+
}
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
case types_1.OpCode.POP_BLOCK: {
|
|
707
|
+
frame.blockStack.pop();
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
case types_1.OpCode.CALL_FUNCTION: {
|
|
711
|
+
const args = [];
|
|
712
|
+
for (let i = 0; i < arg; i++) {
|
|
713
|
+
args.unshift(frame.stack.pop());
|
|
714
|
+
}
|
|
715
|
+
const func = frame.stack.pop();
|
|
716
|
+
frame.stack.push(this.callFunction(func, args, frame.scope));
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
case types_1.OpCode.CALL_FUNCTION_KW: {
|
|
720
|
+
const kwNames = frame.stack.pop();
|
|
721
|
+
const kwList = Array.isArray(kwNames) ? kwNames : [];
|
|
722
|
+
const kwCount = kwList.length;
|
|
723
|
+
const total = arg;
|
|
724
|
+
const values = [];
|
|
725
|
+
for (let i = 0; i < total; i++) {
|
|
726
|
+
values.unshift(frame.stack.pop());
|
|
727
|
+
}
|
|
728
|
+
const func = frame.stack.pop();
|
|
729
|
+
const positionalCount = total - kwCount;
|
|
730
|
+
const positional = values.slice(0, positionalCount);
|
|
731
|
+
const kwargs = {};
|
|
732
|
+
for (let i = 0; i < kwCount; i++) {
|
|
733
|
+
kwargs[String(kwList[i])] = values[positionalCount + i];
|
|
734
|
+
}
|
|
735
|
+
frame.stack.push(this.callFunction(func, positional, frame.scope, kwargs));
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
case types_1.OpCode.CALL_FUNCTION_EX: {
|
|
739
|
+
// arg == 1 means there's a kwargs dict on top
|
|
740
|
+
// Stack: [func, args_tuple] or [func, args_tuple, kwargs_dict]
|
|
741
|
+
const kwargs = {};
|
|
742
|
+
if (arg === 1) {
|
|
743
|
+
const kwDict = frame.stack.pop();
|
|
744
|
+
if (kwDict instanceof runtime_types_1.PyDict) {
|
|
745
|
+
for (const [k, v] of kwDict.entries()) {
|
|
746
|
+
kwargs[String(k)] = v;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
else if (kwDict && typeof kwDict === 'object') {
|
|
750
|
+
for (const [k, v] of Object.entries(kwDict)) {
|
|
751
|
+
kwargs[k] = v;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const argsTuple = frame.stack.pop();
|
|
756
|
+
const func = frame.stack.pop();
|
|
757
|
+
const args = Array.isArray(argsTuple) ? argsTuple : (argsTuple ? Array.from(argsTuple) : []);
|
|
758
|
+
frame.stack.push(this.callFunction(func, args, frame.scope, kwargs));
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
case types_1.OpCode.EVAL_AST: {
|
|
762
|
+
const node = frame.stack.pop();
|
|
763
|
+
frame.stack.push(this.evaluateExpression(node, frame.scope));
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
case types_1.OpCode.RETURN_VALUE:
|
|
767
|
+
return frame.stack.pop();
|
|
768
|
+
default:
|
|
769
|
+
throw new Error(`VM: Unknown opcode ${types_1.OpCode[opcode]} (${opcode}) at pc ${frame.pc - 1}`);
|
|
262
770
|
}
|
|
263
|
-
return null;
|
|
264
771
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
: null;
|
|
271
|
-
throw new runtime_types_1.ReturnSignal(value);
|
|
772
|
+
catch (err) {
|
|
773
|
+
if (dispatchException(err)) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
throw err;
|
|
272
777
|
}
|
|
273
|
-
case types_1.ASTNodeType.BREAK_STATEMENT:
|
|
274
|
-
throw new runtime_types_1.BreakSignal();
|
|
275
|
-
case types_1.ASTNodeType.CONTINUE_STATEMENT:
|
|
276
|
-
throw new runtime_types_1.ContinueSignal();
|
|
277
|
-
case types_1.ASTNodeType.PASS_STATEMENT:
|
|
278
|
-
return null;
|
|
279
|
-
default:
|
|
280
|
-
return this.executeStatement(node, scope);
|
|
281
778
|
}
|
|
779
|
+
return lastValue;
|
|
282
780
|
}
|
|
781
|
+
var execution_helpers_1 = require("./execution-helpers");
|
|
782
|
+
Object.defineProperty(exports, "applyCompare", { enumerable: true, get: function () { return execution_helpers_1.applyCompare; } });
|
|
783
|
+
Object.defineProperty(exports, "iterableToArray", { enumerable: true, get: function () { return execution_helpers_1.iterableToArray; } });
|
|
784
|
+
Object.defineProperty(exports, "matchValueEquals", { enumerable: true, get: function () { return execution_helpers_1.matchValueEquals; } });
|
|
785
|
+
Object.defineProperty(exports, "applyBindings", { enumerable: true, get: function () { return execution_helpers_1.applyBindings; } });
|
|
786
|
+
Object.defineProperty(exports, "matchPattern", { enumerable: true, get: function () { return execution_helpers_1.matchPattern; } });
|
|
787
|
+
Object.defineProperty(exports, "evaluateExpression", { enumerable: true, get: function () { return execution_helpers_1.evaluateExpression; } });
|
|
788
|
+
Object.defineProperty(exports, "executeStatement", { enumerable: true, get: function () { return execution_helpers_1.executeStatement; } });
|
|
789
|
+
Object.defineProperty(exports, "executeBlockGenerator", { enumerable: true, get: function () { return execution_helpers_1.executeBlockGenerator; } });
|
|
790
|
+
Object.defineProperty(exports, "executeStatementGenerator", { enumerable: true, get: function () { return execution_helpers_1.executeStatementGenerator; } });
|
|
791
|
+
Object.defineProperty(exports, "executeBlock", { enumerable: true, get: function () { return execution_helpers_1.executeBlock; } });
|
|
283
792
|
//# sourceMappingURL=execution.js.map
|