@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/builtins.ts
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import type { VirtualMachine } from './vm';
|
|
2
|
-
import { PyClass, PyDict, PyException, PyFile, PyGenerator, Scope } from './runtime-types';
|
|
3
|
-
import { isFloatLike, isNumericLike, pyStr, pyTypeName, toBigIntValue, toNumber } from './value-utils';
|
|
4
|
-
|
|
5
|
-
export function installBuiltins(this: VirtualMachine, scope: Scope) {
|
|
6
|
-
const builtins = new Map<string, any>();
|
|
7
|
-
builtins.set('print', (...args: any[]) => {
|
|
8
|
-
let sep = ' ';
|
|
9
|
-
let end = '\n';
|
|
10
|
-
if (args.length > 0) {
|
|
11
|
-
const last = args[args.length - 1];
|
|
12
|
-
if (last && last.__kwargs__) {
|
|
13
|
-
const kwargs = last.__kwargs__;
|
|
14
|
-
sep = kwargs.sep !== undefined ? kwargs.sep : sep;
|
|
15
|
-
end = kwargs.end !== undefined ? kwargs.end : end;
|
|
16
|
-
args = args.slice(0, -1);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
const output = args.map((a) => pyStr(a)).join(sep) + end;
|
|
20
|
-
process.stdout.write(output);
|
|
21
|
-
return null;
|
|
22
|
-
});
|
|
23
|
-
builtins.set('len', (value: any) => {
|
|
24
|
-
if (typeof value === 'string' || Array.isArray(value)) return value.length;
|
|
25
|
-
if (value instanceof PyDict || value instanceof Set) return value.size;
|
|
26
|
-
throw new PyException('TypeError', 'object has no len()');
|
|
27
|
-
});
|
|
28
|
-
builtins.set('range', (...args: any[]) => {
|
|
29
|
-
let start = 0;
|
|
30
|
-
let end = 0;
|
|
31
|
-
let step = 1;
|
|
32
|
-
if (args.length === 1) {
|
|
33
|
-
end = toNumber(args[0]);
|
|
34
|
-
} else if (args.length === 2) {
|
|
35
|
-
start = toNumber(args[0]);
|
|
36
|
-
end = toNumber(args[1]);
|
|
37
|
-
} else if (args.length >= 3) {
|
|
38
|
-
start = toNumber(args[0]);
|
|
39
|
-
end = toNumber(args[1]);
|
|
40
|
-
step = toNumber(args[2]);
|
|
41
|
-
}
|
|
42
|
-
const result: number[] = [];
|
|
43
|
-
if (step === 0) throw new PyException('ValueError', 'range() arg 3 must not be zero');
|
|
44
|
-
if (step > 0) {
|
|
45
|
-
for (let i = start; i < end; i += step) result.push(i);
|
|
46
|
-
} else {
|
|
47
|
-
for (let i = start; i > end; i += step) result.push(i);
|
|
48
|
-
}
|
|
49
|
-
return result;
|
|
50
|
-
});
|
|
51
|
-
const listFn = (value: any) => {
|
|
52
|
-
if (Array.isArray(value)) return [...value];
|
|
53
|
-
if (value instanceof Set) return Array.from(value.values());
|
|
54
|
-
if (value && typeof value[Symbol.iterator] === 'function') return Array.from(value);
|
|
55
|
-
return [];
|
|
56
|
-
};
|
|
57
|
-
(listFn as any).__typeName__ = 'list';
|
|
58
|
-
builtins.set('list', listFn);
|
|
59
|
-
const tupleFn = (value: any) => {
|
|
60
|
-
const arr = Array.isArray(value) ? [...value] : value && typeof value[Symbol.iterator] === 'function' ? Array.from(value) : [];
|
|
61
|
-
(arr as any).__tuple__ = true;
|
|
62
|
-
return arr;
|
|
63
|
-
};
|
|
64
|
-
(tupleFn as any).__typeName__ = 'tuple';
|
|
65
|
-
builtins.set('tuple', tupleFn);
|
|
66
|
-
const setFn = (value: any) => {
|
|
67
|
-
if (value instanceof Set) return new Set(value);
|
|
68
|
-
if (Array.isArray(value)) return new Set(value);
|
|
69
|
-
if (value && typeof value[Symbol.iterator] === 'function') return new Set(Array.from(value));
|
|
70
|
-
return new Set();
|
|
71
|
-
};
|
|
72
|
-
(setFn as any).__typeName__ = 'set';
|
|
73
|
-
builtins.set('set', setFn);
|
|
74
|
-
builtins.set('sum', (value: any[]) => {
|
|
75
|
-
if (value.some((v) => typeof v === 'bigint')) {
|
|
76
|
-
return value.reduce((acc, v) => acc + toBigIntValue(v), 0n);
|
|
77
|
-
}
|
|
78
|
-
return value.reduce((acc, v) => acc + v, 0);
|
|
79
|
-
});
|
|
80
|
-
builtins.set('max', (...args: any[]) => {
|
|
81
|
-
const values = args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
|
|
82
|
-
if (values.every((v: any) => isNumericLike(v) && !isFloatLike(v))) {
|
|
83
|
-
return values.reduce((acc: any, v: any) => (toBigIntValue(v) > toBigIntValue(acc) ? v : acc));
|
|
84
|
-
}
|
|
85
|
-
return Math.max(...values.map((v: any) => toNumber(v)));
|
|
86
|
-
});
|
|
87
|
-
builtins.set('min', (...args: any[]) => {
|
|
88
|
-
const values = args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
|
|
89
|
-
if (values.every((v: any) => isNumericLike(v) && !isFloatLike(v))) {
|
|
90
|
-
return values.reduce((acc: any, v: any) => (toBigIntValue(v) < toBigIntValue(acc) ? v : acc));
|
|
91
|
-
}
|
|
92
|
-
return Math.min(...values.map((v: any) => toNumber(v)));
|
|
93
|
-
});
|
|
94
|
-
builtins.set('abs', (value: any) => {
|
|
95
|
-
if (typeof value === 'bigint') return value < 0n ? -value : value;
|
|
96
|
-
return Math.abs(toNumber(value));
|
|
97
|
-
});
|
|
98
|
-
const roundHalfToEven = (input: number) => {
|
|
99
|
-
const floored = Math.floor(input);
|
|
100
|
-
const diff = input - floored;
|
|
101
|
-
const epsilon = 1e-12;
|
|
102
|
-
if (diff > 0.5 + epsilon) return floored + 1;
|
|
103
|
-
if (diff < 0.5 - epsilon) return floored;
|
|
104
|
-
return floored % 2 === 0 ? floored : floored + 1;
|
|
105
|
-
};
|
|
106
|
-
builtins.set('round', (value: number, digits?: number) => {
|
|
107
|
-
if (digits === undefined) return roundHalfToEven(value);
|
|
108
|
-
const factor = Math.pow(10, digits);
|
|
109
|
-
return roundHalfToEven(value * factor) / factor;
|
|
110
|
-
});
|
|
111
|
-
const intFn = (value: any) => {
|
|
112
|
-
if (typeof value === 'bigint') return value;
|
|
113
|
-
const text = typeof value === 'string' ? value.trim() : null;
|
|
114
|
-
if (text && /^[-+]?\\d+$/.test(text)) {
|
|
115
|
-
const big = BigInt(text);
|
|
116
|
-
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
|
117
|
-
const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
|
|
118
|
-
if (big > maxSafe || big < minSafe) {
|
|
119
|
-
return big;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const result = parseInt(value, 10);
|
|
123
|
-
if (Number.isNaN(result)) throw new PyException('ValueError', 'Invalid integer');
|
|
124
|
-
const boxed = new Number(result);
|
|
125
|
-
(boxed as any).__int__ = true;
|
|
126
|
-
return boxed;
|
|
127
|
-
};
|
|
128
|
-
(intFn as any).__typeName__ = 'int';
|
|
129
|
-
builtins.set('int', intFn);
|
|
130
|
-
const floatFn = (value?: any) => {
|
|
131
|
-
if (value === undefined) return new Number(0);
|
|
132
|
-
if (value instanceof Number) return new Number(value.valueOf());
|
|
133
|
-
if (typeof value === 'number') return new Number(value);
|
|
134
|
-
if (typeof value === 'boolean') return new Number(value ? 1 : 0);
|
|
135
|
-
if (typeof value === 'string') {
|
|
136
|
-
const text = value.trim();
|
|
137
|
-
if (text.length === 0) throw new PyException('ValueError', 'Invalid float');
|
|
138
|
-
const lower = text.toLowerCase();
|
|
139
|
-
if (lower === 'nan' || lower === '+nan' || lower === '-nan') return new Number(NaN);
|
|
140
|
-
if (lower === 'inf' || lower === '+inf' || lower === 'infinity' || lower === '+infinity') return new Number(Infinity);
|
|
141
|
-
if (lower === '-inf' || lower === '-infinity') return new Number(-Infinity);
|
|
142
|
-
const result = parseFloat(text);
|
|
143
|
-
if (Number.isNaN(result)) throw new PyException('ValueError', 'Invalid float');
|
|
144
|
-
return new Number(result);
|
|
145
|
-
}
|
|
146
|
-
const result = parseFloat(value);
|
|
147
|
-
if (Number.isNaN(result)) throw new PyException('ValueError', 'Invalid float');
|
|
148
|
-
return new Number(result);
|
|
149
|
-
};
|
|
150
|
-
(floatFn as any).__typeName__ = 'float';
|
|
151
|
-
builtins.set('float', floatFn);
|
|
152
|
-
const strFn = (value: any) => pyStr(value);
|
|
153
|
-
(strFn as any).__typeName__ = 'str';
|
|
154
|
-
builtins.set('str', strFn);
|
|
155
|
-
const boolFn = (value: any) => this.isTruthy(value, scope);
|
|
156
|
-
(boolFn as any).__typeName__ = 'bool';
|
|
157
|
-
builtins.set('bool', boolFn);
|
|
158
|
-
builtins.set('type', (value: any) => ({ __typeName__: pyTypeName(value) }));
|
|
159
|
-
builtins.set('isinstance', (value: any, typeObj: any) => {
|
|
160
|
-
if (typeObj && typeObj.__typeName__) {
|
|
161
|
-
return pyTypeName(value) === typeObj.__typeName__;
|
|
162
|
-
}
|
|
163
|
-
return false;
|
|
164
|
-
});
|
|
165
|
-
builtins.set('enumerate', (iterable: any) => {
|
|
166
|
-
const arr = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
167
|
-
return arr.map((v, i) => {
|
|
168
|
-
const tup = [i, v];
|
|
169
|
-
(tup as any).__tuple__ = true;
|
|
170
|
-
return tup;
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
builtins.set('zip', (...iterables: any[]) => {
|
|
174
|
-
const arrays = iterables.map((it) => (Array.isArray(it) ? it : Array.from(it)));
|
|
175
|
-
const length = Math.min(...arrays.map((a) => a.length));
|
|
176
|
-
const result: any[] = [];
|
|
177
|
-
for (let i = 0; i < length; i++) {
|
|
178
|
-
const tup = arrays.map((a) => a[i]);
|
|
179
|
-
(tup as any).__tuple__ = true;
|
|
180
|
-
result.push(tup);
|
|
181
|
-
}
|
|
182
|
-
return result;
|
|
183
|
-
});
|
|
184
|
-
builtins.set('sorted', (iterable: any) => {
|
|
185
|
-
const arr = Array.isArray(iterable) ? [...iterable] : Array.from(iterable);
|
|
186
|
-
if (arr.every((v) => isNumericLike(v))) {
|
|
187
|
-
return arr.sort((a, b) => toNumber(a) - toNumber(b));
|
|
188
|
-
}
|
|
189
|
-
return arr.sort();
|
|
190
|
-
});
|
|
191
|
-
builtins.set('reversed', (iterable: any) => {
|
|
192
|
-
const arr = Array.isArray(iterable) ? [...iterable] : Array.from(iterable);
|
|
193
|
-
return arr.reverse();
|
|
194
|
-
});
|
|
195
|
-
builtins.set('map', (fn: any, iterable: any) => {
|
|
196
|
-
const arr = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
197
|
-
return arr.map((value) => this.callFunction(fn, [value], scope));
|
|
198
|
-
});
|
|
199
|
-
builtins.set('filter', (fn: any, iterable: any) => {
|
|
200
|
-
const arr = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
201
|
-
return arr.filter((value) => this.isTruthy(this.callFunction(fn, [value], scope), scope));
|
|
202
|
-
});
|
|
203
|
-
builtins.set('next', (iterable: any) => {
|
|
204
|
-
if (iterable instanceof PyGenerator) {
|
|
205
|
-
return iterable.next();
|
|
206
|
-
}
|
|
207
|
-
if (iterable && typeof iterable.next === 'function') {
|
|
208
|
-
const result = iterable.next();
|
|
209
|
-
if (result.done) throw new PyException('StopIteration', 'StopIteration');
|
|
210
|
-
return result.value;
|
|
211
|
-
}
|
|
212
|
-
throw new PyException('TypeError', 'object is not an iterator');
|
|
213
|
-
});
|
|
214
|
-
builtins.set('open', (path: any, mode: any = 'r') => {
|
|
215
|
-
const file = new PyFile(String(path), String(mode));
|
|
216
|
-
try {
|
|
217
|
-
file.open();
|
|
218
|
-
} catch (err) {
|
|
219
|
-
throw new PyException('FileNotFoundError', 'File not found');
|
|
220
|
-
}
|
|
221
|
-
return file;
|
|
222
|
-
});
|
|
223
|
-
const exceptionClass = (name: string, base?: PyClass) => {
|
|
224
|
-
const klass = new PyClass(name, base ? [base] : [], new Map(), true);
|
|
225
|
-
return klass;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const ExceptionBase = exceptionClass('Exception');
|
|
229
|
-
builtins.set('Exception', ExceptionBase);
|
|
230
|
-
builtins.set('AssertionError', exceptionClass('AssertionError', ExceptionBase));
|
|
231
|
-
builtins.set('ZeroDivisionError', exceptionClass('ZeroDivisionError', ExceptionBase));
|
|
232
|
-
builtins.set('ValueError', exceptionClass('ValueError', ExceptionBase));
|
|
233
|
-
builtins.set('TypeError', exceptionClass('TypeError', ExceptionBase));
|
|
234
|
-
builtins.set('FileNotFoundError', exceptionClass('FileNotFoundError', ExceptionBase));
|
|
235
|
-
|
|
236
|
-
scope.values = new Map([...builtins.entries()]);
|
|
237
|
-
}
|
package/src/vm/callable.ts
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import type { VirtualMachine } from './vm';
|
|
2
|
-
import { ASTNodeType } from '../types';
|
|
3
|
-
import { PyClass, PyDict, PyException, PyFunction, PyGenerator, PyInstance, ReturnSignal, Scope } from './runtime-types';
|
|
4
|
-
|
|
5
|
-
export function callFunction(
|
|
6
|
-
this: VirtualMachine,
|
|
7
|
-
func: any,
|
|
8
|
-
args: any[],
|
|
9
|
-
scope: Scope,
|
|
10
|
-
kwargs: Record<string, any> = {}
|
|
11
|
-
): any {
|
|
12
|
-
if (!kwargs) {
|
|
13
|
-
kwargs = {};
|
|
14
|
-
}
|
|
15
|
-
if (func instanceof PyFunction) {
|
|
16
|
-
const callScope = new Scope(func.closure);
|
|
17
|
-
for (const param of func.params) {
|
|
18
|
-
if (param.type === 'Param') {
|
|
19
|
-
let argValue: any;
|
|
20
|
-
if (args.length > 0) {
|
|
21
|
-
argValue = args.shift();
|
|
22
|
-
} else if (param.name in kwargs) {
|
|
23
|
-
argValue = kwargs[param.name];
|
|
24
|
-
delete kwargs[param.name];
|
|
25
|
-
} else if (param.defaultEvaluated !== undefined) {
|
|
26
|
-
argValue = param.defaultEvaluated;
|
|
27
|
-
} else if (param.defaultValue) {
|
|
28
|
-
argValue = this.evaluateExpression(param.defaultValue, scope);
|
|
29
|
-
} else {
|
|
30
|
-
argValue = null;
|
|
31
|
-
}
|
|
32
|
-
callScope.set(param.name, argValue);
|
|
33
|
-
} else if (param.type === 'VarArg') {
|
|
34
|
-
const varArgs = [...args];
|
|
35
|
-
(varArgs as any).__tuple__ = true;
|
|
36
|
-
callScope.set(param.name, varArgs);
|
|
37
|
-
args = [];
|
|
38
|
-
} else if (param.type === 'KwArg') {
|
|
39
|
-
const kwDict = new PyDict();
|
|
40
|
-
for (const [key, value] of Object.entries(kwargs)) {
|
|
41
|
-
kwDict.set(key, value);
|
|
42
|
-
}
|
|
43
|
-
callScope.set(param.name, kwDict);
|
|
44
|
-
kwargs = {};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
if (func.isGenerator) {
|
|
49
|
-
const iterator = this.executeBlockGenerator(func.body, callScope);
|
|
50
|
-
return new PyGenerator(iterator);
|
|
51
|
-
}
|
|
52
|
-
const result = this.executeBlock(func.body, callScope);
|
|
53
|
-
return result;
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (err instanceof ReturnSignal) return err.value;
|
|
56
|
-
throw err;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (func instanceof PyClass) {
|
|
60
|
-
const instance = new PyInstance(func);
|
|
61
|
-
if (func.isException && args.length > 0) {
|
|
62
|
-
instance.attributes.set('message', args[0]);
|
|
63
|
-
}
|
|
64
|
-
const init = this.findClassAttribute(func, '__init__');
|
|
65
|
-
if (init instanceof PyFunction) {
|
|
66
|
-
this.callFunction(init, [instance, ...args], scope, kwargs);
|
|
67
|
-
}
|
|
68
|
-
return instance;
|
|
69
|
-
}
|
|
70
|
-
if (typeof func === 'function') {
|
|
71
|
-
if (Object.keys(kwargs).length > 0) {
|
|
72
|
-
return func(...args, { __kwargs__: kwargs });
|
|
73
|
-
}
|
|
74
|
-
return func(...args);
|
|
75
|
-
}
|
|
76
|
-
throw new PyException('TypeError', 'object is not callable');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function containsYield(this: VirtualMachine, body: any[]): boolean {
|
|
80
|
-
for (const stmt of body) {
|
|
81
|
-
if (stmt.type === ASTNodeType.YIELD) return true;
|
|
82
|
-
if (stmt.expression && this.expressionHasYield(stmt.expression)) return true;
|
|
83
|
-
if (stmt.value && this.expressionHasYield(stmt.value)) return true;
|
|
84
|
-
if (stmt.body && Array.isArray(stmt.body) && this.containsYield(stmt.body)) return true;
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function evaluateComprehension(this: VirtualMachine, node: any, scope: Scope, emit: () => void) {
|
|
90
|
-
const clauses = node.clauses || [];
|
|
91
|
-
const walk = (index: number) => {
|
|
92
|
-
if (index >= clauses.length) {
|
|
93
|
-
emit();
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
const clause = clauses[index];
|
|
97
|
-
const iterable = this.evaluateExpression(clause.iter, scope);
|
|
98
|
-
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
99
|
-
for (const item of items) {
|
|
100
|
-
this.assignTarget(clause.target, item, scope);
|
|
101
|
-
const passes = clause.ifs.every((cond: any) => this.isTruthy(this.evaluateExpression(cond, scope), scope));
|
|
102
|
-
if (passes) {
|
|
103
|
-
walk(index + 1);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
walk(0);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function* generateComprehension(
|
|
111
|
-
this: VirtualMachine,
|
|
112
|
-
node: any,
|
|
113
|
-
scope: Scope,
|
|
114
|
-
valueFactory: () => any
|
|
115
|
-
): Generator<any, any, any> {
|
|
116
|
-
const clauses = node.clauses || [];
|
|
117
|
-
const walk = (index: number): Generator<any, any, any> => {
|
|
118
|
-
const self = this;
|
|
119
|
-
return (function* (): Generator<any, any, any> {
|
|
120
|
-
if (index >= clauses.length) {
|
|
121
|
-
yield valueFactory();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const clause = clauses[index];
|
|
125
|
-
const iterable = self.evaluateExpression(clause.iter, scope);
|
|
126
|
-
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
127
|
-
for (const item of items) {
|
|
128
|
-
self.assignTarget(clause.target, item, scope);
|
|
129
|
-
const passes = clause.ifs.every((cond: any) => self.isTruthy(self.evaluateExpression(cond, scope), scope));
|
|
130
|
-
if (passes) {
|
|
131
|
-
yield* walk(index + 1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
})();
|
|
135
|
-
};
|
|
136
|
-
yield* walk(0);
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function expressionHasYield(this: VirtualMachine, node: any): boolean {
|
|
141
|
-
if (!node) return false;
|
|
142
|
-
if (node.type === ASTNodeType.YIELD) return true;
|
|
143
|
-
for (const key of Object.keys(node)) {
|
|
144
|
-
const value = (node as any)[key];
|
|
145
|
-
if (Array.isArray(value)) {
|
|
146
|
-
if (value.some((item) => item && typeof item === 'object' && this.expressionHasYield(item))) {
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
} else if (value && typeof value === 'object') {
|
|
150
|
-
if (this.expressionHasYield(value)) return true;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
154
|
-
}
|
package/src/vm/execution.ts
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import type { VirtualMachine } from './vm';
|
|
2
|
-
import { ByteCode, ASTNodeType } from '../types';
|
|
3
|
-
import { BreakSignal, ContinueSignal, ReturnSignal, Scope } from './runtime-types';
|
|
4
|
-
import { PyDict } from './runtime-types';
|
|
5
|
-
|
|
6
|
-
export function execute(this: VirtualMachine, bytecode: ByteCode): any {
|
|
7
|
-
if (!bytecode.ast) {
|
|
8
|
-
throw new Error('Bytecode missing AST');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const globalScope = new Scope();
|
|
12
|
-
this.installBuiltins(globalScope);
|
|
13
|
-
return this.executeBlock(bytecode.ast.body, globalScope);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function executeBlock(this: VirtualMachine, body: any[], scope: Scope): any {
|
|
17
|
-
let lastValue: any = null;
|
|
18
|
-
for (const stmt of body) {
|
|
19
|
-
lastValue = this.executeStatement(stmt, scope);
|
|
20
|
-
}
|
|
21
|
-
return lastValue;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function iterableToArray(this: VirtualMachine, iterable: any): any[] {
|
|
25
|
-
if (iterable instanceof PyDict) return Array.from(iterable.keys());
|
|
26
|
-
if (iterable instanceof Set) return Array.from(iterable.values());
|
|
27
|
-
if (Array.isArray(iterable)) return iterable;
|
|
28
|
-
if (iterable && typeof iterable[Symbol.iterator] === 'function') return Array.from(iterable);
|
|
29
|
-
throw new Error('Object is not iterable');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function matchValueEquals(left: any, right: any): boolean {
|
|
33
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
34
|
-
if (left.length !== right.length) return false;
|
|
35
|
-
for (let i = 0; i < left.length; i++) {
|
|
36
|
-
if (!matchValueEquals(left[i], right[i])) return false;
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
if (left instanceof Map && right instanceof Map) {
|
|
41
|
-
if (left.size !== right.size) return false;
|
|
42
|
-
for (const [k, v] of left.entries()) {
|
|
43
|
-
if (!right.has(k) || !matchValueEquals(v, right.get(k))) return false;
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
return left === right;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function matchPattern(this: VirtualMachine, node: any, value: any, scope: Scope): { matched: boolean; bindings: Map<string, any> } {
|
|
51
|
-
switch (node.type) {
|
|
52
|
-
case ASTNodeType.MATCH_PATTERN_VALUE:
|
|
53
|
-
return { matched: matchValueEquals(value, this.evaluateExpression(node.value, scope)), bindings: new Map() };
|
|
54
|
-
case ASTNodeType.MATCH_PATTERN_WILDCARD:
|
|
55
|
-
return { matched: true, bindings: new Map() };
|
|
56
|
-
case ASTNodeType.MATCH_PATTERN_CAPTURE:
|
|
57
|
-
return { matched: true, bindings: new Map([[node.name, value]]) };
|
|
58
|
-
case ASTNodeType.MATCH_PATTERN_OR: {
|
|
59
|
-
for (const pattern of node.patterns || []) {
|
|
60
|
-
const result = this.matchPattern(pattern, value, scope);
|
|
61
|
-
if (result.matched) return result;
|
|
62
|
-
}
|
|
63
|
-
return { matched: false, bindings: new Map() };
|
|
64
|
-
}
|
|
65
|
-
case ASTNodeType.MATCH_PATTERN_SEQUENCE: {
|
|
66
|
-
const elements = node.elements || node.patterns;
|
|
67
|
-
if (!Array.isArray(value) || !elements || elements.length !== value.length) {
|
|
68
|
-
return { matched: false, bindings: new Map() };
|
|
69
|
-
}
|
|
70
|
-
const bindings = new Map<string, any>();
|
|
71
|
-
for (let i = 0; i < elements.length; i++) {
|
|
72
|
-
const result = this.matchPattern(elements[i], value[i], scope);
|
|
73
|
-
if (!result.matched) return { matched: false, bindings: new Map() };
|
|
74
|
-
for (const [k, v] of result.bindings.entries()) bindings.set(k, v);
|
|
75
|
-
}
|
|
76
|
-
return { matched: true, bindings };
|
|
77
|
-
}
|
|
78
|
-
case 'MatchValue':
|
|
79
|
-
return { matched: matchValueEquals(value, node.value), bindings: new Map() };
|
|
80
|
-
case 'MatchSingleton':
|
|
81
|
-
return { matched: value === node.value, bindings: new Map() };
|
|
82
|
-
case 'MatchSequence': {
|
|
83
|
-
if (!Array.isArray(value)) return { matched: false, bindings: new Map() };
|
|
84
|
-
if (!node.patterns || node.patterns.length !== value.length) return { matched: false, bindings: new Map() };
|
|
85
|
-
const bindings = new Map<string, any>();
|
|
86
|
-
for (let i = 0; i < node.patterns.length; i++) {
|
|
87
|
-
const result = this.matchPattern(node.patterns[i], value[i], scope);
|
|
88
|
-
if (!result.matched) return { matched: false, bindings: new Map() };
|
|
89
|
-
for (const [k, v] of result.bindings.entries()) bindings.set(k, v);
|
|
90
|
-
}
|
|
91
|
-
return { matched: true, bindings };
|
|
92
|
-
}
|
|
93
|
-
case 'MatchMapping': {
|
|
94
|
-
if (!(value instanceof PyDict)) return { matched: false, bindings: new Map() };
|
|
95
|
-
const bindings = new Map<string, any>();
|
|
96
|
-
for (const { key, pattern } of node.keys) {
|
|
97
|
-
if (!value.has(key)) return { matched: false, bindings: new Map() };
|
|
98
|
-
const result = this.matchPattern(pattern, value.get(key), scope);
|
|
99
|
-
if (!result.matched) return { matched: false, bindings: new Map() };
|
|
100
|
-
for (const [k, v] of result.bindings.entries()) bindings.set(k, v);
|
|
101
|
-
}
|
|
102
|
-
return { matched: true, bindings };
|
|
103
|
-
}
|
|
104
|
-
case 'MatchAs':
|
|
105
|
-
return {
|
|
106
|
-
matched: true,
|
|
107
|
-
bindings: node.name ? new Map([[node.name, value]]) : new Map(),
|
|
108
|
-
};
|
|
109
|
-
case 'MatchClass': {
|
|
110
|
-
if (!value || !value.klass || value.klass.name !== node.className) return { matched: false, bindings: new Map() };
|
|
111
|
-
const bindings = new Map<string, any>();
|
|
112
|
-
for (let i = 0; i < node.patterns.length; i++) {
|
|
113
|
-
const attrName = node.kwd_attrs[i] || node.patterns[i].name;
|
|
114
|
-
const attrValue = value.attributes.get(attrName);
|
|
115
|
-
const result = this.matchPattern(node.patterns[i], attrValue, scope);
|
|
116
|
-
if (!result.matched) return { matched: false, bindings: new Map() };
|
|
117
|
-
for (const [k, v] of result.bindings.entries()) bindings.set(k, v);
|
|
118
|
-
}
|
|
119
|
-
return { matched: true, bindings };
|
|
120
|
-
}
|
|
121
|
-
default:
|
|
122
|
-
return { matched: false, bindings: new Map() };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function applyBindings(bindings: Map<string, any>, scope: Scope): void {
|
|
127
|
-
for (const [name, value] of bindings.entries()) {
|
|
128
|
-
scope.set(name, value);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function* executeBlockGenerator(this: VirtualMachine, body: any[], scope: Scope): Generator<any, any, any> {
|
|
133
|
-
for (const stmt of body) {
|
|
134
|
-
yield* this.executeStatementGenerator(stmt, scope);
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function* executeStatementGenerator(this: VirtualMachine, node: any, scope: Scope): Generator<any, any, any> {
|
|
140
|
-
switch (node.type) {
|
|
141
|
-
case ASTNodeType.EXPRESSION_STATEMENT: {
|
|
142
|
-
if (this.expressionHasYield(node.expression)) {
|
|
143
|
-
yield* this.evaluateExpressionGenerator(node.expression, scope);
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
this.evaluateExpression(node.expression, scope);
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
case ASTNodeType.ASSIGNMENT: {
|
|
150
|
-
const value = this.expressionHasYield(node.value)
|
|
151
|
-
? yield* this.evaluateExpressionGenerator(node.value, scope)
|
|
152
|
-
: this.evaluateExpression(node.value, scope);
|
|
153
|
-
for (const target of node.targets) {
|
|
154
|
-
this.assignTarget(target, value, scope);
|
|
155
|
-
}
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
case ASTNodeType.IF_STATEMENT: {
|
|
159
|
-
const test = this.expressionHasYield(node.test)
|
|
160
|
-
? yield* this.evaluateExpressionGenerator(node.test, scope)
|
|
161
|
-
: this.evaluateExpression(node.test, scope);
|
|
162
|
-
if (this.isTruthy(test, scope)) {
|
|
163
|
-
yield* this.executeBlockGenerator(node.body, scope);
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
for (const branch of node.elifs) {
|
|
167
|
-
const branchTest = this.expressionHasYield(branch.test)
|
|
168
|
-
? yield* this.evaluateExpressionGenerator(branch.test, scope)
|
|
169
|
-
: this.evaluateExpression(branch.test, scope);
|
|
170
|
-
if (this.isTruthy(branchTest, scope)) {
|
|
171
|
-
yield* this.executeBlockGenerator(branch.body, scope);
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (node.orelse?.length) {
|
|
176
|
-
yield* this.executeBlockGenerator(node.orelse, scope);
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
case ASTNodeType.WHILE_STATEMENT: {
|
|
181
|
-
while (true) {
|
|
182
|
-
const test = this.expressionHasYield(node.test)
|
|
183
|
-
? yield* this.evaluateExpressionGenerator(node.test, scope)
|
|
184
|
-
: this.evaluateExpression(node.test, scope);
|
|
185
|
-
if (!this.isTruthy(test, scope)) break;
|
|
186
|
-
try {
|
|
187
|
-
yield* this.executeBlockGenerator(node.body, scope);
|
|
188
|
-
} catch (err) {
|
|
189
|
-
if (err instanceof BreakSignal) break;
|
|
190
|
-
if (err instanceof ContinueSignal) continue;
|
|
191
|
-
throw err;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
case ASTNodeType.FOR_STATEMENT: {
|
|
197
|
-
const iterable = this.expressionHasYield(node.iter)
|
|
198
|
-
? yield* this.evaluateExpressionGenerator(node.iter, scope)
|
|
199
|
-
: this.evaluateExpression(node.iter, scope);
|
|
200
|
-
const items = this.iterableToArray(iterable);
|
|
201
|
-
for (const item of items) {
|
|
202
|
-
this.assignTarget(node.target, item, scope);
|
|
203
|
-
try {
|
|
204
|
-
yield* this.executeBlockGenerator(node.body, scope);
|
|
205
|
-
} catch (err) {
|
|
206
|
-
if (err instanceof BreakSignal) break;
|
|
207
|
-
if (err instanceof ContinueSignal) continue;
|
|
208
|
-
throw err;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
case ASTNodeType.MATCH_STATEMENT: {
|
|
214
|
-
const subject = this.expressionHasYield(node.subject)
|
|
215
|
-
? yield* this.evaluateExpressionGenerator(node.subject, scope)
|
|
216
|
-
: this.evaluateExpression(node.subject, scope);
|
|
217
|
-
for (const matchCase of node.cases) {
|
|
218
|
-
const result = this.matchPattern(matchCase.pattern, subject, scope);
|
|
219
|
-
if (!result.matched) continue;
|
|
220
|
-
if (matchCase.guard) {
|
|
221
|
-
const guardScope = new Scope(scope);
|
|
222
|
-
this.applyBindings(result.bindings, guardScope);
|
|
223
|
-
const guardValue = this.expressionHasYield(matchCase.guard)
|
|
224
|
-
? yield* this.evaluateExpressionGenerator(matchCase.guard, guardScope)
|
|
225
|
-
: this.evaluateExpression(matchCase.guard, guardScope);
|
|
226
|
-
if (!this.isTruthy(guardValue, scope)) continue;
|
|
227
|
-
}
|
|
228
|
-
this.applyBindings(result.bindings, scope);
|
|
229
|
-
yield* this.executeBlockGenerator(matchCase.body, scope);
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
case ASTNodeType.RETURN_STATEMENT: {
|
|
235
|
-
const value = node.value
|
|
236
|
-
? (this.expressionHasYield(node.value)
|
|
237
|
-
? yield* this.evaluateExpressionGenerator(node.value, scope)
|
|
238
|
-
: this.evaluateExpression(node.value, scope))
|
|
239
|
-
: null;
|
|
240
|
-
throw new ReturnSignal(value);
|
|
241
|
-
}
|
|
242
|
-
case ASTNodeType.BREAK_STATEMENT:
|
|
243
|
-
throw new BreakSignal();
|
|
244
|
-
case ASTNodeType.CONTINUE_STATEMENT:
|
|
245
|
-
throw new ContinueSignal();
|
|
246
|
-
case ASTNodeType.PASS_STATEMENT:
|
|
247
|
-
return null;
|
|
248
|
-
default:
|
|
249
|
-
return this.executeStatement(node, scope);
|
|
250
|
-
}
|
|
251
|
-
}
|