@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.
Files changed (144) hide show
  1. package/.claude/settings.local.json +3 -0
  2. package/.prettierrc +7 -0
  3. package/Agents.md +66 -0
  4. package/README.md +93 -0
  5. package/README_zh-CN.md +93 -0
  6. package/SETUP.md +171 -0
  7. package/dist/compiler.d.ts +20 -0
  8. package/dist/compiler.js +91 -0
  9. package/dist/compiler_module/compiler.d.ts +8 -0
  10. package/dist/compiler_module/compiler.js +22 -0
  11. package/dist/compiler_module/index.d.ts +2 -0
  12. package/dist/compiler_module/index.js +6 -0
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +67 -0
  15. package/dist/lexer/index.d.ts +2 -0
  16. package/dist/lexer/index.js +6 -0
  17. package/dist/lexer/lexer.d.ts +16 -0
  18. package/dist/lexer/lexer.js +403 -0
  19. package/dist/parser/expressions.d.ts +30 -0
  20. package/dist/parser/expressions.js +483 -0
  21. package/dist/parser/index.d.ts +2 -0
  22. package/dist/parser/index.js +6 -0
  23. package/dist/parser/parser.d.ts +63 -0
  24. package/dist/parser/parser.js +129 -0
  25. package/dist/parser/statements.d.ts +20 -0
  26. package/dist/parser/statements.js +388 -0
  27. package/dist/parser/targets.d.ts +6 -0
  28. package/dist/parser/targets.js +75 -0
  29. package/dist/types/ast.d.ts +63 -0
  30. package/dist/types/ast.js +60 -0
  31. package/dist/types/bytecode.d.ts +38 -0
  32. package/dist/types/bytecode.js +35 -0
  33. package/dist/types/index.d.ts +4 -0
  34. package/dist/types/index.js +20 -0
  35. package/dist/types/token.d.ts +34 -0
  36. package/dist/types/token.js +39 -0
  37. package/dist/vm/builtins.d.ts +4 -0
  38. package/dist/vm/builtins.js +269 -0
  39. package/dist/vm/callable.d.ts +8 -0
  40. package/dist/vm/callable.js +161 -0
  41. package/dist/vm/execution.d.ts +15 -0
  42. package/dist/vm/execution.js +283 -0
  43. package/dist/vm/expression-generator.d.ts +3 -0
  44. package/dist/vm/expression-generator.js +70 -0
  45. package/dist/vm/expressions.d.ts +13 -0
  46. package/dist/vm/expressions.js +390 -0
  47. package/dist/vm/imports.d.ts +7 -0
  48. package/dist/vm/imports.js +99 -0
  49. package/dist/vm/index.d.ts +3 -0
  50. package/dist/vm/index.js +21 -0
  51. package/dist/vm/operations.d.ts +16 -0
  52. package/dist/vm/operations.js +439 -0
  53. package/dist/vm/runtime-types.d.ts +84 -0
  54. package/dist/vm/runtime-types.js +290 -0
  55. package/dist/vm/statements.d.ts +7 -0
  56. package/dist/vm/statements.js +381 -0
  57. package/dist/vm/truthy.d.ts +4 -0
  58. package/dist/vm/truthy.js +47 -0
  59. package/dist/vm/value-utils.d.ts +28 -0
  60. package/dist/vm/value-utils.js +225 -0
  61. package/dist/vm/vm.d.ts +56 -0
  62. package/dist/vm/vm.js +75 -0
  63. package/examples/assert_testing.py +38 -0
  64. package/examples/big_int_precision.py +2 -0
  65. package/examples/boolean_logic.py +35 -0
  66. package/examples/break_continue.py +43 -0
  67. package/examples/classes_objects.py +43 -0
  68. package/examples/compiler_killer_async.py +6 -0
  69. package/examples/compiler_killer_bigint.py +3 -0
  70. package/examples/compiler_killer_bool_int_dict_key.py +5 -0
  71. package/examples/compiler_killer_bool_len.py +9 -0
  72. package/examples/compiler_killer_floor_division.py +4 -0
  73. package/examples/compiler_killer_is_identity.py +3 -0
  74. package/examples/compiler_killer_list_sort_return.py +3 -0
  75. package/examples/compiler_killer_match.py +13 -0
  76. package/examples/compiler_killer_negative_repeat.py +3 -0
  77. package/examples/compiler_killer_negative_zero_repr.py +3 -0
  78. package/examples/compiler_killer_rounding.py +4 -0
  79. package/examples/compiler_killer_slice_assign.py +3 -0
  80. package/examples/comprehensions.py +28 -0
  81. package/examples/conditions.py +13 -0
  82. package/examples/context_manager.py +35 -0
  83. package/examples/decorators.py +50 -0
  84. package/examples/exceptions.py +40 -0
  85. package/examples/fibonacci.py +10 -0
  86. package/examples/functions.py +38 -0
  87. package/examples/generator.py +51 -0
  88. package/examples/global_nonlocal.py +48 -0
  89. package/examples/hello.py +3 -0
  90. package/examples/itertools_example.py +33 -0
  91. package/examples/lists_dicts.py +29 -0
  92. package/examples/loops.py +19 -0
  93. package/examples/math_ops.py +15 -0
  94. package/examples/nan_set.py +6 -0
  95. package/examples/numbers_operators.py +51 -0
  96. package/examples/sets.py +36 -0
  97. package/examples/slicing.py +29 -0
  98. package/examples/starred_unpacking.py +3 -0
  99. package/examples/string_formatting.py +36 -0
  100. package/examples/strings.py +22 -0
  101. package/examples/tuples.py +45 -0
  102. package/examples/type_conversion.py +41 -0
  103. package/jest.config.js +15 -0
  104. package/notes/iterations/compiler-runtime/compiler-runtime_2025-09-16.md +25 -0
  105. package/notes/iterations/compiler-runtime/compiler-runtime_2026-01-16.md +24 -0
  106. package/notes/iterations/compiler-runtime/compiler-runtime_test_2026-01-16.md +21 -0
  107. package/notes/iterations/floor-division/floor-division_2026-01-16.md +29 -0
  108. package/package.json +36 -0
  109. package/prompts/commit.txt +9 -0
  110. package/prompts/task.txt +21 -0
  111. package/prompts/test.txt +23 -0
  112. package/scripts/codex-loop.js +215 -0
  113. package/scripts/verify.sh +12 -0
  114. package/src/compiler.ts +58 -0
  115. package/src/compiler_module/compiler.ts +19 -0
  116. package/src/compiler_module/index.ts +1 -0
  117. package/src/index.ts +39 -0
  118. package/src/lexer/index.ts +1 -0
  119. package/src/lexer/lexer.ts +402 -0
  120. package/src/parser/expressions.ts +462 -0
  121. package/src/parser/index.ts +1 -0
  122. package/src/parser/parser.ts +102 -0
  123. package/src/parser/statements.ts +366 -0
  124. package/src/parser/targets.ts +71 -0
  125. package/src/types/ast.ts +64 -0
  126. package/src/types/bytecode.ts +50 -0
  127. package/src/types/index.ts +3 -0
  128. package/src/types/token.ts +44 -0
  129. package/src/vm/builtins.ts +237 -0
  130. package/src/vm/callable.ts +154 -0
  131. package/src/vm/execution.ts +251 -0
  132. package/src/vm/expression-generator.ts +65 -0
  133. package/src/vm/expressions.ts +373 -0
  134. package/src/vm/imports.ts +61 -0
  135. package/src/vm/index.ts +2 -0
  136. package/src/vm/operations.ts +414 -0
  137. package/src/vm/runtime-types.ts +292 -0
  138. package/src/vm/statements.ts +358 -0
  139. package/src/vm/truthy.ts +36 -0
  140. package/src/vm/value-utils.ts +173 -0
  141. package/src/vm/vm.ts +80 -0
  142. package/tests/compiler.test.ts +111 -0
  143. package/tsconfig.json +20 -0
  144. package/vitest.config.ts +16 -0
@@ -0,0 +1,403 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Lexer = void 0;
4
+ const types_1 = require("../types");
5
+ /**
6
+ * 词法分析器 - 将源代码转换为 token 流
7
+ */
8
+ class Lexer {
9
+ constructor(code) {
10
+ this.pos = 0;
11
+ this.line = 1;
12
+ this.column = 1;
13
+ this.tokens = [];
14
+ this.indentStack = [0];
15
+ this.atLineStart = true;
16
+ this.code = code;
17
+ }
18
+ tokenize() {
19
+ // Helper function to create a token
20
+ const createToken = (type, value) => ({
21
+ type,
22
+ value,
23
+ line: this.line,
24
+ column: this.column - value.length
25
+ });
26
+ // Helper function to advance position
27
+ const advance = (n = 1) => {
28
+ for (let i = 0; i < n; i++) {
29
+ if (this.code[this.pos] === '\n') {
30
+ this.line++;
31
+ this.column = 1;
32
+ }
33
+ else {
34
+ this.column++;
35
+ }
36
+ this.pos++;
37
+ }
38
+ };
39
+ // Helper function to peek ahead
40
+ const peek = (n = 0) => this.code[this.pos + n] || '';
41
+ const emitIndentTokens = (indent) => {
42
+ const currentIndent = this.indentStack[this.indentStack.length - 1];
43
+ if (indent > currentIndent) {
44
+ this.tokens.push(createToken(types_1.TokenType.INDENT, ''));
45
+ this.indentStack.push(indent);
46
+ }
47
+ else if (indent < currentIndent) {
48
+ while (indent < this.indentStack[this.indentStack.length - 1]) {
49
+ this.tokens.push(createToken(types_1.TokenType.DEDENT, ''));
50
+ this.indentStack.pop();
51
+ }
52
+ if (indent !== this.indentStack[this.indentStack.length - 1]) {
53
+ throw new Error(`Indentation error at line ${this.line}`);
54
+ }
55
+ }
56
+ };
57
+ // Main tokenization loop
58
+ while (this.pos < this.code.length) {
59
+ if (this.atLineStart) {
60
+ let indent = 0;
61
+ while (peek() === ' ' || peek() === '\t') {
62
+ indent += peek() === '\t' ? 4 : 1;
63
+ advance();
64
+ }
65
+ if (peek() === '\n') {
66
+ this.tokens.push(createToken(types_1.TokenType.NEWLINE, '\n'));
67
+ advance();
68
+ this.atLineStart = true;
69
+ continue;
70
+ }
71
+ if (peek() === '#') {
72
+ while (this.pos < this.code.length && peek() !== '\n') {
73
+ advance();
74
+ }
75
+ continue;
76
+ }
77
+ emitIndentTokens(indent);
78
+ this.atLineStart = false;
79
+ }
80
+ if (this.pos >= this.code.length) {
81
+ break;
82
+ }
83
+ const char = peek();
84
+ if (char === ' ' || char === '\t') {
85
+ advance();
86
+ continue;
87
+ }
88
+ if (char === '#') {
89
+ while (this.pos < this.code.length && peek() !== '\n') {
90
+ advance();
91
+ }
92
+ continue;
93
+ }
94
+ if (char === '\n') {
95
+ this.tokens.push(createToken(types_1.TokenType.NEWLINE, '\n'));
96
+ advance();
97
+ this.atLineStart = true;
98
+ continue;
99
+ }
100
+ // Numbers
101
+ if (/[0-9]/.test(char) || (char === '.' && /[0-9]/.test(peek(1)))) {
102
+ let num = '';
103
+ let hasDot = false;
104
+ if (char === '.') {
105
+ hasDot = true;
106
+ num += '.';
107
+ advance();
108
+ }
109
+ while (this.pos < this.code.length && /[0-9]/.test(peek())) {
110
+ num += peek();
111
+ advance();
112
+ }
113
+ if (peek() === '.' && !hasDot) {
114
+ hasDot = true;
115
+ num += '.';
116
+ advance();
117
+ while (this.pos < this.code.length && /[0-9]/.test(peek())) {
118
+ num += peek();
119
+ advance();
120
+ }
121
+ }
122
+ if (peek() === 'j' || peek() === 'J') {
123
+ num += peek();
124
+ advance();
125
+ }
126
+ this.tokens.push(createToken(types_1.TokenType.NUMBER, num));
127
+ continue;
128
+ }
129
+ // Strings
130
+ if (char === '"' || char === "'" || ((char === 'f' || char === 'F') && (peek(1) === '"' || peek(1) === "'"))) {
131
+ let prefix = '';
132
+ let quote = char;
133
+ if (char === 'f' || char === 'F') {
134
+ prefix = char;
135
+ quote = peek(1);
136
+ advance();
137
+ }
138
+ let str = prefix + quote;
139
+ advance();
140
+ const isTriple = peek() === quote && peek(1) === quote;
141
+ if (isTriple) {
142
+ str += quote + quote;
143
+ advance(2);
144
+ }
145
+ while (this.pos < this.code.length) {
146
+ if (!isTriple && peek() === quote) {
147
+ break;
148
+ }
149
+ if (isTriple && peek() === quote && peek(1) === quote && peek(2) === quote) {
150
+ break;
151
+ }
152
+ if (peek() === '\\') {
153
+ str += peek();
154
+ advance();
155
+ if (this.pos < this.code.length) {
156
+ str += peek();
157
+ advance();
158
+ }
159
+ }
160
+ else {
161
+ str += peek();
162
+ advance();
163
+ }
164
+ }
165
+ if (!isTriple && peek() === quote) {
166
+ str += quote;
167
+ advance();
168
+ }
169
+ else if (isTriple && peek() === quote && peek(1) === quote && peek(2) === quote) {
170
+ str += quote + quote + quote;
171
+ advance(3);
172
+ }
173
+ else {
174
+ throw new Error(`Unterminated string at line ${this.line}`);
175
+ }
176
+ this.tokens.push(createToken(types_1.TokenType.STRING, str));
177
+ continue;
178
+ }
179
+ // Identifiers and keywords
180
+ if (/[a-zA-Z_]/.test(char)) {
181
+ let ident = '';
182
+ while (this.pos < this.code.length && /[a-zA-Z0-9_]/.test(peek())) {
183
+ ident += peek();
184
+ advance();
185
+ }
186
+ // Check for keywords and boolean literals
187
+ if (ident === 'def' || ident === 'class' || ident === 'if' || ident === 'elif' || ident === 'else' ||
188
+ ident === 'for' || ident === 'while' || ident === 'return' || ident === 'break' || ident === 'continue' ||
189
+ ident === 'pass' || ident === 'in' || ident === 'is' || ident === 'and' || ident === 'or' || ident === 'not' ||
190
+ ident === 'lambda' || ident === 'yield' || ident === 'try' || ident === 'except' || ident === 'finally' ||
191
+ ident === 'with' || ident === 'as' || ident === 'global' || ident === 'nonlocal' || ident === 'assert' ||
192
+ ident === 'raise' || ident === 'del' || ident === 'match' || ident === 'case' || ident === 'import' ||
193
+ ident === 'async') {
194
+ this.tokens.push(createToken(types_1.TokenType.KEYWORD, ident));
195
+ }
196
+ else if (ident === 'True' || ident === 'False') {
197
+ this.tokens.push(createToken(types_1.TokenType.BOOLEAN, ident));
198
+ }
199
+ else if (ident === 'None') {
200
+ this.tokens.push(createToken(types_1.TokenType.NONE, ident));
201
+ }
202
+ else {
203
+ this.tokens.push(createToken(types_1.TokenType.IDENTIFIER, ident));
204
+ }
205
+ continue;
206
+ }
207
+ // Operators and delimiters
208
+ switch (char) {
209
+ case '+':
210
+ if (peek(1) === '=') {
211
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '+='));
212
+ advance(2);
213
+ }
214
+ else {
215
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '+'));
216
+ advance();
217
+ }
218
+ break;
219
+ case '-':
220
+ if (peek(1) === '=') {
221
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '-='));
222
+ advance(2);
223
+ }
224
+ else {
225
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '-'));
226
+ advance();
227
+ }
228
+ break;
229
+ case '*':
230
+ if (peek(1) === '*') {
231
+ if (peek(2) === '=') {
232
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '**='));
233
+ advance(3);
234
+ }
235
+ else {
236
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '**'));
237
+ advance(2);
238
+ }
239
+ }
240
+ else {
241
+ if (peek(1) === '=') {
242
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '*='));
243
+ advance(2);
244
+ }
245
+ else {
246
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '*'));
247
+ advance();
248
+ }
249
+ }
250
+ break;
251
+ case '/':
252
+ if (peek(1) === '/') {
253
+ if (peek(2) === '=') {
254
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '//='));
255
+ advance(3);
256
+ }
257
+ else {
258
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '//'));
259
+ advance(2);
260
+ }
261
+ }
262
+ else if (peek(1) === '=') {
263
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '/='));
264
+ advance(2);
265
+ }
266
+ else {
267
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '/'));
268
+ advance();
269
+ }
270
+ break;
271
+ case '%':
272
+ if (peek(1) === '=') {
273
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '%='));
274
+ advance(2);
275
+ }
276
+ else {
277
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '%'));
278
+ advance();
279
+ }
280
+ break;
281
+ case '=':
282
+ if (peek(1) === '=') {
283
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '=='));
284
+ advance(2);
285
+ }
286
+ else {
287
+ this.tokens.push(createToken(types_1.TokenType.ASSIGN, '='));
288
+ advance();
289
+ }
290
+ break;
291
+ case '!':
292
+ if (peek(1) === '=') {
293
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '!='));
294
+ advance(2);
295
+ }
296
+ else {
297
+ throw new Error(`Unexpected character '!' at line ${this.line}`);
298
+ }
299
+ break;
300
+ case '&':
301
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '&'));
302
+ advance();
303
+ break;
304
+ case '|':
305
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '|'));
306
+ advance();
307
+ break;
308
+ case '^':
309
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '^'));
310
+ advance();
311
+ break;
312
+ case '~':
313
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '~'));
314
+ advance();
315
+ break;
316
+ case '<':
317
+ if (peek(1) === '<') {
318
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '<<'));
319
+ advance(2);
320
+ }
321
+ else if (peek(1) === '=') {
322
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '<='));
323
+ advance(2);
324
+ }
325
+ else {
326
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '<'));
327
+ advance();
328
+ }
329
+ break;
330
+ case '>':
331
+ if (peek(1) === '>') {
332
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '>>'));
333
+ advance(2);
334
+ }
335
+ else if (peek(1) === '=') {
336
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '>='));
337
+ advance(2);
338
+ }
339
+ else {
340
+ this.tokens.push(createToken(types_1.TokenType.OPERATOR, '>'));
341
+ advance();
342
+ }
343
+ break;
344
+ case '(':
345
+ this.tokens.push(createToken(types_1.TokenType.LPAREN, '('));
346
+ advance();
347
+ break;
348
+ case ')':
349
+ this.tokens.push(createToken(types_1.TokenType.RPAREN, ')'));
350
+ advance();
351
+ break;
352
+ case '[':
353
+ this.tokens.push(createToken(types_1.TokenType.LBRACKET, '['));
354
+ advance();
355
+ break;
356
+ case ']':
357
+ this.tokens.push(createToken(types_1.TokenType.RBRACKET, ']'));
358
+ advance();
359
+ break;
360
+ case '{':
361
+ this.tokens.push(createToken(types_1.TokenType.LBRACE, '{'));
362
+ advance();
363
+ break;
364
+ case '}':
365
+ this.tokens.push(createToken(types_1.TokenType.RBRACE, '}'));
366
+ advance();
367
+ break;
368
+ case ':':
369
+ this.tokens.push(createToken(types_1.TokenType.COLON, ':'));
370
+ advance();
371
+ break;
372
+ case ',':
373
+ this.tokens.push(createToken(types_1.TokenType.COMMA, ','));
374
+ advance();
375
+ break;
376
+ case '.':
377
+ this.tokens.push(createToken(types_1.TokenType.DOT, '.'));
378
+ advance();
379
+ break;
380
+ case '@':
381
+ this.tokens.push(createToken(types_1.TokenType.AT, '@'));
382
+ advance();
383
+ break;
384
+ case ' ':
385
+ case '\t':
386
+ // Should be handled by skipWhitespace
387
+ advance();
388
+ break;
389
+ default:
390
+ throw new Error(`Unexpected character '${char}' at line ${this.line}, column ${this.column}`);
391
+ }
392
+ }
393
+ while (this.indentStack.length > 1) {
394
+ this.tokens.push(createToken(types_1.TokenType.DEDENT, ''));
395
+ this.indentStack.pop();
396
+ }
397
+ // Add EOF
398
+ this.tokens.push(createToken(types_1.TokenType.EOF, ''));
399
+ return this.tokens;
400
+ }
401
+ }
402
+ exports.Lexer = Lexer;
403
+ //# sourceMappingURL=lexer.js.map
@@ -0,0 +1,30 @@
1
+ import type { Parser } from './parser';
2
+ import { ASTNode } from '../types';
3
+ export declare function parseStringLiteral(this: Parser): ASTNode;
4
+ export declare function parseLiteral(this: Parser): ASTNode;
5
+ export declare function parseIdentifier(this: Parser): ASTNode;
6
+ export declare function parseArguments(this: Parser): ASTNode[];
7
+ export declare function parseSlice(this: Parser): ASTNode;
8
+ export declare function parsePatternAtom(this: Parser): ASTNode;
9
+ export declare function parsePattern(this: Parser): ASTNode;
10
+ export declare function parseLambdaParameters(this: Parser): string[];
11
+ export declare function parseLambda(this: Parser): ASTNode;
12
+ export declare function parseAtom(this: Parser): ASTNode;
13
+ export declare function parsePostfix(this: Parser): ASTNode;
14
+ export declare function parseUnary(this: Parser): ASTNode;
15
+ export declare function parsePower(this: Parser): ASTNode;
16
+ export declare function parseFactor(this: Parser): ASTNode;
17
+ export declare function parseTerm(this: Parser): ASTNode;
18
+ export declare function parseShift(this: Parser): ASTNode;
19
+ export declare function parseBitAnd(this: Parser): ASTNode;
20
+ export declare function parseBitXor(this: Parser): ASTNode;
21
+ export declare function parseBitOr(this: Parser): ASTNode;
22
+ export declare function parseComparison(this: Parser): ASTNode;
23
+ export declare function parseNot(this: Parser): ASTNode;
24
+ export declare function parseAnd(this: Parser): ASTNode;
25
+ export declare function parseOr(this: Parser): ASTNode;
26
+ export declare function parseIfExpression(this: Parser): ASTNode;
27
+ export declare function parseExpression(this: Parser): ASTNode;
28
+ export declare function parseExpressionNoIf(this: Parser): ASTNode;
29
+ export declare function parseComprehension(this: Parser, expression: ASTNode): ASTNode;
30
+ //# sourceMappingURL=expressions.d.ts.map