@lewin671/python-vm 0.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/.claude/settings.local.json +3 -0
- package/.prettierrc +7 -0
- package/Agents.md +66 -0
- package/README.md +93 -0
- package/README_zh-CN.md +93 -0
- package/SETUP.md +171 -0
- package/dist/compiler.d.ts +20 -0
- package/dist/compiler.js +91 -0
- package/dist/compiler_module/compiler.d.ts +8 -0
- package/dist/compiler_module/compiler.js +22 -0
- package/dist/compiler_module/index.d.ts +2 -0
- package/dist/compiler_module/index.js +6 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +67 -0
- package/dist/lexer/index.d.ts +2 -0
- package/dist/lexer/index.js +6 -0
- package/dist/lexer/lexer.d.ts +16 -0
- package/dist/lexer/lexer.js +403 -0
- package/dist/parser/expressions.d.ts +30 -0
- package/dist/parser/expressions.js +483 -0
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.js +6 -0
- package/dist/parser/parser.d.ts +63 -0
- package/dist/parser/parser.js +129 -0
- package/dist/parser/statements.d.ts +20 -0
- package/dist/parser/statements.js +388 -0
- package/dist/parser/targets.d.ts +6 -0
- package/dist/parser/targets.js +75 -0
- package/dist/types/ast.d.ts +63 -0
- package/dist/types/ast.js +60 -0
- package/dist/types/bytecode.d.ts +38 -0
- package/dist/types/bytecode.js +35 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +20 -0
- package/dist/types/token.d.ts +34 -0
- package/dist/types/token.js +39 -0
- package/dist/vm/builtins.d.ts +4 -0
- package/dist/vm/builtins.js +269 -0
- package/dist/vm/callable.d.ts +8 -0
- package/dist/vm/callable.js +161 -0
- package/dist/vm/execution.d.ts +15 -0
- package/dist/vm/execution.js +283 -0
- package/dist/vm/expression-generator.d.ts +3 -0
- package/dist/vm/expression-generator.js +70 -0
- package/dist/vm/expressions.d.ts +13 -0
- package/dist/vm/expressions.js +390 -0
- package/dist/vm/imports.d.ts +7 -0
- package/dist/vm/imports.js +99 -0
- package/dist/vm/index.d.ts +3 -0
- package/dist/vm/index.js +21 -0
- package/dist/vm/operations.d.ts +16 -0
- package/dist/vm/operations.js +439 -0
- package/dist/vm/runtime-types.d.ts +84 -0
- package/dist/vm/runtime-types.js +290 -0
- package/dist/vm/statements.d.ts +7 -0
- package/dist/vm/statements.js +381 -0
- package/dist/vm/truthy.d.ts +4 -0
- package/dist/vm/truthy.js +47 -0
- package/dist/vm/value-utils.d.ts +28 -0
- package/dist/vm/value-utils.js +225 -0
- package/dist/vm/vm.d.ts +56 -0
- package/dist/vm/vm.js +75 -0
- package/examples/assert_testing.py +38 -0
- package/examples/big_int_precision.py +2 -0
- package/examples/boolean_logic.py +35 -0
- package/examples/break_continue.py +43 -0
- package/examples/classes_objects.py +43 -0
- package/examples/compiler_killer_async.py +6 -0
- package/examples/compiler_killer_bigint.py +3 -0
- package/examples/compiler_killer_bool_int_dict_key.py +5 -0
- package/examples/compiler_killer_bool_len.py +9 -0
- package/examples/compiler_killer_floor_division.py +4 -0
- package/examples/compiler_killer_is_identity.py +3 -0
- package/examples/compiler_killer_list_sort_return.py +3 -0
- package/examples/compiler_killer_match.py +13 -0
- package/examples/compiler_killer_negative_repeat.py +3 -0
- package/examples/compiler_killer_negative_zero_repr.py +3 -0
- package/examples/compiler_killer_rounding.py +4 -0
- package/examples/compiler_killer_slice_assign.py +3 -0
- package/examples/comprehensions.py +28 -0
- package/examples/conditions.py +13 -0
- package/examples/context_manager.py +35 -0
- package/examples/decorators.py +50 -0
- package/examples/exceptions.py +40 -0
- package/examples/fibonacci.py +10 -0
- package/examples/functions.py +38 -0
- package/examples/generator.py +51 -0
- package/examples/global_nonlocal.py +48 -0
- package/examples/hello.py +3 -0
- package/examples/itertools_example.py +33 -0
- package/examples/lists_dicts.py +29 -0
- package/examples/loops.py +19 -0
- package/examples/math_ops.py +15 -0
- package/examples/nan_set.py +6 -0
- package/examples/numbers_operators.py +51 -0
- package/examples/sets.py +36 -0
- package/examples/slicing.py +29 -0
- package/examples/starred_unpacking.py +3 -0
- package/examples/string_formatting.py +36 -0
- package/examples/strings.py +22 -0
- package/examples/tuples.py +45 -0
- package/examples/type_conversion.py +41 -0
- package/jest.config.js +15 -0
- package/notes/iterations/compiler-runtime/compiler-runtime_2025-09-16.md +25 -0
- package/notes/iterations/compiler-runtime/compiler-runtime_2026-01-16.md +24 -0
- package/notes/iterations/compiler-runtime/compiler-runtime_test_2026-01-16.md +21 -0
- package/notes/iterations/floor-division/floor-division_2026-01-16.md +29 -0
- package/package.json +36 -0
- package/prompts/commit.txt +9 -0
- package/prompts/task.txt +21 -0
- package/prompts/test.txt +23 -0
- package/scripts/codex-loop.js +215 -0
- package/scripts/verify.sh +12 -0
- package/src/compiler.ts +58 -0
- package/src/compiler_module/compiler.ts +19 -0
- package/src/compiler_module/index.ts +1 -0
- package/src/index.ts +39 -0
- package/src/lexer/index.ts +1 -0
- package/src/lexer/lexer.ts +402 -0
- package/src/parser/expressions.ts +462 -0
- package/src/parser/index.ts +1 -0
- package/src/parser/parser.ts +102 -0
- package/src/parser/statements.ts +366 -0
- package/src/parser/targets.ts +71 -0
- package/src/types/ast.ts +64 -0
- package/src/types/bytecode.ts +50 -0
- package/src/types/index.ts +3 -0
- package/src/types/token.ts +44 -0
- package/src/vm/builtins.ts +237 -0
- package/src/vm/callable.ts +154 -0
- package/src/vm/execution.ts +251 -0
- package/src/vm/expression-generator.ts +65 -0
- package/src/vm/expressions.ts +373 -0
- package/src/vm/imports.ts +61 -0
- package/src/vm/index.ts +2 -0
- package/src/vm/operations.ts +414 -0
- package/src/vm/runtime-types.ts +292 -0
- package/src/vm/statements.ts +358 -0
- package/src/vm/truthy.ts +36 -0
- package/src/vm/value-utils.ts +173 -0
- package/src/vm/vm.ts +80 -0
- package/tests/compiler.test.ts +111 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateExpression = evaluateExpression;
|
|
4
|
+
exports.evaluateExpressionString = evaluateExpressionString;
|
|
5
|
+
exports.executeExpressionInline = executeExpressionInline;
|
|
6
|
+
exports.applyFormatSpec = applyFormatSpec;
|
|
7
|
+
exports.splitFormatSpec = splitFormatSpec;
|
|
8
|
+
exports.applyWidth = applyWidth;
|
|
9
|
+
exports.contains = contains;
|
|
10
|
+
const types_1 = require("../types");
|
|
11
|
+
const lexer_1 = require("../lexer");
|
|
12
|
+
const parser_1 = require("../parser");
|
|
13
|
+
const runtime_types_1 = require("./runtime-types");
|
|
14
|
+
const value_utils_1 = require("./value-utils");
|
|
15
|
+
function evaluateExpression(node, scope) {
|
|
16
|
+
switch (node.type) {
|
|
17
|
+
case types_1.ASTNodeType.NUMBER_LITERAL: {
|
|
18
|
+
const raw = node.value;
|
|
19
|
+
if (typeof raw === 'number')
|
|
20
|
+
return raw;
|
|
21
|
+
if (typeof raw === 'string' && raw.endsWith('j')) {
|
|
22
|
+
const imag = parseFloat(raw.slice(0, -1));
|
|
23
|
+
return { __complex__: true, re: 0, im: imag };
|
|
24
|
+
}
|
|
25
|
+
if (raw.includes('.'))
|
|
26
|
+
return new Number(parseFloat(raw));
|
|
27
|
+
const big = BigInt(raw);
|
|
28
|
+
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
|
29
|
+
const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
|
|
30
|
+
if (big > maxSafe || big < minSafe)
|
|
31
|
+
return big;
|
|
32
|
+
return Number(raw);
|
|
33
|
+
}
|
|
34
|
+
case types_1.ASTNodeType.STRING_LITERAL: {
|
|
35
|
+
const { value, isFString } = (0, value_utils_1.parseStringToken)(node.value);
|
|
36
|
+
if (isFString) {
|
|
37
|
+
return value.replace(/\{([^}]+)\}/g, (_m, expr) => {
|
|
38
|
+
const { rawExpr, rawSpec } = this.splitFormatSpec(expr);
|
|
39
|
+
const inner = this.evaluateExpressionString(rawExpr.trim(), scope);
|
|
40
|
+
const formatted = this.applyFormatSpec(inner, rawSpec ? rawSpec.trim() : '');
|
|
41
|
+
return formatted;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
case types_1.ASTNodeType.BOOLEAN_LITERAL:
|
|
47
|
+
return node.value;
|
|
48
|
+
case types_1.ASTNodeType.NONE_LITERAL:
|
|
49
|
+
return null;
|
|
50
|
+
case types_1.ASTNodeType.IDENTIFIER:
|
|
51
|
+
return scope.get(node.name);
|
|
52
|
+
case types_1.ASTNodeType.LIST_LITERAL:
|
|
53
|
+
return node.elements.map((el) => this.evaluateExpression(el, scope));
|
|
54
|
+
case types_1.ASTNodeType.LIST_COMP: {
|
|
55
|
+
const result = [];
|
|
56
|
+
const compScope = new runtime_types_1.Scope(scope);
|
|
57
|
+
this.evaluateComprehension(node.comprehension, compScope, () => {
|
|
58
|
+
result.push(this.evaluateExpression(node.expression, compScope));
|
|
59
|
+
});
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
case types_1.ASTNodeType.TUPLE_LITERAL: {
|
|
63
|
+
const arr = node.elements.map((el) => this.evaluateExpression(el, scope));
|
|
64
|
+
arr.__tuple__ = true;
|
|
65
|
+
return arr;
|
|
66
|
+
}
|
|
67
|
+
case types_1.ASTNodeType.SET_COMP: {
|
|
68
|
+
const result = new Set();
|
|
69
|
+
const compScope = new runtime_types_1.Scope(scope);
|
|
70
|
+
this.evaluateComprehension(node.comprehension, compScope, () => {
|
|
71
|
+
result.add(this.evaluateExpression(node.expression, compScope));
|
|
72
|
+
});
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
case types_1.ASTNodeType.SET_LITERAL:
|
|
76
|
+
return new Set(node.elements.map((el) => this.evaluateExpression(el, scope)));
|
|
77
|
+
case types_1.ASTNodeType.DICT_LITERAL: {
|
|
78
|
+
const map = new runtime_types_1.PyDict();
|
|
79
|
+
for (const entry of node.entries) {
|
|
80
|
+
map.set(this.evaluateExpression(entry.key, scope), this.evaluateExpression(entry.value, scope));
|
|
81
|
+
}
|
|
82
|
+
return map;
|
|
83
|
+
}
|
|
84
|
+
case types_1.ASTNodeType.DICT_COMP: {
|
|
85
|
+
const map = new runtime_types_1.PyDict();
|
|
86
|
+
const compScope = new runtime_types_1.Scope(scope);
|
|
87
|
+
this.evaluateComprehension(node.comprehension, compScope, () => {
|
|
88
|
+
map.set(this.evaluateExpression(node.key, compScope), this.evaluateExpression(node.value, compScope));
|
|
89
|
+
});
|
|
90
|
+
return map;
|
|
91
|
+
}
|
|
92
|
+
case types_1.ASTNodeType.GENERATOR_EXPR: {
|
|
93
|
+
const self = this;
|
|
94
|
+
const compScope = new runtime_types_1.Scope(scope);
|
|
95
|
+
const iterator = function* () {
|
|
96
|
+
yield* self.generateComprehension(node.comprehension, compScope, () => self.evaluateExpression(node.expression, compScope));
|
|
97
|
+
};
|
|
98
|
+
return new runtime_types_1.PyGenerator(iterator());
|
|
99
|
+
}
|
|
100
|
+
case types_1.ASTNodeType.BINARY_OPERATION: {
|
|
101
|
+
const left = this.evaluateExpression(node.left, scope);
|
|
102
|
+
const right = this.evaluateExpression(node.right, scope);
|
|
103
|
+
return this.applyBinary(node.operator, left, right);
|
|
104
|
+
}
|
|
105
|
+
case types_1.ASTNodeType.UNARY_OPERATION: {
|
|
106
|
+
const operand = this.evaluateExpression(node.operand, scope);
|
|
107
|
+
switch (node.operator) {
|
|
108
|
+
case 'not':
|
|
109
|
+
return !this.isTruthy(operand, scope);
|
|
110
|
+
case '+':
|
|
111
|
+
return typeof operand === 'bigint' ? operand : +operand;
|
|
112
|
+
case '-':
|
|
113
|
+
if ((0, value_utils_1.isIntObject)(operand)) {
|
|
114
|
+
const boxed = new Number(-operand.valueOf());
|
|
115
|
+
boxed.__int__ = true;
|
|
116
|
+
return boxed;
|
|
117
|
+
}
|
|
118
|
+
if ((0, value_utils_1.isFloatObject)(operand))
|
|
119
|
+
return new Number(-operand.valueOf());
|
|
120
|
+
return -operand;
|
|
121
|
+
case '~':
|
|
122
|
+
return ~operand;
|
|
123
|
+
default:
|
|
124
|
+
throw new runtime_types_1.PyException('TypeError', `unsupported unary operator ${node.operator}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
case types_1.ASTNodeType.BOOL_OPERATION: {
|
|
128
|
+
if (node.operator === 'and') {
|
|
129
|
+
const left = this.evaluateExpression(node.values[0], scope);
|
|
130
|
+
return this.isTruthy(left, scope) ? this.evaluateExpression(node.values[1], scope) : left;
|
|
131
|
+
}
|
|
132
|
+
const left = this.evaluateExpression(node.values[0], scope);
|
|
133
|
+
return this.isTruthy(left, scope) ? left : this.evaluateExpression(node.values[1], scope);
|
|
134
|
+
}
|
|
135
|
+
case types_1.ASTNodeType.COMPARE: {
|
|
136
|
+
let left = this.evaluateExpression(node.left, scope);
|
|
137
|
+
for (let i = 0; i < node.ops.length; i++) {
|
|
138
|
+
const op = node.ops[i];
|
|
139
|
+
const right = this.evaluateExpression(node.comparators[i], scope);
|
|
140
|
+
let result = false;
|
|
141
|
+
switch (op) {
|
|
142
|
+
case '==':
|
|
143
|
+
result = (0, value_utils_1.numericEquals)(left, right);
|
|
144
|
+
break;
|
|
145
|
+
case '!=':
|
|
146
|
+
result = !(0, value_utils_1.numericEquals)(left, right);
|
|
147
|
+
break;
|
|
148
|
+
case '<': {
|
|
149
|
+
const numeric = (0, value_utils_1.numericCompare)(left, right);
|
|
150
|
+
if (numeric) {
|
|
151
|
+
if (numeric.kind === 'float') {
|
|
152
|
+
result =
|
|
153
|
+
!Number.isNaN(numeric.left) && !Number.isNaN(numeric.right) && numeric.left < numeric.right;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
result = numeric.left < numeric.right;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
result = left < right;
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case '>': {
|
|
165
|
+
const numeric = (0, value_utils_1.numericCompare)(left, right);
|
|
166
|
+
if (numeric) {
|
|
167
|
+
if (numeric.kind === 'float') {
|
|
168
|
+
result =
|
|
169
|
+
!Number.isNaN(numeric.left) && !Number.isNaN(numeric.right) && numeric.left > numeric.right;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
result = numeric.left > numeric.right;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
result = left > right;
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case '<=': {
|
|
181
|
+
const numeric = (0, value_utils_1.numericCompare)(left, right);
|
|
182
|
+
if (numeric) {
|
|
183
|
+
if (numeric.kind === 'float') {
|
|
184
|
+
result =
|
|
185
|
+
!Number.isNaN(numeric.left) && !Number.isNaN(numeric.right) && numeric.left <= numeric.right;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
result = numeric.left <= numeric.right;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
result = left <= right;
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case '>=': {
|
|
197
|
+
const numeric = (0, value_utils_1.numericCompare)(left, right);
|
|
198
|
+
if (numeric) {
|
|
199
|
+
if (numeric.kind === 'float') {
|
|
200
|
+
result =
|
|
201
|
+
!Number.isNaN(numeric.left) && !Number.isNaN(numeric.right) && numeric.left >= numeric.right;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
result = numeric.left >= numeric.right;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
result = left >= right;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'in':
|
|
213
|
+
result = this.contains(right, left);
|
|
214
|
+
break;
|
|
215
|
+
case 'not in':
|
|
216
|
+
result = !this.contains(right, left);
|
|
217
|
+
break;
|
|
218
|
+
case 'is':
|
|
219
|
+
result = left === right;
|
|
220
|
+
break;
|
|
221
|
+
case 'is not':
|
|
222
|
+
result = left !== right;
|
|
223
|
+
break;
|
|
224
|
+
default:
|
|
225
|
+
throw new runtime_types_1.PyException('TypeError', `unsupported comparison ${op}`);
|
|
226
|
+
}
|
|
227
|
+
if (!result)
|
|
228
|
+
return false;
|
|
229
|
+
left = right;
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
case types_1.ASTNodeType.CALL: {
|
|
234
|
+
const callee = this.evaluateExpression(node.callee, scope);
|
|
235
|
+
const positional = [];
|
|
236
|
+
const kwargs = {};
|
|
237
|
+
for (const arg of node.args) {
|
|
238
|
+
if (arg.type === 'KeywordArg') {
|
|
239
|
+
kwargs[arg.name] = this.evaluateExpression(arg.value, scope);
|
|
240
|
+
}
|
|
241
|
+
else if (arg.type === 'StarArg') {
|
|
242
|
+
const value = this.evaluateExpression(arg.value, scope);
|
|
243
|
+
positional.push(...(Array.isArray(value) ? value : Array.from(value)));
|
|
244
|
+
}
|
|
245
|
+
else if (arg.type === 'KwArg') {
|
|
246
|
+
const value = this.evaluateExpression(arg.value, scope);
|
|
247
|
+
Object.assign(kwargs, value);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
positional.push(this.evaluateExpression(arg, scope));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return this.callFunction(callee, positional, scope, kwargs);
|
|
254
|
+
}
|
|
255
|
+
case types_1.ASTNodeType.ATTRIBUTE: {
|
|
256
|
+
const obj = this.evaluateExpression(node.object, scope);
|
|
257
|
+
return this.getAttribute(obj, node.name, scope);
|
|
258
|
+
}
|
|
259
|
+
case types_1.ASTNodeType.SUBSCRIPT: {
|
|
260
|
+
const obj = this.evaluateExpression(node.object, scope);
|
|
261
|
+
if (node.index && node.index.type === types_1.ASTNodeType.SLICE) {
|
|
262
|
+
const slice = {
|
|
263
|
+
type: types_1.ASTNodeType.SLICE,
|
|
264
|
+
start: node.index.start ? this.evaluateExpression(node.index.start, scope) : null,
|
|
265
|
+
end: node.index.end ? this.evaluateExpression(node.index.end, scope) : null,
|
|
266
|
+
step: node.index.step ? this.evaluateExpression(node.index.step, scope) : null,
|
|
267
|
+
};
|
|
268
|
+
return this.getSubscript(obj, slice);
|
|
269
|
+
}
|
|
270
|
+
const index = this.evaluateExpression(node.index, scope);
|
|
271
|
+
return this.getSubscript(obj, index);
|
|
272
|
+
}
|
|
273
|
+
case types_1.ASTNodeType.IF_EXPRESSION: {
|
|
274
|
+
const test = this.evaluateExpression(node.test, scope);
|
|
275
|
+
return this.isTruthy(test, scope)
|
|
276
|
+
? this.evaluateExpression(node.consequent, scope)
|
|
277
|
+
: this.evaluateExpression(node.alternate, scope);
|
|
278
|
+
}
|
|
279
|
+
case types_1.ASTNodeType.LAMBDA: {
|
|
280
|
+
return new runtime_types_1.PyFunction('<lambda>', node.params.map((p) => ({ type: 'Param', name: p })), [
|
|
281
|
+
{
|
|
282
|
+
type: types_1.ASTNodeType.RETURN_STATEMENT,
|
|
283
|
+
value: node.body,
|
|
284
|
+
},
|
|
285
|
+
], scope, false);
|
|
286
|
+
}
|
|
287
|
+
default:
|
|
288
|
+
throw new Error(`Unsupported expression type: ${node.type}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function evaluateExpressionString(expr, scope) {
|
|
292
|
+
const wrapped = `__f = ${expr}\n`;
|
|
293
|
+
const tokens = new lexer_1.Lexer(wrapped).tokenize();
|
|
294
|
+
const ast = new parser_1.Parser(tokens).parse();
|
|
295
|
+
const assignment = ast.body[0];
|
|
296
|
+
if (!assignment || assignment.type !== types_1.ASTNodeType.ASSIGNMENT) {
|
|
297
|
+
return this.executeExpressionInline(expr, scope);
|
|
298
|
+
}
|
|
299
|
+
return this.evaluateExpression(assignment.value, scope);
|
|
300
|
+
}
|
|
301
|
+
function executeExpressionInline(expr, scope) {
|
|
302
|
+
const tokens = expr.trim().split(/\s+/);
|
|
303
|
+
if (tokens.length === 1 && scope.values.has(tokens[0])) {
|
|
304
|
+
return scope.get(tokens[0]);
|
|
305
|
+
}
|
|
306
|
+
return expr;
|
|
307
|
+
}
|
|
308
|
+
function applyFormatSpec(value, spec) {
|
|
309
|
+
if (!spec)
|
|
310
|
+
return (0, value_utils_1.pyStr)(value);
|
|
311
|
+
if (spec.endsWith('%')) {
|
|
312
|
+
const digits = spec.includes('.') ? parseInt(spec.split('.')[1], 10) : 0;
|
|
313
|
+
const num = (0, value_utils_1.isNumericLike)(value) ? (0, value_utils_1.toNumber)(value) : parseFloat(value);
|
|
314
|
+
return (num * 100).toFixed(digits) + '%';
|
|
315
|
+
}
|
|
316
|
+
if (spec.includes('.')) {
|
|
317
|
+
const parts = spec.split('.');
|
|
318
|
+
const width = parts[0];
|
|
319
|
+
const precision = parseInt(parts[1].replace(/[^\d]/g, ''), 10);
|
|
320
|
+
const num = (0, value_utils_1.isNumericLike)(value) ? (0, value_utils_1.toNumber)(value) : parseFloat(value);
|
|
321
|
+
const formatted = num.toFixed(precision);
|
|
322
|
+
return this.applyWidth(formatted, width);
|
|
323
|
+
}
|
|
324
|
+
if (spec === 'd')
|
|
325
|
+
return typeof value === 'bigint' ? value.toString() : String(parseInt(value, 10));
|
|
326
|
+
if (spec === 'b')
|
|
327
|
+
return typeof value === 'bigint' ? value.toString(2) : Number(value).toString(2);
|
|
328
|
+
if (spec === 'x')
|
|
329
|
+
return typeof value === 'bigint' ? value.toString(16) : Number(value).toString(16);
|
|
330
|
+
if (spec === 'o')
|
|
331
|
+
return typeof value === 'bigint' ? value.toString(8) : Number(value).toString(8);
|
|
332
|
+
return this.applyWidth(String(value), spec);
|
|
333
|
+
}
|
|
334
|
+
function splitFormatSpec(expr) {
|
|
335
|
+
let depth = 0;
|
|
336
|
+
for (let i = 0; i < expr.length; i++) {
|
|
337
|
+
const ch = expr[i];
|
|
338
|
+
if (ch === '(' || ch === '[' || ch === '{')
|
|
339
|
+
depth++;
|
|
340
|
+
if (ch === ')' || ch === ']' || ch === '}')
|
|
341
|
+
depth = Math.max(0, depth - 1);
|
|
342
|
+
if (ch === ':' && depth === 0) {
|
|
343
|
+
return { rawExpr: expr.slice(0, i), rawSpec: expr.slice(i + 1) };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return { rawExpr: expr, rawSpec: '' };
|
|
347
|
+
}
|
|
348
|
+
function applyWidth(text, spec) {
|
|
349
|
+
const match = spec.match(/([<^>])?(\d+)/);
|
|
350
|
+
if (!match)
|
|
351
|
+
return text;
|
|
352
|
+
const align = match[1] || '>';
|
|
353
|
+
const width = parseInt(match[2], 10);
|
|
354
|
+
if (text.length >= width)
|
|
355
|
+
return text;
|
|
356
|
+
const padding = width - text.length;
|
|
357
|
+
if (align === '<')
|
|
358
|
+
return text + ' '.repeat(padding);
|
|
359
|
+
if (align === '^') {
|
|
360
|
+
const left = Math.floor(padding / 2);
|
|
361
|
+
const right = padding - left;
|
|
362
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
363
|
+
}
|
|
364
|
+
return ' '.repeat(padding) + text;
|
|
365
|
+
}
|
|
366
|
+
function contains(container, value) {
|
|
367
|
+
if (Array.isArray(container)) {
|
|
368
|
+
if ((0, value_utils_1.isNumericLike)(value)) {
|
|
369
|
+
return container.some((item) => (0, value_utils_1.isNumericLike)(item) && (0, value_utils_1.numericEquals)(item, value));
|
|
370
|
+
}
|
|
371
|
+
return container.includes(value);
|
|
372
|
+
}
|
|
373
|
+
if (typeof container === 'string')
|
|
374
|
+
return container.includes(value);
|
|
375
|
+
if (container instanceof Set) {
|
|
376
|
+
if (container.has(value))
|
|
377
|
+
return true;
|
|
378
|
+
if ((0, value_utils_1.isNumericLike)(value)) {
|
|
379
|
+
for (const item of container.values()) {
|
|
380
|
+
if ((0, value_utils_1.isNumericLike)(item) && (0, value_utils_1.numericEquals)(item, value))
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (container instanceof runtime_types_1.PyDict)
|
|
387
|
+
return container.has(value);
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=expressions.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VirtualMachine } from './vm';
|
|
2
|
+
import { Scope } from './runtime-types';
|
|
3
|
+
export declare function importModule(this: VirtualMachine, name: string, scope: Scope): any;
|
|
4
|
+
export declare function createAsyncioModule(this: VirtualMachine, scope: Scope): any;
|
|
5
|
+
export declare function loadModuleFromFile(this: VirtualMachine, name: string, scope: Scope): any;
|
|
6
|
+
export declare function resolveModulePath(this: VirtualMachine, name: string): string | null;
|
|
7
|
+
//# sourceMappingURL=imports.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.importModule = importModule;
|
|
37
|
+
exports.createAsyncioModule = createAsyncioModule;
|
|
38
|
+
exports.loadModuleFromFile = loadModuleFromFile;
|
|
39
|
+
exports.resolveModulePath = resolveModulePath;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const lexer_1 = require("../lexer");
|
|
43
|
+
const parser_1 = require("../parser");
|
|
44
|
+
const runtime_types_1 = require("./runtime-types");
|
|
45
|
+
function importModule(name, scope) {
|
|
46
|
+
if (this.moduleCache.has(name)) {
|
|
47
|
+
return this.moduleCache.get(name);
|
|
48
|
+
}
|
|
49
|
+
let module;
|
|
50
|
+
if (name === 'asyncio') {
|
|
51
|
+
module = this.createAsyncioModule(scope);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
module = this.loadModuleFromFile(name, scope);
|
|
55
|
+
}
|
|
56
|
+
this.moduleCache.set(name, module);
|
|
57
|
+
return module;
|
|
58
|
+
}
|
|
59
|
+
function createAsyncioModule(scope) {
|
|
60
|
+
return {
|
|
61
|
+
__name__: 'asyncio',
|
|
62
|
+
run: (value) => {
|
|
63
|
+
if (value instanceof runtime_types_1.PyFunction) {
|
|
64
|
+
return this.callFunction(value, [], scope);
|
|
65
|
+
}
|
|
66
|
+
if (value instanceof runtime_types_1.PyGenerator) {
|
|
67
|
+
return value.next();
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function loadModuleFromFile(name, scope) {
|
|
74
|
+
const modulePath = this.resolveModulePath(name);
|
|
75
|
+
if (!modulePath) {
|
|
76
|
+
throw new runtime_types_1.PyException('ImportError', `No module named '${name}'`);
|
|
77
|
+
}
|
|
78
|
+
const code = fs.readFileSync(modulePath, 'utf-8');
|
|
79
|
+
const lexer = new lexer_1.Lexer(code);
|
|
80
|
+
const tokens = lexer.tokenize();
|
|
81
|
+
const parser = new parser_1.Parser(tokens);
|
|
82
|
+
const ast = parser.parse();
|
|
83
|
+
const moduleScope = new runtime_types_1.Scope(scope.root());
|
|
84
|
+
moduleScope.set('__name__', name);
|
|
85
|
+
this.executeBlock(ast.body, moduleScope);
|
|
86
|
+
return { __name__: name, __moduleScope__: moduleScope };
|
|
87
|
+
}
|
|
88
|
+
function resolveModulePath(name) {
|
|
89
|
+
for (const basePath of this.moduleSearchPaths) {
|
|
90
|
+
const directPath = path.join(basePath, `${name}.py`);
|
|
91
|
+
if (fs.existsSync(directPath))
|
|
92
|
+
return directPath;
|
|
93
|
+
const initPath = path.join(basePath, name, '__init__.py');
|
|
94
|
+
if (fs.existsSync(initPath))
|
|
95
|
+
return initPath;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=imports.js.map
|
package/dist/vm/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.VirtualMachine = void 0;
|
|
18
|
+
var vm_1 = require("./vm");
|
|
19
|
+
Object.defineProperty(exports, "VirtualMachine", { enumerable: true, get: function () { return vm_1.VirtualMachine; } });
|
|
20
|
+
__exportStar(require("./runtime-types"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { VirtualMachine } from './vm';
|
|
2
|
+
import { PyClass, Scope } from './runtime-types';
|
|
3
|
+
export declare function applyBinary(this: VirtualMachine, op: string, left: any, right: any): any;
|
|
4
|
+
export declare function formatPercent(this: VirtualMachine, format: string, value: any): string;
|
|
5
|
+
export declare function getSubscript(this: VirtualMachine, obj: any, index: any): any;
|
|
6
|
+
export declare function computeSliceBounds(this: VirtualMachine, length: number, start: any, end: any, step: any): {
|
|
7
|
+
start: number;
|
|
8
|
+
end: number;
|
|
9
|
+
step: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function computeSliceIndices(this: VirtualMachine, length: number, start: any, end: any, step: any): number[];
|
|
12
|
+
export declare function normalizeSliceStep(step: any): number;
|
|
13
|
+
export declare function getAttribute(this: VirtualMachine, obj: any, name: string, scope: Scope): any;
|
|
14
|
+
export declare function setAttribute(this: VirtualMachine, obj: any, name: string, value: any): void;
|
|
15
|
+
export declare function findClassAttribute(klass: PyClass, name: string): any;
|
|
16
|
+
//# sourceMappingURL=operations.d.ts.map
|