@lewin671/python-vm 0.1.0 → 0.1.2
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 +1 -2
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js.map +1 -0
- package/dist/compiler_module/compiler.d.ts.map +1 -0
- package/dist/compiler_module/compiler.js.map +1 -0
- package/dist/compiler_module/index.d.ts.map +1 -0
- package/dist/compiler_module/index.js.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer/index.d.ts.map +1 -0
- package/dist/lexer/index.js.map +1 -0
- package/dist/lexer/lexer.d.ts.map +1 -0
- package/dist/lexer/lexer.js.map +1 -0
- package/dist/parser/expressions.d.ts.map +1 -0
- package/dist/parser/expressions.js.map +1 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/statements.d.ts.map +1 -0
- package/dist/parser/statements.js.map +1 -0
- package/dist/parser/targets.d.ts.map +1 -0
- package/dist/parser/targets.js.map +1 -0
- package/dist/types/ast.d.ts.map +1 -0
- package/dist/types/ast.js.map +1 -0
- package/dist/types/bytecode.d.ts.map +1 -0
- package/dist/types/bytecode.js.map +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/token.d.ts.map +1 -0
- package/dist/types/token.js.map +1 -0
- package/dist/vm/builtins.d.ts.map +1 -0
- package/dist/vm/builtins.js.map +1 -0
- package/dist/vm/callable.d.ts.map +1 -0
- package/dist/vm/callable.js.map +1 -0
- package/dist/vm/execution.d.ts.map +1 -0
- package/dist/vm/execution.js.map +1 -0
- package/dist/vm/expression-generator.d.ts.map +1 -0
- package/dist/vm/expression-generator.js.map +1 -0
- package/dist/vm/expressions.d.ts.map +1 -0
- package/dist/vm/expressions.js.map +1 -0
- package/dist/vm/imports.d.ts.map +1 -0
- package/dist/vm/imports.js.map +1 -0
- package/dist/vm/index.d.ts.map +1 -0
- package/dist/vm/index.js.map +1 -0
- package/dist/vm/operations.d.ts.map +1 -0
- package/dist/vm/operations.js.map +1 -0
- package/dist/vm/runtime-types.d.ts.map +1 -0
- package/dist/vm/runtime-types.js.map +1 -0
- package/dist/vm/statements.d.ts.map +1 -0
- package/dist/vm/statements.js.map +1 -0
- package/dist/vm/truthy.d.ts.map +1 -0
- package/dist/vm/truthy.js.map +1 -0
- package/dist/vm/value-utils.d.ts.map +1 -0
- package/dist/vm/value-utils.js.map +1 -0
- package/dist/vm/vm.d.ts.map +1 -0
- package/dist/vm/vm.js.map +1 -0
- package/package.json +7 -1
- package/.claude/settings.local.json +0 -3
- package/.prettierrc +0 -7
- package/Agents.md +0 -66
- package/SETUP.md +0 -171
- package/examples/assert_testing.py +0 -38
- package/examples/big_int_precision.py +0 -2
- package/examples/boolean_logic.py +0 -35
- package/examples/break_continue.py +0 -43
- package/examples/classes_objects.py +0 -43
- package/examples/compiler_killer_async.py +0 -6
- package/examples/compiler_killer_bigint.py +0 -3
- package/examples/compiler_killer_bool_int_dict_key.py +0 -5
- package/examples/compiler_killer_bool_len.py +0 -9
- package/examples/compiler_killer_floor_division.py +0 -4
- package/examples/compiler_killer_is_identity.py +0 -3
- package/examples/compiler_killer_list_sort_return.py +0 -3
- package/examples/compiler_killer_match.py +0 -13
- package/examples/compiler_killer_negative_repeat.py +0 -3
- package/examples/compiler_killer_negative_zero_repr.py +0 -3
- package/examples/compiler_killer_rounding.py +0 -4
- package/examples/compiler_killer_slice_assign.py +0 -3
- package/examples/comprehensions.py +0 -28
- package/examples/conditions.py +0 -13
- package/examples/context_manager.py +0 -35
- package/examples/decorators.py +0 -50
- package/examples/exceptions.py +0 -40
- package/examples/fibonacci.py +0 -10
- package/examples/functions.py +0 -38
- package/examples/generator.py +0 -51
- package/examples/global_nonlocal.py +0 -48
- package/examples/hello.py +0 -3
- package/examples/itertools_example.py +0 -33
- package/examples/lists_dicts.py +0 -29
- package/examples/loops.py +0 -19
- package/examples/math_ops.py +0 -15
- package/examples/nan_set.py +0 -6
- package/examples/numbers_operators.py +0 -51
- package/examples/sets.py +0 -36
- package/examples/slicing.py +0 -29
- package/examples/starred_unpacking.py +0 -3
- package/examples/string_formatting.py +0 -36
- package/examples/strings.py +0 -22
- package/examples/tuples.py +0 -45
- package/examples/type_conversion.py +0 -41
- package/jest.config.js +0 -15
- package/notes/iterations/compiler-runtime/compiler-runtime_2025-09-16.md +0 -25
- package/notes/iterations/compiler-runtime/compiler-runtime_2026-01-16.md +0 -24
- package/notes/iterations/compiler-runtime/compiler-runtime_test_2026-01-16.md +0 -21
- package/notes/iterations/floor-division/floor-division_2026-01-16.md +0 -29
- package/prompts/commit.txt +0 -9
- package/prompts/task.txt +0 -21
- package/prompts/test.txt +0 -23
- package/scripts/codex-loop.js +0 -215
- package/scripts/verify.sh +0 -12
- package/src/compiler.ts +0 -58
- package/src/compiler_module/compiler.ts +0 -19
- package/src/compiler_module/index.ts +0 -1
- package/src/index.ts +0 -39
- package/src/lexer/index.ts +0 -1
- package/src/lexer/lexer.ts +0 -402
- package/src/parser/expressions.ts +0 -462
- package/src/parser/index.ts +0 -1
- package/src/parser/parser.ts +0 -102
- package/src/parser/statements.ts +0 -366
- package/src/parser/targets.ts +0 -71
- package/src/types/ast.ts +0 -64
- package/src/types/bytecode.ts +0 -50
- package/src/types/index.ts +0 -3
- package/src/types/token.ts +0 -44
- package/src/vm/builtins.ts +0 -237
- package/src/vm/callable.ts +0 -154
- package/src/vm/execution.ts +0 -251
- package/src/vm/expression-generator.ts +0 -65
- package/src/vm/expressions.ts +0 -373
- package/src/vm/imports.ts +0 -61
- package/src/vm/index.ts +0 -2
- package/src/vm/operations.ts +0 -414
- package/src/vm/runtime-types.ts +0 -292
- package/src/vm/statements.ts +0 -358
- package/src/vm/truthy.ts +0 -36
- package/src/vm/value-utils.ts +0 -173
- package/src/vm/vm.ts +0 -80
- package/tests/compiler.test.ts +0 -111
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -16
package/src/vm/operations.ts
DELETED
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
import type { VirtualMachine } from './vm';
|
|
2
|
-
import { ASTNodeType } from '../types';
|
|
3
|
-
import { PyClass, PyDict, PyException, PyFile, PyFunction, PyGenerator, PyInstance, Scope } from './runtime-types';
|
|
4
|
-
import {
|
|
5
|
-
bigIntFloorDiv,
|
|
6
|
-
isComplex,
|
|
7
|
-
isFloatLike,
|
|
8
|
-
isIntLike,
|
|
9
|
-
isNumericLike,
|
|
10
|
-
numericEquals,
|
|
11
|
-
pythonModulo,
|
|
12
|
-
shouldUseBigInt,
|
|
13
|
-
toBigIntValue,
|
|
14
|
-
toComplex,
|
|
15
|
-
toNumber,
|
|
16
|
-
pyStr,
|
|
17
|
-
pyTypeName,
|
|
18
|
-
} from './value-utils';
|
|
19
|
-
|
|
20
|
-
export function applyBinary(this: VirtualMachine, op: string, left: any, right: any): any {
|
|
21
|
-
if (isComplex(left) || isComplex(right)) {
|
|
22
|
-
const a = toComplex(left);
|
|
23
|
-
const b = toComplex(right);
|
|
24
|
-
switch (op) {
|
|
25
|
-
case '+':
|
|
26
|
-
return { __complex__: true, re: a.re + b.re, im: a.im + b.im };
|
|
27
|
-
case '-':
|
|
28
|
-
return { __complex__: true, re: a.re - b.re, im: a.im - b.im };
|
|
29
|
-
case '*':
|
|
30
|
-
return {
|
|
31
|
-
__complex__: true,
|
|
32
|
-
re: a.re * b.re - a.im * b.im,
|
|
33
|
-
im: a.re * b.im + a.im * b.re,
|
|
34
|
-
};
|
|
35
|
-
default:
|
|
36
|
-
throw new PyException('TypeError', `unsupported complex operator ${op}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
switch (op) {
|
|
40
|
-
case '+':
|
|
41
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
42
|
-
const result = [...left, ...right];
|
|
43
|
-
if ((left as any).__tuple__ && (right as any).__tuple__) {
|
|
44
|
-
(result as any).__tuple__ = true;
|
|
45
|
-
}
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
if (isFloatLike(left) || isFloatLike(right)) {
|
|
49
|
-
return new Number(toNumber(left) + toNumber(right));
|
|
50
|
-
}
|
|
51
|
-
if (shouldUseBigInt(left, right)) {
|
|
52
|
-
return toBigIntValue(left) + toBigIntValue(right);
|
|
53
|
-
}
|
|
54
|
-
return left + right;
|
|
55
|
-
case '-':
|
|
56
|
-
if (left instanceof Set && right instanceof Set) {
|
|
57
|
-
const result = new Set(left);
|
|
58
|
-
for (const item of right.values()) result.delete(item);
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
if (isFloatLike(left) || isFloatLike(right)) {
|
|
62
|
-
return new Number(toNumber(left) - toNumber(right));
|
|
63
|
-
}
|
|
64
|
-
if (shouldUseBigInt(left, right)) {
|
|
65
|
-
return toBigIntValue(left) - toBigIntValue(right);
|
|
66
|
-
}
|
|
67
|
-
return left - right;
|
|
68
|
-
case '*':
|
|
69
|
-
if (typeof left === 'string' && isIntLike(right)) {
|
|
70
|
-
const count = toNumber(right);
|
|
71
|
-
if (count <= 0) return '';
|
|
72
|
-
return left.repeat(count);
|
|
73
|
-
}
|
|
74
|
-
if (typeof right === 'string' && isIntLike(left)) {
|
|
75
|
-
const count = toNumber(left);
|
|
76
|
-
if (count <= 0) return '';
|
|
77
|
-
return right.repeat(count);
|
|
78
|
-
}
|
|
79
|
-
if (Array.isArray(left) && isIntLike(right)) {
|
|
80
|
-
const count = toNumber(right);
|
|
81
|
-
if (count <= 0) {
|
|
82
|
-
const result: any[] = [];
|
|
83
|
-
if ((left as any).__tuple__) {
|
|
84
|
-
(result as any).__tuple__ = true;
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
const result = Array(count).fill(null).flatMap(() => left);
|
|
89
|
-
if ((left as any).__tuple__) {
|
|
90
|
-
(result as any).__tuple__ = true;
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
if (isFloatLike(left) || isFloatLike(right)) {
|
|
95
|
-
return new Number(toNumber(left) * toNumber(right));
|
|
96
|
-
}
|
|
97
|
-
if (shouldUseBigInt(left, right)) {
|
|
98
|
-
return toBigIntValue(left) * toBigIntValue(right);
|
|
99
|
-
}
|
|
100
|
-
return left * right;
|
|
101
|
-
case '/':
|
|
102
|
-
if (right === 0 || right === 0n) throw new PyException('ZeroDivisionError', 'division by zero');
|
|
103
|
-
return new Number(toNumber(left) / toNumber(right));
|
|
104
|
-
case '//':
|
|
105
|
-
if (right === 0 || right === 0n) throw new PyException('ZeroDivisionError', 'division by zero');
|
|
106
|
-
if (isFloatLike(left) || isFloatLike(right)) {
|
|
107
|
-
return new Number(Math.floor(toNumber(left) / toNumber(right)));
|
|
108
|
-
}
|
|
109
|
-
if (shouldUseBigInt(left, right)) {
|
|
110
|
-
return bigIntFloorDiv(toBigIntValue(left), toBigIntValue(right));
|
|
111
|
-
}
|
|
112
|
-
return Math.floor(left / right);
|
|
113
|
-
case '%':
|
|
114
|
-
if (typeof left === 'string') {
|
|
115
|
-
return this.formatPercent(left, right);
|
|
116
|
-
}
|
|
117
|
-
return pythonModulo(left, right);
|
|
118
|
-
case '**':
|
|
119
|
-
if (isIntLike(left) && isIntLike(right) && !isFloatLike(left) && !isFloatLike(right)) {
|
|
120
|
-
const exponentNum = toNumber(right);
|
|
121
|
-
if (Number.isInteger(exponentNum) && exponentNum >= 0) {
|
|
122
|
-
const approx = Math.pow(toNumber(left), exponentNum);
|
|
123
|
-
if (shouldUseBigInt(left, right) || !Number.isSafeInteger(approx)) {
|
|
124
|
-
return toBigIntValue(left) ** toBigIntValue(right);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (shouldUseBigInt(left, right)) {
|
|
129
|
-
const exponent = toBigIntValue(right);
|
|
130
|
-
if (exponent < 0n) {
|
|
131
|
-
return new Number(Math.pow(toNumber(left), toNumber(right)));
|
|
132
|
-
}
|
|
133
|
-
return toBigIntValue(left) ** exponent;
|
|
134
|
-
}
|
|
135
|
-
return Math.pow(toNumber(left), toNumber(right));
|
|
136
|
-
case '&':
|
|
137
|
-
if (left instanceof Set && right instanceof Set) {
|
|
138
|
-
const result = new Set<any>();
|
|
139
|
-
for (const item of left.values()) {
|
|
140
|
-
if (right.has(item)) result.add(item);
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
if (shouldUseBigInt(left, right)) {
|
|
145
|
-
return toBigIntValue(left) & toBigIntValue(right);
|
|
146
|
-
}
|
|
147
|
-
return left & right;
|
|
148
|
-
case '|':
|
|
149
|
-
if (left instanceof Set && right instanceof Set) {
|
|
150
|
-
const result = new Set<any>(left);
|
|
151
|
-
for (const item of right.values()) result.add(item);
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
if (shouldUseBigInt(left, right)) {
|
|
155
|
-
return toBigIntValue(left) | toBigIntValue(right);
|
|
156
|
-
}
|
|
157
|
-
return left | right;
|
|
158
|
-
case '^':
|
|
159
|
-
if (left instanceof Set && right instanceof Set) {
|
|
160
|
-
const result = new Set<any>();
|
|
161
|
-
for (const item of left.values()) {
|
|
162
|
-
if (!right.has(item)) result.add(item);
|
|
163
|
-
}
|
|
164
|
-
for (const item of right.values()) {
|
|
165
|
-
if (!left.has(item)) result.add(item);
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
if (shouldUseBigInt(left, right)) {
|
|
170
|
-
return toBigIntValue(left) ^ toBigIntValue(right);
|
|
171
|
-
}
|
|
172
|
-
return left ^ right;
|
|
173
|
-
case '<<':
|
|
174
|
-
if (shouldUseBigInt(left, right)) {
|
|
175
|
-
return toBigIntValue(left) << toBigIntValue(right);
|
|
176
|
-
}
|
|
177
|
-
return left << right;
|
|
178
|
-
case '>>':
|
|
179
|
-
if (shouldUseBigInt(left, right)) {
|
|
180
|
-
return toBigIntValue(left) >> toBigIntValue(right);
|
|
181
|
-
}
|
|
182
|
-
return left >> right;
|
|
183
|
-
default:
|
|
184
|
-
throw new PyException('TypeError', `unsupported operator ${op}`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function formatPercent(this: VirtualMachine, format: string, value: any): string {
|
|
189
|
-
const values = Array.isArray(value) ? value : [value];
|
|
190
|
-
let index = 0;
|
|
191
|
-
return format.replace(/%[sdfo]/g, (match) => {
|
|
192
|
-
const val = values[index++];
|
|
193
|
-
if (match === '%d') return typeof val === 'bigint' ? val.toString() : String(parseInt(val, 10));
|
|
194
|
-
if (match === '%f') return String(parseFloat(val));
|
|
195
|
-
return String(val);
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export function getSubscript(this: VirtualMachine, obj: any, index: any): any {
|
|
200
|
-
if (index && index.type === ASTNodeType.SLICE) {
|
|
201
|
-
const start = index.start !== null && index.start !== undefined ? index.start : null;
|
|
202
|
-
const end = index.end !== null && index.end !== undefined ? index.end : null;
|
|
203
|
-
const step = index.step !== null && index.step !== undefined ? index.step : 1;
|
|
204
|
-
const indices = this.computeSliceIndices(obj.length, start, end, step);
|
|
205
|
-
const result: any[] = [];
|
|
206
|
-
for (const idx of indices) result.push(obj[idx]);
|
|
207
|
-
if (typeof obj === 'string') return result.join('');
|
|
208
|
-
if (Array.isArray(obj) && (obj as any).__tuple__) {
|
|
209
|
-
(result as any).__tuple__ = true;
|
|
210
|
-
}
|
|
211
|
-
return result;
|
|
212
|
-
}
|
|
213
|
-
if (Array.isArray(obj) || typeof obj === 'string') {
|
|
214
|
-
let idx = index;
|
|
215
|
-
if (isIntLike(idx) && toNumber(idx) < 0) {
|
|
216
|
-
idx = obj.length + toNumber(idx);
|
|
217
|
-
}
|
|
218
|
-
if (isIntLike(idx)) {
|
|
219
|
-
idx = toNumber(idx);
|
|
220
|
-
}
|
|
221
|
-
return obj[idx];
|
|
222
|
-
}
|
|
223
|
-
if (obj instanceof PyDict) {
|
|
224
|
-
return obj.get(index);
|
|
225
|
-
}
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function computeSliceBounds(this: VirtualMachine, length: number, start: any, end: any, step: any) {
|
|
230
|
-
const stepValue = this.normalizeSliceStep(step);
|
|
231
|
-
const startProvided = start !== null && start !== undefined;
|
|
232
|
-
const endProvided = end !== null && end !== undefined;
|
|
233
|
-
let startValue = startProvided ? toNumber(start) : null;
|
|
234
|
-
let endValue = endProvided ? toNumber(end) : null;
|
|
235
|
-
if (startValue === null) startValue = stepValue < 0 ? length - 1 : 0;
|
|
236
|
-
if (endValue === null) endValue = stepValue < 0 ? -1 : length;
|
|
237
|
-
if (startProvided && startValue < 0) startValue = length + startValue;
|
|
238
|
-
if (endProvided && endValue < 0) endValue = length + endValue;
|
|
239
|
-
return { start: startValue, end: endValue, step: stepValue };
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function computeSliceIndices(this: VirtualMachine, length: number, start: any, end: any, step: any) {
|
|
243
|
-
const bounds = this.computeSliceBounds(length, start, end, step);
|
|
244
|
-
const indices: number[] = [];
|
|
245
|
-
for (let i = bounds.start; bounds.step > 0 ? i < bounds.end : i > bounds.end; i += bounds.step) {
|
|
246
|
-
indices.push(i);
|
|
247
|
-
}
|
|
248
|
-
return indices;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export function normalizeSliceStep(step: any) {
|
|
252
|
-
const stepValue = step !== null && step !== undefined ? toNumber(step) : 1;
|
|
253
|
-
if (stepValue === 0) {
|
|
254
|
-
throw new PyException('ValueError', 'slice step cannot be zero');
|
|
255
|
-
}
|
|
256
|
-
return stepValue;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export function getAttribute(this: VirtualMachine, obj: any, name: string, scope: Scope): any {
|
|
260
|
-
if (obj && obj.__moduleScope__) {
|
|
261
|
-
if (obj.__moduleScope__.values.has(name)) {
|
|
262
|
-
return obj.__moduleScope__.values.get(name);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (obj instanceof PyInstance) {
|
|
266
|
-
if (obj.attributes.has(name)) return obj.attributes.get(name);
|
|
267
|
-
const attr = this.findClassAttribute(obj.klass, name);
|
|
268
|
-
if (attr instanceof PyFunction) {
|
|
269
|
-
return (...args: any[]) => this.callFunction(attr, [obj, ...args], scope);
|
|
270
|
-
}
|
|
271
|
-
return attr;
|
|
272
|
-
}
|
|
273
|
-
if (obj instanceof PyClass) {
|
|
274
|
-
const attr = this.findClassAttribute(obj, name);
|
|
275
|
-
return attr;
|
|
276
|
-
}
|
|
277
|
-
if (obj instanceof PyFile) {
|
|
278
|
-
const value = (obj as any)[name];
|
|
279
|
-
if (typeof value === 'function') return value.bind(obj);
|
|
280
|
-
return value;
|
|
281
|
-
}
|
|
282
|
-
if (obj instanceof PyGenerator) {
|
|
283
|
-
const value = (obj as any)[name];
|
|
284
|
-
if (typeof value === 'function') return value.bind(obj);
|
|
285
|
-
return value;
|
|
286
|
-
}
|
|
287
|
-
if (isComplex(obj)) {
|
|
288
|
-
if (name === 'real') return new Number(obj.re);
|
|
289
|
-
if (name === 'imag') return new Number(obj.im);
|
|
290
|
-
}
|
|
291
|
-
if (typeof obj === 'string') {
|
|
292
|
-
if (name === 'upper') return () => obj.toUpperCase();
|
|
293
|
-
if (name === 'replace')
|
|
294
|
-
return (a: any, b: any) => {
|
|
295
|
-
return obj.replace(a, b);
|
|
296
|
-
};
|
|
297
|
-
if (name === 'format')
|
|
298
|
-
return (...args: any[]) => {
|
|
299
|
-
let kwargs: Record<string, any> = {};
|
|
300
|
-
if (args.length > 0) {
|
|
301
|
-
const last = args[args.length - 1];
|
|
302
|
-
if (last && last.__kwargs__) {
|
|
303
|
-
kwargs = last.__kwargs__;
|
|
304
|
-
args = args.slice(0, -1);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
let autoIndex = 0;
|
|
308
|
-
return obj.replace(/\{([^{}]*)\}/g, (_match, key) => {
|
|
309
|
-
if (key === '') {
|
|
310
|
-
const value = args[autoIndex++];
|
|
311
|
-
return pyStr(value);
|
|
312
|
-
}
|
|
313
|
-
if (/^\d+$/.test(key)) {
|
|
314
|
-
const value = args[parseInt(key, 10)];
|
|
315
|
-
return pyStr(value);
|
|
316
|
-
}
|
|
317
|
-
if (key in kwargs) {
|
|
318
|
-
return pyStr(kwargs[key]);
|
|
319
|
-
}
|
|
320
|
-
return '';
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
if (name === 'count') return (ch: any) => obj.split(ch).length - 1;
|
|
324
|
-
}
|
|
325
|
-
if (Array.isArray(obj)) {
|
|
326
|
-
if (name === 'append') return (value: any) => obj.push(value);
|
|
327
|
-
if (name === 'count') return (value: any) => obj.filter((item: any) => item === value).length;
|
|
328
|
-
if (name === 'index') return (value: any) => obj.indexOf(value);
|
|
329
|
-
if (name === 'sort') {
|
|
330
|
-
return (...args: any[]) => {
|
|
331
|
-
let kwargs: Record<string, any> = {};
|
|
332
|
-
if (args.length > 0) {
|
|
333
|
-
const last = args[args.length - 1];
|
|
334
|
-
if (last && last.__kwargs__) {
|
|
335
|
-
kwargs = last.__kwargs__;
|
|
336
|
-
args = args.slice(0, -1);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
let keyFn = args.length > 0 ? args[0] : null;
|
|
340
|
-
if ('key' in kwargs) keyFn = kwargs.key;
|
|
341
|
-
const reverse = 'reverse' in kwargs ? Boolean(kwargs.reverse) : false;
|
|
342
|
-
if (keyFn) {
|
|
343
|
-
const keyed = obj.map((item: any) => ({
|
|
344
|
-
item,
|
|
345
|
-
key: this.callFunction(keyFn, [item], scope),
|
|
346
|
-
}));
|
|
347
|
-
keyed.sort((a, b) => {
|
|
348
|
-
if (isNumericLike(a.key) && isNumericLike(b.key)) {
|
|
349
|
-
return toNumber(a.key) - toNumber(b.key);
|
|
350
|
-
}
|
|
351
|
-
return String(a.key).localeCompare(String(b.key));
|
|
352
|
-
});
|
|
353
|
-
obj.length = 0;
|
|
354
|
-
obj.push(...keyed.map((entry) => entry.item));
|
|
355
|
-
} else if (obj.every((value: any) => isNumericLike(value))) {
|
|
356
|
-
obj.sort((a: any, b: any) => toNumber(a) - toNumber(b));
|
|
357
|
-
} else {
|
|
358
|
-
obj.sort();
|
|
359
|
-
}
|
|
360
|
-
if (reverse) obj.reverse();
|
|
361
|
-
return null;
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (obj instanceof PyDict) {
|
|
366
|
-
if (name === 'items')
|
|
367
|
-
return () =>
|
|
368
|
-
Array.from(obj.entries()).map(([k, v]) => {
|
|
369
|
-
const tup = [k, v];
|
|
370
|
-
(tup as any).__tuple__ = true;
|
|
371
|
-
return tup;
|
|
372
|
-
});
|
|
373
|
-
const value = (obj as any)[name];
|
|
374
|
-
if (typeof value === 'function') return value.bind(obj);
|
|
375
|
-
return value;
|
|
376
|
-
}
|
|
377
|
-
if (obj instanceof Set) {
|
|
378
|
-
if (name === 'add') return (value: any) => obj.add(value);
|
|
379
|
-
if (name === 'update')
|
|
380
|
-
return (values: any) => {
|
|
381
|
-
const items = Array.isArray(values) ? values : Array.from(values);
|
|
382
|
-
for (const item of items) obj.add(item);
|
|
383
|
-
};
|
|
384
|
-
if (name === 'remove') return (value: any) => obj.delete(value);
|
|
385
|
-
}
|
|
386
|
-
if (obj && typeof obj === 'object' && obj.__typeName__) {
|
|
387
|
-
if (name === '__name__') return obj.__typeName__;
|
|
388
|
-
}
|
|
389
|
-
if (obj && obj.__typeName__ === undefined && name === '__name__') {
|
|
390
|
-
return obj.name;
|
|
391
|
-
}
|
|
392
|
-
return (obj as any)[name];
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export function setAttribute(this: VirtualMachine, obj: any, name: string, value: any) {
|
|
396
|
-
if (obj && obj.__moduleScope__) {
|
|
397
|
-
obj.__moduleScope__.values.set(name, value);
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
if (obj instanceof PyInstance) {
|
|
401
|
-
obj.attributes.set(name, value);
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
(obj as any)[name] = value;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
export function findClassAttribute(klass: PyClass, name: string): any {
|
|
408
|
-
if (klass.attributes.has(name)) return klass.attributes.get(name);
|
|
409
|
-
for (const base of klass.bases) {
|
|
410
|
-
const attr = findClassAttribute(base, name);
|
|
411
|
-
if (attr !== undefined) return attr;
|
|
412
|
-
}
|
|
413
|
-
return undefined;
|
|
414
|
-
}
|
package/src/vm/runtime-types.ts
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
|
|
3
|
-
export type ScopeValue = any;
|
|
4
|
-
|
|
5
|
-
export class ReturnSignal {
|
|
6
|
-
value: any;
|
|
7
|
-
constructor(value: any) {
|
|
8
|
-
this.value = value;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class BreakSignal {}
|
|
13
|
-
export class ContinueSignal {}
|
|
14
|
-
|
|
15
|
-
export class PyException extends Error {
|
|
16
|
-
pyType: string;
|
|
17
|
-
pyValue: any;
|
|
18
|
-
constructor(pyType: string, message?: string, pyValue?: any) {
|
|
19
|
-
super(message || pyType);
|
|
20
|
-
this.pyType = pyType;
|
|
21
|
-
this.pyValue = pyValue;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class Scope {
|
|
26
|
-
values: Map<string, ScopeValue> = new Map();
|
|
27
|
-
parent: Scope | null;
|
|
28
|
-
globals: Set<string> = new Set();
|
|
29
|
-
nonlocals: Set<string> = new Set();
|
|
30
|
-
|
|
31
|
-
constructor(parent: Scope | null = null) {
|
|
32
|
-
this.parent = parent;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get(name: string): ScopeValue {
|
|
36
|
-
if (this.values.has(name)) {
|
|
37
|
-
return this.values.get(name);
|
|
38
|
-
}
|
|
39
|
-
if (this.parent) {
|
|
40
|
-
return this.parent.get(name);
|
|
41
|
-
}
|
|
42
|
-
throw new PyException('NameError', `name '${name}' is not defined`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
set(name: string, value: ScopeValue): void {
|
|
46
|
-
if (this.globals.has(name) && this.parent) {
|
|
47
|
-
this.root().values.set(name, value);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (this.nonlocals.has(name) && this.parent) {
|
|
51
|
-
const scope = this.parent.findScopeWith(name);
|
|
52
|
-
if (!scope) {
|
|
53
|
-
throw new PyException('NameError', `no binding for nonlocal '${name}' found`);
|
|
54
|
-
}
|
|
55
|
-
scope.values.set(name, value);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
this.values.set(name, value);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
root(): Scope {
|
|
62
|
-
let scope: Scope = this;
|
|
63
|
-
while (scope.parent) {
|
|
64
|
-
scope = scope.parent;
|
|
65
|
-
}
|
|
66
|
-
return scope;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
findScopeWith(name: string): Scope | null {
|
|
70
|
-
let scope: Scope | null = this;
|
|
71
|
-
while (scope) {
|
|
72
|
-
if (scope.values.has(name)) return scope;
|
|
73
|
-
scope = scope.parent;
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export class PyFunction {
|
|
80
|
-
name: string;
|
|
81
|
-
params: any[];
|
|
82
|
-
body: any[];
|
|
83
|
-
closure: Scope;
|
|
84
|
-
isGenerator: boolean;
|
|
85
|
-
|
|
86
|
-
constructor(name: string, params: any[], body: any[], closure: Scope, isGenerator: boolean) {
|
|
87
|
-
this.name = name;
|
|
88
|
-
this.params = params;
|
|
89
|
-
this.body = body;
|
|
90
|
-
this.closure = closure;
|
|
91
|
-
this.isGenerator = isGenerator;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export class PyClass {
|
|
96
|
-
name: string;
|
|
97
|
-
bases: PyClass[];
|
|
98
|
-
attributes: Map<string, any>;
|
|
99
|
-
isException: boolean;
|
|
100
|
-
|
|
101
|
-
constructor(name: string, bases: PyClass[], attributes: Map<string, any>, isException: boolean = false) {
|
|
102
|
-
this.name = name;
|
|
103
|
-
this.bases = bases;
|
|
104
|
-
this.attributes = attributes;
|
|
105
|
-
this.isException = isException;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export class PyInstance {
|
|
110
|
-
klass: PyClass;
|
|
111
|
-
attributes: Map<string, any>;
|
|
112
|
-
|
|
113
|
-
constructor(klass: PyClass) {
|
|
114
|
-
this.klass = klass;
|
|
115
|
-
this.attributes = new Map();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export class PyGenerator {
|
|
120
|
-
private iterator: Generator<any, any, any>;
|
|
121
|
-
|
|
122
|
-
constructor(iterator: Generator<any, any, any>) {
|
|
123
|
-
this.iterator = iterator;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
next(value?: any) {
|
|
127
|
-
const result = this.iterator.next(value === undefined ? null : value);
|
|
128
|
-
if (result.done) {
|
|
129
|
-
throw new PyException('StopIteration', 'StopIteration');
|
|
130
|
-
}
|
|
131
|
-
return result.value;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
send(value?: any) {
|
|
135
|
-
const result = this.iterator.next(value === undefined ? null : value);
|
|
136
|
-
if (result.done) {
|
|
137
|
-
throw new PyException('StopIteration', 'StopIteration');
|
|
138
|
-
}
|
|
139
|
-
return result.value;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
[Symbol.iterator]() {
|
|
143
|
-
return this.iterator;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export type DictEntry = { key: any; value: any };
|
|
148
|
-
|
|
149
|
-
export class PyDict {
|
|
150
|
-
private primitiveStore: Map<string, DictEntry> = new Map();
|
|
151
|
-
private objectStore: Map<any, DictEntry> = new Map();
|
|
152
|
-
|
|
153
|
-
get size(): number {
|
|
154
|
-
return this.primitiveStore.size + this.objectStore.size;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
set(key: any, value: any): this {
|
|
158
|
-
const info = this.keyInfo(key);
|
|
159
|
-
const existing = info.store.get(info.id);
|
|
160
|
-
if (existing) {
|
|
161
|
-
existing.value = value;
|
|
162
|
-
return this;
|
|
163
|
-
}
|
|
164
|
-
info.store.set(info.id, { key, value });
|
|
165
|
-
return this;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
get(key: any): any {
|
|
169
|
-
const info = this.keyInfo(key);
|
|
170
|
-
const entry = info.store.get(info.id);
|
|
171
|
-
return entry ? entry.value : undefined;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
has(key: any): boolean {
|
|
175
|
-
const info = this.keyInfo(key);
|
|
176
|
-
return info.store.has(info.id);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
delete(key: any): boolean {
|
|
180
|
-
const info = this.keyInfo(key);
|
|
181
|
-
return info.store.delete(info.id);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
*entries(): IterableIterator<[any, any]> {
|
|
185
|
-
for (const entry of this.primitiveStore.values()) {
|
|
186
|
-
yield [entry.key, entry.value];
|
|
187
|
-
}
|
|
188
|
-
for (const entry of this.objectStore.values()) {
|
|
189
|
-
yield [entry.key, entry.value];
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
*keys(): IterableIterator<any> {
|
|
194
|
-
for (const entry of this.primitiveStore.values()) {
|
|
195
|
-
yield entry.key;
|
|
196
|
-
}
|
|
197
|
-
for (const entry of this.objectStore.values()) {
|
|
198
|
-
yield entry.key;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
*values(): IterableIterator<any> {
|
|
203
|
-
for (const entry of this.primitiveStore.values()) {
|
|
204
|
-
yield entry.value;
|
|
205
|
-
}
|
|
206
|
-
for (const entry of this.objectStore.values()) {
|
|
207
|
-
yield entry.value;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
[Symbol.iterator](): IterableIterator<[any, any]> {
|
|
212
|
-
return this.entries();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private keyInfo(key: any): { store: Map<any, DictEntry>; id: any } {
|
|
216
|
-
const numeric = this.normalizeNumericKey(key);
|
|
217
|
-
if (numeric !== null) {
|
|
218
|
-
return { store: this.primitiveStore, id: `n:${String(numeric)}` };
|
|
219
|
-
}
|
|
220
|
-
if (typeof key === 'string') {
|
|
221
|
-
return { store: this.primitiveStore, id: `s:${key}` };
|
|
222
|
-
}
|
|
223
|
-
if (key === null) {
|
|
224
|
-
return { store: this.primitiveStore, id: 'none' };
|
|
225
|
-
}
|
|
226
|
-
if (key === undefined) {
|
|
227
|
-
return { store: this.primitiveStore, id: 'undefined' };
|
|
228
|
-
}
|
|
229
|
-
return { store: this.objectStore, id: key };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
private normalizeNumericKey(key: any): number | bigint | null {
|
|
233
|
-
if (typeof key === 'boolean') return key ? 1 : 0;
|
|
234
|
-
if (typeof key === 'bigint') return key;
|
|
235
|
-
if (typeof key === 'number') return key;
|
|
236
|
-
if (key instanceof Number) return key.valueOf();
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export class PyFile {
|
|
242
|
-
path: string;
|
|
243
|
-
mode: string;
|
|
244
|
-
handle: number | null;
|
|
245
|
-
|
|
246
|
-
constructor(path: string, mode: string) {
|
|
247
|
-
this.path = path;
|
|
248
|
-
this.mode = mode;
|
|
249
|
-
this.handle = null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
open() {
|
|
253
|
-
if (this.handle !== null) return;
|
|
254
|
-
if (this.mode.includes('w')) {
|
|
255
|
-
this.handle = fs.openSync(this.path, 'w');
|
|
256
|
-
} else if (this.mode.includes('r')) {
|
|
257
|
-
this.handle = fs.openSync(this.path, 'r');
|
|
258
|
-
} else {
|
|
259
|
-
this.handle = fs.openSync(this.path, 'r');
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
write(data: string) {
|
|
264
|
-
this.open();
|
|
265
|
-
if (this.handle === null) return;
|
|
266
|
-
fs.writeSync(this.handle, data);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
read(): string {
|
|
270
|
-
if (this.mode.includes('r')) {
|
|
271
|
-
return fs.readFileSync(this.path, 'utf8');
|
|
272
|
-
}
|
|
273
|
-
return '';
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
close() {
|
|
277
|
-
if (this.handle !== null) {
|
|
278
|
-
fs.closeSync(this.handle);
|
|
279
|
-
this.handle = null;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
__enter__() {
|
|
284
|
-
this.open();
|
|
285
|
-
return this;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
__exit__() {
|
|
289
|
-
this.close();
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
}
|