vladx 1.0.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 (42) hide show
  1. package/README.md +256 -0
  2. package/bin/cli.js +486 -0
  3. package/bin/vlad.js +539 -0
  4. package/bin/vladpm.js +710 -0
  5. package/bin/vladx.js +491 -0
  6. package/package.json +57 -0
  7. package/src/engine/jit-compiler.js +285 -0
  8. package/src/engine/vladx-engine.js +941 -0
  9. package/src/index.js +44 -0
  10. package/src/interpreter/interpreter.js +2114 -0
  11. package/src/lexer/lexer.js +658 -0
  12. package/src/lexer/optimized-lexer.js +106 -0
  13. package/src/lexer/regex-cache.js +83 -0
  14. package/src/parser/ast-nodes.js +472 -0
  15. package/src/parser/parser.js +1408 -0
  16. package/src/runtime/advanced-type-system.js +209 -0
  17. package/src/runtime/async-manager.js +252 -0
  18. package/src/runtime/builtins.js +143 -0
  19. package/src/runtime/bundler.js +422 -0
  20. package/src/runtime/cache-manager.js +126 -0
  21. package/src/runtime/data-structures.js +612 -0
  22. package/src/runtime/debugger.js +260 -0
  23. package/src/runtime/enhanced-module-system.js +196 -0
  24. package/src/runtime/environment-enhanced.js +272 -0
  25. package/src/runtime/environment.js +140 -0
  26. package/src/runtime/event-emitter.js +232 -0
  27. package/src/runtime/formatter.js +280 -0
  28. package/src/runtime/functional.js +359 -0
  29. package/src/runtime/io-operations.js +390 -0
  30. package/src/runtime/linter.js +374 -0
  31. package/src/runtime/logging.js +314 -0
  32. package/src/runtime/minifier.js +242 -0
  33. package/src/runtime/module-system.js +377 -0
  34. package/src/runtime/network-operations.js +373 -0
  35. package/src/runtime/profiler.js +295 -0
  36. package/src/runtime/repl.js +336 -0
  37. package/src/runtime/security-manager.js +244 -0
  38. package/src/runtime/source-map-generator.js +208 -0
  39. package/src/runtime/test-runner.js +394 -0
  40. package/src/runtime/transformer.js +277 -0
  41. package/src/runtime/type-system.js +244 -0
  42. package/src/runtime/vladx-object.js +250 -0
@@ -0,0 +1,2114 @@
1
+ /**
2
+ * VladX Interpreter — Интерпретатор
3
+ * Выполняет AST и управляет выполнением программы
4
+ */
5
+
6
+ import { VladXObject, types } from '../runtime/vladx-object.js';
7
+ import { Environment } from '../runtime/environment.js';
8
+ import { AsyncManager } from '../runtime/async-manager.js';
9
+ import { Functional } from '../runtime/functional.js';
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+
16
+ export class Interpreter {
17
+ constructor(options = {}) {
18
+ this.debug = options.debug || false;
19
+ this.strictMode = options.strictMode || false;
20
+ this.maxExecutionTime = options.maxExecutionTime || 30000;
21
+ this.executionTimer = null;
22
+ this.moduleSystem = options.moduleSystem || null;
23
+ this.debugger = options.debugger || null;
24
+ this.profiler = options.profiler || null;
25
+
26
+ this.globalEnv = new Environment(null, '<global>');
27
+ this.currentEnv = this.globalEnv;
28
+
29
+ this.isReturn = false;
30
+ this.returnValue = null;
31
+ this.isBreak = false;
32
+ this.isContinue = false;
33
+
34
+ this.builtins = new Map();
35
+
36
+ this.asyncManager = new AsyncManager(options.async);
37
+ this.currentFilename = '<unknown>';
38
+ this.currentLine = 0;
39
+
40
+ this.registerBuiltins();
41
+ }
42
+
43
+ /**
44
+ * Интерпретация программы
45
+ */
46
+ async interpret(ast, options = {}) {
47
+ const filename = options.filename || '<unknown>';
48
+ this.currentFilename = filename;
49
+
50
+ if (this.maxExecutionTime > 0 && !this.executionTimer) {
51
+ this.executionTimer = setTimeout(() => {
52
+ this.stopExecution();
53
+ throw new Error(`Превышено максимальное время выполнения (${this.maxExecutionTime}мс)`);
54
+ }, this.maxExecutionTime);
55
+ }
56
+
57
+ if (options.environment) {
58
+ this.currentEnv = options.environment;
59
+ } else {
60
+ this.currentEnv = this.globalEnv;
61
+ }
62
+
63
+ let result = VladXObject.null();
64
+
65
+ try {
66
+ // Start profiler if enabled
67
+ if (this.profiler && !this.profiler.startTime) {
68
+ this.profiler.start();
69
+ }
70
+
71
+ for (const statement of ast.body) {
72
+ // Debugger check
73
+ if (this.debugger) {
74
+ this.currentLine = statement.line || 0;
75
+ if (this.debugger.checkBreak(filename, statement.line || 0)) {
76
+ this.debugger.paused = true;
77
+ await new Promise(resolve => setTimeout(resolve, 100));
78
+ }
79
+ }
80
+
81
+ result = await this.evaluateStatement(statement);
82
+
83
+ if (this.isReturn) {
84
+ return this.returnValue;
85
+ }
86
+ }
87
+ } catch (error) {
88
+ throw error;
89
+ }
90
+
91
+ return result;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Остановка выполнения
97
+ */
98
+ stopExecution() {
99
+ if (this.executionTimer) {
100
+ clearTimeout(this.executionTimer);
101
+ this.executionTimer = null;
102
+ }
103
+
104
+ if (this.asyncManager) {
105
+ this.asyncManager.clear();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Вызов функции
111
+ */
112
+ async evaluateCallExpression(expr) {
113
+ const callee = await this.evaluateExpression(expr.callee);
114
+ const args = [];
115
+
116
+ for (const arg of expr.args) {
117
+ if (arg && arg.type === 'SpreadElement') {
118
+ const spreadValue = await this.evaluateExpression(arg.argument);
119
+ const spreadArray = spreadValue && spreadValue.value ? spreadValue.value : [];
120
+
121
+ if (Array.isArray(spreadArray)) {
122
+ args.push(...spreadArray);
123
+ } else {
124
+ args.push(spreadValue);
125
+ }
126
+ } else {
127
+ args.push(await this.evaluateExpression(arg));
128
+ }
129
+ }
130
+
131
+ const convertToNative = (val) => {
132
+ if (val && typeof val === 'object' && val.type !== undefined) {
133
+ if (val.type === 'function' || val.type === 'closure') {
134
+ return val;
135
+ }
136
+
137
+ if (val.type === 'null') {
138
+ return null;
139
+ }
140
+
141
+ if (val.value !== undefined) {
142
+ const rawValue = val.value;
143
+
144
+ if (Array.isArray(rawValue)) {
145
+ return rawValue.map(item => convertToNative(item));
146
+ } else if (rawValue && typeof rawValue === 'object' && rawValue.constructor === Object) {
147
+ const converted = {};
148
+ for (const key in rawValue) {
149
+ if (rawValue.hasOwnProperty(key)) {
150
+ converted[key] = convertToNative(rawValue[key]);
151
+ }
152
+ }
153
+ return converted;
154
+ } else {
155
+ return rawValue;
156
+ }
157
+ }
158
+ }
159
+ return val;
160
+ };
161
+
162
+ const nativeArgs = args.map(convertToNative);
163
+
164
+ if (callee && (callee.type === 'function' || callee.isNativeFunction?.())) {
165
+ // Native function
166
+ if (this.debugger) {
167
+ this.debugger.pushFrame({
168
+ filename: this.currentFilename,
169
+ line: expr.line || 0,
170
+ functionName: callee.name || '<anonymous>',
171
+ environment: this.currentEnv
172
+ });
173
+ }
174
+
175
+ try {
176
+ let result = callee.value(...nativeArgs);
177
+ if (result && typeof result.then === 'function') {
178
+ result = await result;
179
+ }
180
+
181
+ if (this.debugger) {
182
+ this.debugger.popFrame();
183
+ }
184
+
185
+ return VladXObject.fromJS(result);
186
+ } catch (error) {
187
+ if (this.debugger) {
188
+ this.debugger.popFrame();
189
+ }
190
+ throw error;
191
+ }
192
+ } else if (callee && callee.type === 'closure') {
193
+ // Closure
194
+ if (this.profiler && callee.name) {
195
+ this.profiler.enterFunction(callee.name, callee.ast?.body?.[0]?.filename || this.currentFilename);
196
+ }
197
+
198
+ const closureEnv = this.currentEnv;
199
+
200
+ const closure = async (...args) => {
201
+ const env = this.currentEnv;
202
+
203
+ try {
204
+ this.currentEnv = closureEnv;
205
+
206
+ for (let i = 0; i < (callee.params?.length || 0); i++) {
207
+ const paramName = callee.params[i]?.name || `arg${i}`;
208
+ this.currentEnv.define(paramName, VladXObject.fromJS(args[i]));
209
+ }
210
+
211
+ let result = VladXObject.null();
212
+
213
+ const body = callee.thenBranch?.body || callee.body || [];
214
+
215
+ for (const stmt of body) {
216
+ // Debugger check
217
+ if (this.debugger) {
218
+ this.currentLine = stmt.line || 0;
219
+ if (this.debugger.checkBreak(this.currentFilename, stmt.line || 0)) {
220
+ this.debugger.paused = true;
221
+ await new Promise(resolve => setTimeout(resolve, 100));
222
+ }
223
+ }
224
+
225
+ result = await this.evaluateStatement(stmt);
226
+
227
+ if (this.isReturn) {
228
+ this.isReturn = false;
229
+ return this.returnValue;
230
+ }
231
+ }
232
+
233
+ return result;
234
+ } finally {
235
+ this.currentEnv = env;
236
+ }
237
+ };
238
+
239
+ let result = await closure(...nativeArgs);
240
+
241
+ if (this.profiler && callee.name) {
242
+ this.profiler.exitFunction();
243
+ }
244
+
245
+ return result;
246
+ } else {
247
+ throw new Error(`${callee?.name || expr.callee} не является функцией`);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Оценка оператора для дебаггера
253
+ */
254
+ async evaluateStatementDebug(stmt) {
255
+ return await this.evaluateStatement(stmt);
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Интерпретация AST
261
+ */
262
+ async interpret(ast, options = {}) {
263
+ if (this.debug) {
264
+ console.log('[Interpreter] Starting interpretation');
265
+ }
266
+ this.startExecution();
267
+
268
+ try {
269
+ const previousEnv = this.currentEnv;
270
+ const previousFilename = this.currentFilename;
271
+
272
+ let executionEnv = this.currentEnv;
273
+ if (options.environment) {
274
+ executionEnv = options.environment;
275
+ }
276
+ this.currentFilename = options.filename || '<anonymous>';
277
+
278
+ // Register builtins in the execution environment
279
+ this.currentEnv = executionEnv;
280
+ this.registerBuiltins();
281
+ if (this.debug) {
282
+ console.log('[Interpreter] Builtins registered');
283
+ }
284
+
285
+ // Create a child environment for program execution to allow local variables
286
+ // to shadow built-in functions/constants
287
+ const programEnv = executionEnv.child('<program>');
288
+ this.currentEnv = programEnv;
289
+
290
+ if (this.debug) {
291
+ console.log('[Interpreter] AST body length:', ast.body.length);
292
+ }
293
+ const result = await this.evaluateProgram(ast);
294
+ if (this.debug) {
295
+ console.log('[Interpreter] Program evaluated');
296
+ }
297
+ this.stopExecution();
298
+
299
+ // Merge exports from child environment to parent environment if they exist
300
+ if (programEnv.exports && Object.keys(programEnv.exports).length > 0) {
301
+ if (!executionEnv.exports) {
302
+ executionEnv.exports = {};
303
+ }
304
+ Object.assign(executionEnv.exports, programEnv.exports);
305
+ }
306
+
307
+ this.currentEnv = previousEnv; // Restore the original environment
308
+ this.currentFilename = previousFilename;
309
+
310
+ return result;
311
+
312
+ } catch (error) {
313
+ this.stopExecution();
314
+ if (this.debug) {
315
+ console.error('[Interpreter] ПОЙМАНА ОШИБКА ВЫПОЛНЕНИЯ:', error);
316
+ console.error('[Interpreter] Тип ошибки:', error.constructor.name);
317
+ console.error('[Interpreter] Стек ошибки:');
318
+ console.error(error.stack);
319
+ console.error('[Interpreter] Текущий файл:', this.currentFilename);
320
+ console.error('[Interpreter] Текущий стек вызовов:', this.callStack);
321
+ }
322
+ throw this.wrapError(error);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Регистрация встроенных функций в окружении
328
+ */
329
+ registerBuiltins() {
330
+ if (this.builtinsRegistered) {
331
+ return; // Уже зарегистрировано
332
+ }
333
+
334
+ for (const [name, fn] of this.builtins) {
335
+ this.globalEnv.define(name, VladXObject.function(fn, name), true);
336
+ }
337
+
338
+ this.builtinsRegistered = true;
339
+ }
340
+
341
+ /**
342
+ * Выполнение программы
343
+ */
344
+ async evaluateProgram(program) {
345
+ let result = VladXObject.null();
346
+
347
+ for (const statement of program.body) {
348
+ if (statement.type === 'EmptyStatement') continue;
349
+
350
+ try {
351
+ result = await this.evaluateStatement(statement);
352
+
353
+ if (this.isReturn) {
354
+ break;
355
+ }
356
+ } catch (error) {
357
+ if (this.errorHandler) {
358
+ await this.executeCatch(this.errorHandler, error);
359
+ this.errorHandler = null;
360
+ } else {
361
+ throw error;
362
+ }
363
+ }
364
+ }
365
+
366
+ return result;
367
+ }
368
+
369
+ /**
370
+ * Выполнение инструкции
371
+ */
372
+ async evaluateStatement(statement) {
373
+ if (!statement) {
374
+ return VladXObject.null();
375
+ }
376
+
377
+ switch (statement.type) {
378
+ case 'ExpressionStatement':
379
+ return await this.evaluateExpression(statement.expression);
380
+
381
+ case 'LetStatement':
382
+ return await this.evaluateLetStatement(statement);
383
+
384
+ case 'ConstStatement':
385
+ return await this.evaluateConstStatement(statement);
386
+
387
+ case 'VariableDeclarationWithPattern':
388
+ return await this.evaluateVariableDeclarationWithPattern(statement);
389
+
390
+ case 'ReturnStatement':
391
+ return await this.evaluateReturnStatement(statement);
392
+
393
+ case 'BlockStatement':
394
+ return await this.evaluateBlockStatement(statement);
395
+
396
+ case 'IfStatement':
397
+ return await this.evaluateIfStatement(statement);
398
+
399
+ case 'WhileStatement':
400
+ return await this.evaluateWhileStatement(statement);
401
+
402
+ case 'ForStatement':
403
+ return await this.evaluateForStatement(statement);
404
+
405
+ case 'BreakStatement':
406
+ return this.evaluateBreakStatement(statement);
407
+
408
+ case 'ContinueStatement':
409
+ return this.evaluateContinueStatement(statement);
410
+
411
+ case 'FunctionDeclaration':
412
+ return await this.evaluateFunctionDeclaration(statement);
413
+
414
+ case 'ClassDeclaration':
415
+ return await this.evaluateClassDeclaration(statement);
416
+
417
+ case 'TryStatement':
418
+ return await this.evaluateTryStatement(statement);
419
+
420
+ case 'ThrowStatement':
421
+ return await this.evaluateThrowStatement(statement);
422
+
423
+ case 'SwitchStatement':
424
+ return await this.evaluateSwitchStatement(statement);
425
+
426
+ case 'ImportStatement':
427
+ return await this.evaluateImportStatement(statement);
428
+
429
+ case 'ExportStatement':
430
+ return await this.evaluateExportStatement(statement);
431
+
432
+ case 'LabeledStatement':
433
+ return await this.evaluateLabeledStatement(statement);
434
+
435
+ case 'EmptyStatement':
436
+ return VladXObject.null();
437
+
438
+ default:
439
+ throw new Error(`Неизвестный тип инструкции: '${statement.type}'. Доступные типы: ExpressionStatement, LetStatement, ConstStatement, ReturnStatement, BlockStatement, IfStatement, WhileStatement, ForStatement, BreakStatement, ContinueStatement, FunctionDeclaration, ClassDeclaration, TryStatement, ThrowStatement, SwitchStatement, ImportStatement, ExportStatement, LabeledStatement, EmptyStatement`);
440
+ }
441
+ }
442
+
443
+ // ========== Выражения ==========
444
+
445
+ /**
446
+ * Вычисление выражения
447
+ */
448
+ async evaluateExpression(expr) {
449
+ if (!expr) {
450
+ return VladXObject.null();
451
+ }
452
+
453
+ switch (expr.type) {
454
+ case 'Literal':
455
+ return this.evaluateLiteral(expr);
456
+
457
+ case 'Identifier':
458
+ return this.evaluateIdentifier(expr);
459
+
460
+ case 'ThisExpression':
461
+ return this.evaluateThisExpression(expr);
462
+
463
+ case 'SuperExpression':
464
+ return this.evaluateSuperExpression(expr);
465
+
466
+ case 'NewExpression':
467
+ return this.evaluateNewExpression(expr);
468
+
469
+ case 'BinaryExpression':
470
+ return this.evaluateBinaryExpression(expr);
471
+
472
+ case 'UnaryExpression':
473
+ return this.evaluateUnaryExpression(expr);
474
+
475
+ case 'CallExpression':
476
+ return this.evaluateCallExpression(expr);
477
+
478
+ case 'MemberExpression':
479
+ return this.evaluateMemberExpression(expr);
480
+
481
+ case 'Assignment':
482
+ return this.evaluateAssignment(expr);
483
+
484
+ case 'MemberAssignment':
485
+ return this.evaluateMemberAssignment(expr);
486
+
487
+ case 'AssignmentExpression':
488
+ return this.evaluateAssignmentExpression(expr);
489
+
490
+ case 'CompoundAssignmentExpression':
491
+ return this.evaluateCompoundAssignmentExpression(expr);
492
+
493
+ case 'BitwiseExpression':
494
+ return this.evaluateBitwiseExpression(expr);
495
+
496
+ case 'TemplateLiteral':
497
+ return this.evaluateTemplateLiteral(expr);
498
+
499
+ case 'AwaitExpression':
500
+ return this.evaluateAwaitExpression(expr);
501
+
502
+ case 'ArrayExpression':
503
+ return this.evaluateArrayExpression(expr);
504
+
505
+ case 'ObjectExpression':
506
+ return this.evaluateObjectExpression(expr);
507
+
508
+ case 'FunctionDeclaration':
509
+ return this.evaluateFunctionDeclaration(expr);
510
+
511
+ case 'ArrowFunctionExpression':
512
+ return this.evaluateArrowFunctionExpression(expr);
513
+
514
+ case 'TernaryExpression':
515
+ return this.evaluateTernaryExpression(expr);
516
+
517
+ case 'ImportExpression':
518
+ return this.evaluateImportExpression(expr);
519
+
520
+ case 'SequenceExpression':
521
+ return this.evaluateSequenceExpression(expr);
522
+
523
+ case 'ArrayPattern':
524
+ return this.evaluateArrayPattern(expr);
525
+
526
+ case 'ObjectPattern':
527
+ return this.evaluateObjectPattern(expr);
528
+
529
+ default:
530
+ throw new Error(`Неизвестный тип выражения: '${expr.type}'. Доступные типы: Literal, Identifier, ThisExpression, SuperExpression, BinaryExpression, UnaryExpression, CallExpression, MemberExpression, Assignment, MemberAssignment, AssignmentExpression, CompoundAssignmentExpression, BitwiseExpression, TemplateLiteral, ArrayExpression, ObjectExpression, FunctionDeclaration, ArrowFunctionExpression, TernaryExpression, ImportExpression, SequenceExpression, ArrayPattern, ObjectPattern, NewExpression`);
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Литерал
536
+ */
537
+ evaluateLiteral(expr) {
538
+ const value = expr.value;
539
+ const valueType = typeof value;
540
+
541
+ if (valueType === 'number') {
542
+ return VladXObject.number(value);
543
+ }
544
+ if (valueType === 'boolean') {
545
+ return VladXObject.boolean(value);
546
+ }
547
+ if (valueType === 'string') {
548
+ return VladXObject.string(value);
549
+ }
550
+ if (value === null) {
551
+ return VladXObject.null();
552
+ }
553
+
554
+ return VladXObject.string(value);
555
+ }
556
+
557
+ /**
558
+ * Идентификатор
559
+ */
560
+ evaluateIdentifier(expr) {
561
+ return this.currentEnv.get(expr.name);
562
+ }
563
+
564
+ /**
565
+ * This выражение
566
+ */
567
+ evaluateThisExpression(expr) {
568
+ if (this.currentInstance && this.currentInstance.type === 'instance') {
569
+ return this.currentInstance;
570
+ }
571
+ throw new Error('Использование "это" (this) вне контекста метода класса');
572
+ }
573
+
574
+ /**
575
+ * Super выражение
576
+ */
577
+ evaluateSuperExpression(expr) {
578
+ if (!this.currentInstance || this.currentInstance.type !== 'instance') {
579
+ throw new Error('Использование "super" вне контекста метода класса');
580
+ }
581
+ if (!this.currentInstance.prototype || !this.currentInstance.prototype.prototype) {
582
+ throw new Error('У класса нет родительского класса');
583
+ }
584
+
585
+ const superClass = this.currentInstance.prototype.prototype;
586
+ const superInstance = VladXObject.instance(superClass);
587
+ superInstance.value = this.currentInstance.value;
588
+ return superInstance;
589
+ }
590
+
591
+ /**
592
+ * New expression - создание экземпляра класса
593
+ */
594
+ async evaluateNewExpression(expr) {
595
+ const callee = await this.evaluateExpression(expr.callee);
596
+
597
+ if (!callee || callee.type !== 'class') {
598
+ if (callee) {
599
+ throw new Error(`new можно использовать только с классами, получено: ${callee.type}`);
600
+ } else {
601
+ throw new Error('new можно использовать только с классами, callee is null');
602
+ }
603
+ }
604
+
605
+ const instance = VladXObject.instance(callee);
606
+
607
+ const args = [];
608
+ for (const arg of expr.args) {
609
+ args.push(await this.evaluateExpression(arg));
610
+ }
611
+
612
+ if (callee.methods && (callee.methods.has('конструктор') || callee.methods.has('constructor'))) {
613
+ const constructorMethod = callee.methods.has('конструктор') ? callee.methods.get('конструктор') : callee.methods.get('constructor');
614
+
615
+ const previousInstance = this.currentInstance;
616
+ this.currentInstance = instance;
617
+ try {
618
+ await this.executeFunction(constructorMethod, args);
619
+ } finally {
620
+ this.currentInstance = previousInstance;
621
+ }
622
+ } else if (callee.prototype && callee.prototype.methods && (callee.prototype.methods.has('конструктор') || callee.prototype.methods.has('constructor'))) {
623
+ const parentConstructorMethod = callee.prototype.methods.has('конструктор') ? callee.prototype.methods.get('конструктор') : callee.prototype.methods.get('constructor');
624
+
625
+ const previousInstance = this.currentInstance;
626
+ const superInstance = VladXObject.instance(callee.prototype);
627
+ superInstance.value = instance.value;
628
+ this.currentInstance = superInstance;
629
+ try {
630
+ await this.executeFunction(parentConstructorMethod, args);
631
+ } finally {
632
+ this.currentInstance = previousInstance;
633
+ }
634
+ }
635
+
636
+ return instance;
637
+ }
638
+
639
+ /**
640
+ * Бинарное выражение
641
+ */
642
+ async evaluateBinaryExpression(expr) {
643
+ const left = await this.evaluateExpression(expr.left);
644
+ const right = await this.evaluateExpression(expr.right);
645
+
646
+ // Извлекаем "сырые" значения из VladXObject
647
+ const lval = (left && left.type !== undefined && 'value' in left) ? left.value : (left ?? '');
648
+ const rval = (right && right.type !== undefined && 'value' in right) ? right.value : (right ?? '');
649
+
650
+ switch (expr.operator) {
651
+ case '+':
652
+ // Проверяем, если хотя бы один операнд строка
653
+ if (typeof lval === 'string' || typeof rval === 'string') {
654
+ return VladXObject.string(String(lval) + String(rval));
655
+ }
656
+ return VladXObject.number(lval + rval);
657
+ case '-': return VladXObject.number(lval - rval);
658
+ case '*': return VladXObject.number(lval * rval);
659
+ case '/':
660
+ if (rval === 0) throw new Error(`Деление на ноль: ${lval} / ${rval}`);
661
+ return VladXObject.number(lval / rval);
662
+ case '%': return VladXObject.number(lval % rval);
663
+ case '**': return VladXObject.number(Math.pow(lval, rval));
664
+
665
+ case '==': return VladXObject.boolean(lval == rval);
666
+ case '!=': return VladXObject.boolean(lval != rval);
667
+ case '<': return VladXObject.boolean(lval < rval);
668
+ case '>': return VladXObject.boolean(lval > rval);
669
+ case '<=': return VladXObject.boolean(lval <= rval);
670
+ case '>=': return VladXObject.boolean(lval >= rval);
671
+
672
+ case '&&': return VladXObject.boolean(lval && rval);
673
+ case '||': return VladXObject.boolean(lval || rval);
674
+
675
+ default:
676
+ throw new Error(`Неизвестный оператор: '${expr.operator}' при вычислении выражения ${lval} ${expr.operator} ${rval}`);
677
+ }
678
+ }
679
+
680
+ /**
681
+ * Унарное выражение
682
+ */
683
+ async evaluateUnaryExpression(expr) {
684
+ const operand = await this.evaluateExpression(expr.operand);
685
+ const val = operand && operand.value !== undefined ? operand.value : operand;
686
+
687
+ switch (expr.operator) {
688
+ case '-': return VladXObject.number(-val);
689
+ case '+': return VladXObject.number(+val);
690
+ case '!': return VladXObject.boolean(!val);
691
+ case 'не': return VladXObject.boolean(!val);
692
+ case 'typeof':
693
+ if (operand && operand.type !== undefined) {
694
+ return VladXObject.string(operand.type);
695
+ }
696
+ return VladXObject.string(typeof val);
697
+
698
+ default:
699
+ throw new Error(`Неизвестный унарный оператор: '${expr.operator}' при вычислении выражения ${expr.operator} ${val}`);
700
+ }
701
+ }
702
+
703
+ /**
704
+ * Вызов функции
705
+ */
706
+ async evaluateCallExpression(expr) {
707
+ let instance = null;
708
+
709
+ if (expr.callee.type === 'MemberExpression') {
710
+ const object = await this.evaluateExpression(expr.callee.object);
711
+ if (object && object.type === 'instance') {
712
+ instance = object;
713
+ }
714
+ }
715
+
716
+ const callee = await this.evaluateExpression(expr.callee);
717
+
718
+ const args = [];
719
+ for (const arg of expr.args) {
720
+ if (arg && arg.type === 'SpreadElement') {
721
+ const spreadValue = await this.evaluateExpression(arg.argument);
722
+ const spreadArray = spreadValue && spreadValue.value ? spreadValue.value : [];
723
+
724
+ if (Array.isArray(spreadArray)) {
725
+ args.push(...spreadArray);
726
+ } else {
727
+ args.push(spreadValue);
728
+ }
729
+ } else {
730
+ args.push(await this.evaluateExpression(arg));
731
+ }
732
+ }
733
+
734
+ const convertToNative = (val) => {
735
+ if (val && typeof val === 'object' && val.type !== undefined) {
736
+ if (val.type === 'function' || val.type === 'closure') {
737
+ return val;
738
+ }
739
+
740
+ if (val.type === 'null') {
741
+ return null;
742
+ }
743
+
744
+ if (val.value !== undefined) {
745
+ const rawValue = val.value;
746
+
747
+ if (Array.isArray(rawValue)) {
748
+ return rawValue.map(item => convertToNative(item));
749
+ } else if (rawValue && typeof rawValue === 'object' && rawValue.constructor === Object) {
750
+ const converted = {};
751
+ for (const key in rawValue) {
752
+ if (rawValue.hasOwnProperty(key)) {
753
+ converted[key] = convertToNative(rawValue[key]);
754
+ }
755
+ }
756
+ return converted;
757
+ } else {
758
+ return rawValue;
759
+ }
760
+ }
761
+ }
762
+ return val;
763
+ };
764
+
765
+ const nativeArgs = args.map(arg => convertToNative(arg));
766
+
767
+ if (callee && callee.isNative) {
768
+ try {
769
+ const result = callee.value(...nativeArgs);
770
+ if (result instanceof Promise) {
771
+ return await result;
772
+ }
773
+ return result;
774
+ } catch (error) {
775
+ if (error && error.type === 'error') {
776
+ throw new Error(error.value || 'Неизвестная ошибка во встроенной функции');
777
+ }
778
+ throw error;
779
+ }
780
+ }
781
+
782
+ if (callee && (callee.type === 'function' || callee.type === 'closure')) {
783
+ const previousInstance = this.currentInstance;
784
+ this.currentInstance = instance;
785
+ try {
786
+ const result = await this.executeFunction(callee, args);
787
+ return result;
788
+ } finally {
789
+ this.currentInstance = previousInstance;
790
+ }
791
+ }
792
+
793
+ let calleeInfo = 'неизвестное значение';
794
+ if (callee) {
795
+ if (callee.type === 'string') {
796
+ calleeInfo = `строка "${callee.value}"`;
797
+ } else if (callee.type === 'number') {
798
+ calleeInfo = `число ${callee.value}`;
799
+ } else if (callee.type === 'boolean') {
800
+ calleeInfo = `логическое значение`;
801
+ } else if (callee.type === 'null') {
802
+ calleeInfo = `значение nothing`;
803
+ } else if (Array.isArray(callee)) {
804
+ calleeInfo = `массив`;
805
+ } else if (typeof callee === 'object') {
806
+ calleeInfo = `объект`;
807
+ } else {
808
+ calleeInfo = String(callee);
809
+ }
810
+ }
811
+
812
+ const error = new Error(`Ошибка вызова функции: '${calleeInfo}' не является функцией и не может быть вызвано. Проверьте, что вы вызываете существующую функцию.`);
813
+ console.log('[DEBUG] evaluateCallExpression error:', error);
814
+ throw error;
815
+ }
816
+
817
+ /**
818
+ * Выполнение функции
819
+ */
820
+ async executeFunction(fn, args) {
821
+ const previousFunction = this.currentFunction;
822
+ const previousReturn = { isReturn: this.isReturn, value: this.returnValue };
823
+ const previousEnv = this.currentEnv;
824
+
825
+ // Создаем локальное окружение для функции
826
+ const functionEnv = fn.env ? fn.env.child(`<function ${fn.name}>`) : new Environment(this.currentEnv, `<function ${fn.name}>`);
827
+ this.currentEnv = functionEnv;
828
+
829
+ // Определяем параметры в локальном окружении
830
+ if (fn.ast && fn.ast.params) {
831
+ let restParamIndex = -1;
832
+
833
+ // Найдем индекс rest параметра, если он есть
834
+ for (let i = 0; i < fn.ast.params.length; i++) {
835
+ if (fn.ast.params[i].type === 'RestElement') {
836
+ restParamIndex = i;
837
+ break;
838
+ }
839
+ }
840
+
841
+ for (let i = 0; i < fn.ast.params.length; i++) {
842
+ const param = fn.ast.params[i];
843
+ const argValue = args[i];
844
+
845
+ // Обработка rest параметра
846
+ if (param.type === 'RestElement') {
847
+ // Собираем все оставшиеся аргументы в массив
848
+ const restArgs = args.slice(i) || [];
849
+ functionEnv.define(param.argument.name, VladXObject.array(restArgs));
850
+ }
851
+ // Проверяем, является ли параметр шаблоном присваивания (имеет значение по умолчанию)
852
+ else if (param.type === 'AssignmentPattern') {
853
+ // Если аргумент не передан, используем значение по умолчанию
854
+ if (argValue === undefined) {
855
+ const defaultValue = await this.evaluateExpression(param.right);
856
+ functionEnv.define(param.left.name, defaultValue);
857
+ } else {
858
+ functionEnv.define(param.left.name, argValue);
859
+ }
860
+ } else {
861
+ // Обычный параметр
862
+ functionEnv.define(param.name, argValue ? argValue : VladXObject.null());
863
+ }
864
+ }
865
+ }
866
+
867
+ this.currentFunction = fn;
868
+ this.isReturn = false;
869
+ this.returnValue = null;
870
+
871
+ this.callStack.push(fn.name);
872
+
873
+ let result = VladXObject.null();
874
+ let didReturn = false;
875
+ let returnVal = null;
876
+
877
+ try {
878
+ if (fn.ast && fn.ast.body) {
879
+ // fn.ast.body is a BlockStatement, so we need fn.ast.body.body to get the statements
880
+ const bodyStatements = fn.ast.body.body || [];
881
+ let lastExpressionResult = null;
882
+
883
+ for (const statement of bodyStatements) {
884
+ result = await this.evaluateStatement(statement);
885
+
886
+ // Сохраняем результат ExpressionStatement для возврата
887
+ if (statement.type === 'ExpressionStatement') {
888
+ lastExpressionResult = result;
889
+ }
890
+
891
+ if (this.isReturn) {
892
+ didReturn = true;
893
+ returnVal = this.returnValue;
894
+ break;
895
+ }
896
+ }
897
+
898
+ // Если не было return, возвращаем результат последнего ExpressionStatement
899
+ if (!didReturn && lastExpressionResult !== null) {
900
+ result = lastExpressionResult;
901
+ }
902
+ }
903
+ } finally {
904
+ this.currentFunction = previousFunction;
905
+ this.isReturn = previousReturn.isReturn;
906
+ this.returnValue = previousReturn.value;
907
+ this.currentEnv = previousEnv;
908
+
909
+ this.callStack.pop();
910
+ }
911
+
912
+ // Если функция асинхронная, и результат - промис, дожидаемся его
913
+ if (fn.ast && fn.ast.isAsync) {
914
+ if (result && typeof result.then === 'function') {
915
+ return await result;
916
+ }
917
+ if (result && result.value && typeof result.value.then === 'function') {
918
+ const resolved = await result.value;
919
+ return VladXObject.fromJS(resolved);
920
+ }
921
+ }
922
+
923
+ return didReturn ? returnVal : result;
924
+ }
925
+
926
+ /**
927
+ * Присваивание
928
+ */
929
+ async evaluateAssignment(expr) {
930
+ const value = await this.evaluateExpression(expr.value);
931
+
932
+ // Обработка обычного присваивания
933
+ if (expr.name) {
934
+ this.currentEnv.assign(expr.name, value);
935
+ }
936
+ return value;
937
+ }
938
+
939
+ /**
940
+ * Деструктуризация массива
941
+ */
942
+ async evaluateArrayDestructuring(pattern, value) {
943
+ // Извлекаем нативный массив из VladXObject
944
+ let arrayValue;
945
+ if (value && value.type === 'array' && value.value) {
946
+ arrayValue = value.value;
947
+ } else if (Array.isArray(value)) {
948
+ arrayValue = value;
949
+ } else {
950
+ throw new Error('Деструктуризация массива требует массив в правой части');
951
+ }
952
+
953
+ for (let i = 0; i < pattern.elements.length; i++) {
954
+ const element = pattern.elements[i];
955
+ let elementValue = VladXObject.null();
956
+
957
+ if (i < arrayValue.length) {
958
+ const rawValue = arrayValue[i];
959
+ // Преобразуем значение в VladXObject если нужно
960
+ if (rawValue && rawValue.type !== undefined) {
961
+ elementValue = rawValue; // Уже VladXObject
962
+ } else {
963
+ elementValue = VladXObject.fromJS(rawValue);
964
+ }
965
+ }
966
+
967
+ if (element) {
968
+ if (element.type === 'Identifier') {
969
+ this.currentEnv.define(element.name, elementValue);
970
+ } else if (element.type === 'AssignmentPattern') {
971
+ // Обработка параметров с умолчанием
972
+ this.currentEnv.define(element.left.name, elementValue);
973
+ }
974
+ }
975
+ // TODO: Поддержка вложенной деструктуризации
976
+ }
977
+
978
+ return value;
979
+ }
980
+
981
+ /**
982
+ * Деструктуризация объекта
983
+ */
984
+ async evaluateObjectDestructuring(pattern, value) {
985
+ // Извлекаем нативный объект из VladXObject
986
+ let objectValue;
987
+ if (value && value.type === 'object' && value.value) {
988
+ objectValue = value.value;
989
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
990
+ objectValue = value;
991
+ } else {
992
+ throw new Error('Деструктуризация объекта требует объект в правой части');
993
+ }
994
+
995
+ for (const property of pattern.properties) {
996
+ if (property.type === 'Property') {
997
+ let propertyValue = VladXObject.null();
998
+
999
+ if (property.computed) {
1000
+ // Вычисленное свойство
1001
+ const key = await this.evaluateExpression(property.key);
1002
+ const keyValue = key && key.value !== undefined ? key.value : key;
1003
+ const rawValue = objectValue[keyValue];
1004
+ propertyValue = rawValue !== undefined ? VladXObject.fromJS(rawValue) : VladXObject.null();
1005
+ } else {
1006
+ // Статическое свойство
1007
+ let key;
1008
+ if (property.key && property.key.name) {
1009
+ key = property.key.name;
1010
+ } else if (property.key && property.key.value !== undefined) {
1011
+ key = property.key.value;
1012
+ } else {
1013
+ key = property.key;
1014
+ }
1015
+ const rawValue = objectValue[key];
1016
+ propertyValue = rawValue !== undefined ? VladXObject.fromJS(rawValue) : VladXObject.null();
1017
+ }
1018
+
1019
+ if (property.value && property.value.type === 'Identifier') {
1020
+ this.currentEnv.define(property.value.name, propertyValue);
1021
+ } else if (property.value && property.value.type === 'AssignmentPattern') {
1022
+ // Обработка параметров с умолчанием
1023
+ this.currentEnv.define(property.value.left.name, propertyValue);
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ return value;
1029
+ }
1030
+
1031
+ /**
1032
+ * Обычное присваивание выражения
1033
+ */
1034
+ async evaluateAssignmentExpression(expr) {
1035
+ const left = await this.evaluateExpression(expr.left);
1036
+ const right = await this.evaluateExpression(expr.right);
1037
+
1038
+ // Обработка обычного присваивания
1039
+ if (expr.operator === '=') {
1040
+ if (expr.left.type === 'Identifier') {
1041
+ this.currentEnv.assign(expr.left.name, right);
1042
+ return right;
1043
+ } else if (expr.left.type === 'MemberExpression') {
1044
+ // Обработка присваивания свойству объекта
1045
+ const object = await this.evaluateExpression(expr.left.object);
1046
+ const property = expr.left.property.name;
1047
+
1048
+ if (object && typeof object === 'object' && object.value) {
1049
+ object.value[property] = right;
1050
+ }
1051
+ return right;
1052
+ }
1053
+ }
1054
+
1055
+ return right;
1056
+ }
1057
+
1058
+ /**
1059
+ * Составное присваивание
1060
+ */
1061
+ async evaluateCompoundAssignmentExpression(expr) {
1062
+ const left = await this.evaluateExpression(expr.left);
1063
+ const right = await this.evaluateExpression(expr.right);
1064
+
1065
+ // Получаем текущее значение левой части
1066
+ let currentValue;
1067
+ if (expr.left.type === 'Identifier') {
1068
+ currentValue = this.currentEnv.get(expr.left.name);
1069
+ } else if (expr.left.type === 'MemberExpression') {
1070
+ const object = await this.evaluateExpression(expr.left.object);
1071
+ const property = expr.left.property.name;
1072
+ currentValue = object.value[property];
1073
+ }
1074
+
1075
+ // Выполняем операцию в зависимости от оператора
1076
+ let result;
1077
+ const leftVal = currentValue && currentValue.value !== undefined ? currentValue.value : currentValue;
1078
+ const rightVal = right && right.value !== undefined ? right.value : right;
1079
+
1080
+ switch (expr.operator) {
1081
+ case '+=':
1082
+ if (typeof leftVal === 'string' || typeof rightVal === 'string') {
1083
+ result = VladXObject.string(String(leftVal) + String(rightVal));
1084
+ } else {
1085
+ result = VladXObject.number(leftVal + rightVal);
1086
+ }
1087
+ break;
1088
+ case '-=':
1089
+ result = VladXObject.number(leftVal - rightVal);
1090
+ break;
1091
+ case '*=':
1092
+ result = VladXObject.number(leftVal * rightVal);
1093
+ break;
1094
+ case '/=':
1095
+ if (rightVal === 0) throw new Error(`Деление на ноль: ${leftVal} / ${rightVal}`);
1096
+ result = VladXObject.number(leftVal / rightVal);
1097
+ break;
1098
+ case '%=':
1099
+ result = VladXObject.number(leftVal % rightVal);
1100
+ break;
1101
+ default:
1102
+ throw new Error(`Неизвестный оператор составного присваивания: ${expr.operator}`);
1103
+ }
1104
+
1105
+ // Присваиваем результат обратно
1106
+ if (expr.left.type === 'Identifier') {
1107
+ this.currentEnv.assign(expr.left.name, result);
1108
+ } else if (expr.left.type === 'MemberExpression') {
1109
+ const object = await this.evaluateExpression(expr.left.object);
1110
+ const property = expr.left.property.name;
1111
+ object.value[property] = result;
1112
+ }
1113
+
1114
+ return result;
1115
+ }
1116
+
1117
+ /**
1118
+ * Битовые выражения
1119
+ */
1120
+ async evaluateBitwiseExpression(expr) {
1121
+ const left = await this.evaluateExpression(expr.left);
1122
+ const right = await this.evaluateExpression(expr.right);
1123
+
1124
+ const lval = left && left.value !== undefined ? left.value : left;
1125
+ const rval = right && right.value !== undefined ? right.value : right;
1126
+
1127
+ switch (expr.operator) {
1128
+ case '<<':
1129
+ return VladXObject.number(lval << rval);
1130
+ case '>>':
1131
+ return VladXObject.number(lval >> rval);
1132
+ case '&':
1133
+ return VladXObject.number(lval & rval);
1134
+ case '|':
1135
+ return VladXObject.number(lval | rval);
1136
+ case '^':
1137
+ return VladXObject.number(lval ^ rval);
1138
+ default:
1139
+ throw new Error(`Неизвестный битовый оператор: ${expr.operator}`);
1140
+ }
1141
+ }
1142
+
1143
+ /**
1144
+ * Шаблонная строка
1145
+ */
1146
+ async evaluateTemplateLiteral(expr) {
1147
+ if (!expr || !expr.expressions) {
1148
+ return VladXObject.string('');
1149
+ }
1150
+
1151
+ let result = '';
1152
+
1153
+ for (const quasi of expr.quasis || []) {
1154
+ result += quasi.value || '';
1155
+ }
1156
+
1157
+ for (const exp of expr.expressions || []) {
1158
+ const value = await this.evaluateExpression(exp);
1159
+ const val = value && value.value !== undefined ? value.value : value;
1160
+ result += String(val);
1161
+ }
1162
+
1163
+ return VladXObject.string(result);
1164
+ }
1165
+
1166
+ /**
1167
+ * Await выражение
1168
+ */
1169
+ async evaluateAwaitExpression(expr) {
1170
+ const value = await this.evaluateExpression(expr.argument);
1171
+
1172
+ // Если это VladXObject с промисом внутри
1173
+ if (value && value.value && typeof value.value.then === 'function') {
1174
+ const awaitedValue = await value.value;
1175
+ return VladXObject.fromJS(awaitedValue);
1176
+ }
1177
+
1178
+ // Если значение является промисом, ожидаем его
1179
+ if (value && typeof value.then === 'function') {
1180
+ const awaitedValue = await value;
1181
+ return VladXObject.fromJS(awaitedValue);
1182
+ }
1183
+
1184
+ // В противном случае возвращаем значение как есть
1185
+ return value;
1186
+ }
1187
+
1188
+ async evaluateMemberAssignment(expr) {
1189
+ const object = await this.evaluateExpression(expr.object);
1190
+ const value = await this.evaluateExpression(expr.value);
1191
+
1192
+ // Evaluate the property/index expression (for array access like arr[index])
1193
+ const property = await this.evaluateExpression(expr.property);
1194
+ const indexValue = property && property.value !== undefined ? property.value : property;
1195
+
1196
+ // Handle array element assignment: array[index] = value
1197
+ if (object.type === 'array' && object.value && Array.isArray(object.value)) {
1198
+ const index = typeof indexValue === 'string' ? parseInt(indexValue, 10) : indexValue;
1199
+
1200
+ if (typeof index === 'number' && index >= 0 && index < object.value.length) {
1201
+ object.value[index] = value;
1202
+ } else if (typeof index === 'number' && index === object.value.length) {
1203
+ object.value.push(value);
1204
+ } else {
1205
+ throw new Error(`Недопустимый индекс массива: ${index}, длина массива: ${object.value.length}`);
1206
+ }
1207
+ }
1208
+ else if (object.type === 'instance' && object.value && typeof object.value === 'object') {
1209
+ const propName = expr.property.name || String(indexValue);
1210
+ object.value[propName] = value;
1211
+ }
1212
+ else if (object.type === 'class' && !expr.computed) {
1213
+ const propName = expr.property.name;
1214
+ object[propName] = value;
1215
+ }
1216
+ else if (object.type === 'object' && object.value && typeof object.value === 'object') {
1217
+ const propName = expr.property.name || String(indexValue);
1218
+ object.value[propName] = value;
1219
+ }
1220
+ else if (object && typeof object === 'object' && !object.type) {
1221
+ const propName = expr.property.name || String(indexValue);
1222
+ object[propName] = value;
1223
+ }
1224
+
1225
+ return value;
1226
+ }
1227
+
1228
+ /**
1229
+ * Член объекта
1230
+ */
1231
+ async evaluateMemberExpression(expr) {
1232
+ const object = await this.evaluateExpression(expr.object);
1233
+
1234
+ if (!object) {
1235
+ return VladXObject.null();
1236
+ }
1237
+
1238
+ if (expr.computed) {
1239
+ // Вычисленный доступ: array[index] или obj[expression]
1240
+ const index = await this.evaluateExpression(expr.property);
1241
+ const indexValue = index && index.value !== undefined ? index.value : index;
1242
+
1243
+ // Если это VladXObject массива
1244
+ if (object.type === 'array' && object.value && Array.isArray(object.value)) {
1245
+ return object.value[indexValue] !== undefined ? object.value[indexValue] : VladXObject.null();
1246
+ }
1247
+ // Если это VladXObject объекта
1248
+ else if (object.type === 'object' && object.value && typeof object.value === 'object') {
1249
+ const key = String(indexValue);
1250
+ return object.value[key] !== undefined ? object.value[key] : VladXObject.null();
1251
+ }
1252
+ // Если это обычный JS массив
1253
+ else if (Array.isArray(object)) {
1254
+ return object[indexValue] !== undefined ? object[indexValue] : VladXObject.null();
1255
+ }
1256
+ // Если это обычный JS объект
1257
+ else if (object && typeof object === 'object') {
1258
+ const key = String(indexValue);
1259
+ return object[key] !== undefined ? object[key] : VladXObject.null();
1260
+ }
1261
+ } else {
1262
+ const propName = expr.property.name;
1263
+
1264
+ if (object.type === 'class' && object.staticMethods && object.staticMethods.has(propName)) {
1265
+ const staticMethod = object.staticMethods.get(propName);
1266
+ return staticMethod;
1267
+ }
1268
+
1269
+ if (object.type === 'instance' && object.prototype && object.prototype.methods && object.prototype.methods.has(propName)) {
1270
+ const method = object.prototype.methods.get(propName);
1271
+ return method;
1272
+ }
1273
+
1274
+ if (object.type === 'instance' && object.value && typeof object.value === 'object' && object.value[propName] !== undefined) {
1275
+ const val = object.value[propName];
1276
+ if (val && typeof val === 'object' && val.type !== undefined) {
1277
+ return val;
1278
+ }
1279
+ return VladXObject.fromJS(val);
1280
+ }
1281
+
1282
+ if (object.type !== undefined && object.value && object.value[propName] !== undefined) {
1283
+ const val = object.value[propName];
1284
+ if (val && typeof val === 'object' && val.type !== undefined) {
1285
+ return val;
1286
+ }
1287
+ return VladXObject.fromJS(val);
1288
+ }
1289
+
1290
+ if (object[propName] !== undefined) {
1291
+ const val = object[propName];
1292
+ if (val && typeof val === 'object' && val.type !== undefined) {
1293
+ return val;
1294
+ }
1295
+ return VladXObject.fromJS(val);
1296
+ }
1297
+ }
1298
+
1299
+ return VladXObject.null();
1300
+ }
1301
+
1302
+ /**
1303
+ * Массив
1304
+ */
1305
+ async evaluateArrayExpression(expr) {
1306
+ const elements = [];
1307
+
1308
+ if (expr.elements) {
1309
+ for (const el of expr.elements) {
1310
+ if (!el) {
1311
+ elements.push(null);
1312
+ continue;
1313
+ }
1314
+
1315
+ if (el && el.type === 'SpreadElement') {
1316
+ const spreadValue = await this.evaluateExpression(el.argument);
1317
+ const spreadArray = spreadValue && spreadValue.value ? spreadValue.value : [];
1318
+
1319
+ if (Array.isArray(spreadArray)) {
1320
+ elements.push(...spreadArray.map(v => VladXObject.fromJS(v)));
1321
+ } else {
1322
+ // Если это не массив, просто добавляем значение
1323
+ elements.push(VladXObject.fromJS(spreadValue));
1324
+ }
1325
+ } else {
1326
+ elements.push(await this.evaluateExpression(el));
1327
+ }
1328
+ }
1329
+ }
1330
+
1331
+ return VladXObject.array(elements);
1332
+ }
1333
+
1334
+ /**
1335
+ * Паттерн массива (для деструктуризации)
1336
+ */
1337
+ async evaluateArrayPattern(expr) {
1338
+ const elements = [];
1339
+
1340
+ if (expr.elements) {
1341
+ for (const el of expr.elements) {
1342
+ if (!el) {
1343
+ elements.push(null);
1344
+ continue;
1345
+ }
1346
+
1347
+ if (el && el.type === 'SpreadElement') {
1348
+ const spreadValue = await this.evaluateExpression(el.argument);
1349
+ const spreadArray = spreadValue && spreadValue.value ? spreadValue.value : [];
1350
+
1351
+ if (Array.isArray(spreadArray)) {
1352
+ elements.push(...spreadArray.map(v => VladXObject.fromJS(v)));
1353
+ } else {
1354
+ elements.push(VladXObject.fromJS(spreadValue));
1355
+ }
1356
+ } else {
1357
+ elements.push(await this.evaluateExpression(el));
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ return VladXObject.array(elements);
1363
+ }
1364
+
1365
+ /**
1366
+ * Паттерн объекта (для деструктуризации)
1367
+ */
1368
+ async evaluateObjectPattern(expr) {
1369
+ const obj = {};
1370
+
1371
+ if (expr.properties) {
1372
+ for (const prop of expr.properties) {
1373
+ if (!prop) continue;
1374
+
1375
+ let key = null;
1376
+ if (prop.key) {
1377
+ key = prop.key.value || prop.key.name || String(prop.key);
1378
+ }
1379
+
1380
+ const value = await this.evaluateExpression(prop.value);
1381
+
1382
+ if (key !== null) {
1383
+ obj[key] = value;
1384
+ }
1385
+ }
1386
+ }
1387
+
1388
+ return VladXObject.object(obj);
1389
+ }
1390
+
1391
+ /**
1392
+ * Объект
1393
+ */
1394
+ async evaluateObjectExpression(expr) {
1395
+ const obj = {};
1396
+
1397
+ if (expr.properties) {
1398
+ for (const prop of expr.properties) {
1399
+ if (!prop) continue;
1400
+
1401
+ let key = null;
1402
+ if (prop.key) {
1403
+ key = prop.key.value || prop.key.name || String(prop.key);
1404
+ }
1405
+
1406
+ const value = await this.evaluateExpression(prop.value);
1407
+
1408
+ if (key !== null) {
1409
+ obj[key] = value;
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ return VladXObject.object(obj);
1415
+ }
1416
+
1417
+ /**
1418
+ * Объявление переменной
1419
+ */
1420
+ async evaluateLetStatement(stmt) {
1421
+ let value = VladXObject.null();
1422
+
1423
+ if (stmt.initializer) {
1424
+ value = await this.evaluateExpression(stmt.initializer);
1425
+ }
1426
+
1427
+ this.currentEnv.define(stmt.name, value);
1428
+ return value;
1429
+ }
1430
+
1431
+ /**
1432
+ * Объявление переменной с паттерном (деструктуризация)
1433
+ */
1434
+ async evaluateVariableDeclarationWithPattern(stmt) {
1435
+ let value = VladXObject.null();
1436
+
1437
+ if (stmt.initializer) {
1438
+ value = await this.evaluateExpression(stmt.initializer);
1439
+ }
1440
+
1441
+ // Выполняем деструктуризацию в зависимости от типа паттерна
1442
+ if (stmt.pattern && stmt.pattern.type === 'ArrayPattern') {
1443
+ await this.evaluateArrayDestructuring(stmt.pattern, value);
1444
+ } else if (stmt.pattern && stmt.pattern.type === 'ObjectPattern') {
1445
+ await this.evaluateObjectDestructuring(stmt.pattern, value);
1446
+ } else {
1447
+ // This shouldn't happen if parser is correct, but just in case
1448
+ // If we get here, it means this was incorrectly processed as a pattern declaration
1449
+ throw new Error(`Неправильный тип паттерна: ${stmt.pattern ? stmt.pattern.type : 'undefined'}`);
1450
+ }
1451
+
1452
+ return value;
1453
+ }
1454
+
1455
+ /**
1456
+ * Объявление константы
1457
+ */
1458
+ async evaluateConstStatement(stmt) {
1459
+ if (!stmt.initializer) {
1460
+ throw new Error(`Константа '${stmt.name}' должна быть инициализирована значением. Константы не могут быть объявлены без начального значения.`);
1461
+ }
1462
+
1463
+ const value = await this.evaluateExpression(stmt.initializer);
1464
+ this.currentEnv.define(stmt.name, value, true);
1465
+ return value;
1466
+ }
1467
+
1468
+ /**
1469
+ * Return
1470
+ */
1471
+ async evaluateReturnStatement(stmt) {
1472
+ this.isReturn = true;
1473
+ this.returnValue = stmt.argument ? await this.evaluateExpression(stmt.argument) : VladXObject.null();
1474
+ return this.returnValue;
1475
+ }
1476
+
1477
+ /**
1478
+ * Блок
1479
+ */
1480
+ async evaluateBlockStatement(stmt) {
1481
+ let result = VladXObject.null();
1482
+
1483
+ if (stmt.body) {
1484
+ for (const statement of stmt.body) {
1485
+ result = await this.evaluateStatement(statement);
1486
+
1487
+ if (this.isReturn) {
1488
+ break;
1489
+ }
1490
+ }
1491
+ }
1492
+
1493
+ return this.isReturn ? this.returnValue : result;
1494
+ }
1495
+
1496
+ /**
1497
+ * If-else
1498
+ */
1499
+ async evaluateIfStatement(stmt) {
1500
+ const condition = await this.evaluateExpression(stmt.condition);
1501
+ const condValue = condition && condition.value !== undefined ? condition.value : condition;
1502
+
1503
+ if (condValue) {
1504
+ return this.evaluateStatement(stmt.thenBranch);
1505
+ } else if (stmt.elseBranch) {
1506
+ return this.evaluateStatement(stmt.elseBranch);
1507
+ }
1508
+
1509
+ return VladXObject.null();
1510
+ }
1511
+
1512
+ /**
1513
+ * While
1514
+ */
1515
+ async evaluateWhileStatement(stmt) {
1516
+ let result = VladXObject.null();
1517
+
1518
+ while (true) {
1519
+ const condition = await this.evaluateExpression(stmt.condition);
1520
+ const condValue = condition && condition.value !== undefined ? condition.value : condition;
1521
+
1522
+ if (!condValue) break;
1523
+
1524
+ try {
1525
+ result = await this.evaluateStatement(stmt.body);
1526
+ } catch (e) {
1527
+ if (e.message === 'break') {
1528
+ break;
1529
+ }
1530
+ if (e.message === 'continue') {
1531
+ continue;
1532
+ }
1533
+ throw e;
1534
+ }
1535
+ }
1536
+
1537
+ return result;
1538
+ }
1539
+
1540
+ /**
1541
+ * For
1542
+ */
1543
+ async evaluateForStatement(stmt) {
1544
+ let result = VladXObject.null();
1545
+
1546
+ // Инициализация
1547
+ if (stmt.initializer && stmt.initializer.type !== 'EmptyStatement') {
1548
+ await this.evaluateStatement(stmt.initializer);
1549
+ }
1550
+
1551
+ while (true) {
1552
+ // Проверка условия
1553
+ if (stmt.condition) {
1554
+ const condition = await this.evaluateExpression(stmt.condition);
1555
+ const condValue = condition && condition.value !== undefined ? condition.value : condition;
1556
+ if (!condValue) break;
1557
+ }
1558
+
1559
+ try {
1560
+ result = await this.evaluateStatement(stmt.body);
1561
+ } catch (e) {
1562
+ if (e.message === 'break') {
1563
+ break;
1564
+ }
1565
+ if (e.message === 'continue') {
1566
+ // Продолжаем цикл
1567
+ } else {
1568
+ throw e;
1569
+ }
1570
+ }
1571
+
1572
+ // Обновление
1573
+ if (stmt.update && stmt.update.type !== 'EmptyStatement') {
1574
+ await this.evaluateExpression(stmt.update);
1575
+ }
1576
+ }
1577
+
1578
+ return result;
1579
+ }
1580
+
1581
+ /**
1582
+ * Break
1583
+ */
1584
+ evaluateBreakStatement(stmt) {
1585
+ throw new Error('break');
1586
+ }
1587
+
1588
+ /**
1589
+ * Continue
1590
+ */
1591
+ evaluateContinueStatement(stmt) {
1592
+ throw new Error('continue');
1593
+ }
1594
+
1595
+ /**
1596
+ * Объявление функции
1597
+ */
1598
+ evaluateFunctionDeclaration(stmt) {
1599
+ // Создаём легковесное замыкание без клонирования всего окружения
1600
+ // Это предотвращает утечки памяти через циклические ссылки
1601
+ const closure = VladXObject.closure({
1602
+ type: 'FunctionDeclaration',
1603
+ name: stmt.name,
1604
+ params: stmt.params,
1605
+ body: stmt.body,
1606
+ isAsync: stmt.isAsync
1607
+ }, this.currentEnv, stmt.name || '<anonymous>');
1608
+
1609
+ this.currentEnv.define(stmt.name, closure);
1610
+ return VladXObject.null();
1611
+ }
1612
+
1613
+ /**
1614
+ * Стрелочная функция
1615
+ */
1616
+ evaluateArrowFunctionExpression(expr) {
1617
+ // Используем текущее окружение без клонирования
1618
+ return VladXObject.closure({
1619
+ type: 'ArrowFunctionExpression',
1620
+ params: expr.params,
1621
+ body: Array.isArray(expr.body) ? expr.body : [{ type: 'ReturnStatement', argument: expr.body }],
1622
+ isAsync: expr.isAsync
1623
+ }, this.currentEnv, '<arrow>');
1624
+ }
1625
+
1626
+ /**
1627
+ * Класс
1628
+ */
1629
+ async evaluateClassDeclaration(stmt) {
1630
+ const methods = new Map();
1631
+ const staticMethods = new Map();
1632
+ let superClass = null;
1633
+
1634
+ if (stmt.superClass) {
1635
+ superClass = this.currentEnv.get(stmt.superClass);
1636
+ if (!superClass || superClass.type !== 'class') {
1637
+ throw new Error(`Родительский класс '${stmt.superClass}' не найден или не является классом`);
1638
+ }
1639
+ }
1640
+
1641
+ if (stmt.methods) {
1642
+ for (const method of stmt.methods) {
1643
+ const methodClosure = VladXObject.closure({
1644
+ type: 'FunctionDeclaration',
1645
+ name: method.name,
1646
+ params: method.params,
1647
+ body: method.body,
1648
+ isAsync: method.isAsync
1649
+ }, this.currentEnv, method.name);
1650
+
1651
+ if (method.isStatic) {
1652
+ staticMethods.set(method.name, methodClosure);
1653
+ } else {
1654
+ methods.set(method.name, methodClosure);
1655
+ }
1656
+ }
1657
+ }
1658
+
1659
+ const classObj = VladXObject.class(stmt.name, methods, staticMethods, superClass);
1660
+ this.currentEnv.define(stmt.name, classObj);
1661
+ return VladXObject.null();
1662
+ }
1663
+
1664
+ /**
1665
+ * Try-catch-finally
1666
+ */
1667
+ async evaluateTryStatement(stmt) {
1668
+ let result = VladXObject.null();
1669
+ let error = null;
1670
+
1671
+ // Try
1672
+ try {
1673
+ if (stmt.block && stmt.block.body) {
1674
+ for (const statement of stmt.block.body) {
1675
+ result = await this.evaluateStatement(statement);
1676
+ if (this.isReturn) break;
1677
+ }
1678
+ }
1679
+ } catch (e) {
1680
+ error = e;
1681
+ }
1682
+
1683
+ // Catch
1684
+ if (stmt.handler && error) {
1685
+ const catchEnv = this.currentEnv.child('<catch>');
1686
+ if (stmt.handler.param) {
1687
+ catchEnv.define(stmt.handler.param, VladXObject.string(error.toString ? error.toString() : String(error)));
1688
+ }
1689
+
1690
+ const previousEnv = this.currentEnv;
1691
+ this.currentEnv = catchEnv;
1692
+
1693
+ if (stmt.handler.body && stmt.handler.body.body) {
1694
+ for (const statement of stmt.handler.body.body) {
1695
+ result = await this.evaluateStatement(statement);
1696
+ if (this.isReturn) break;
1697
+ }
1698
+ }
1699
+
1700
+ this.currentEnv = previousEnv;
1701
+ }
1702
+
1703
+ // Finally
1704
+ if (stmt.finalizer) {
1705
+ for (const statement of stmt.finalizer.body) {
1706
+ result = await this.evaluateStatement(statement);
1707
+ if (this.isReturn) break;
1708
+ }
1709
+ }
1710
+
1711
+ return result;
1712
+ }
1713
+
1714
+ /**
1715
+ * Throw
1716
+ */
1717
+ async evaluateThrowStatement(stmt) {
1718
+ const value = await this.evaluateExpression(stmt.argument);
1719
+ const val = value && value.value !== undefined ? value.value : value;
1720
+ throw new Error(String(val));
1721
+ }
1722
+
1723
+ /**
1724
+ * Import - загрузка модуля
1725
+ */
1726
+ async evaluateImportStatement(stmt) {
1727
+ const modulePath = stmt.path;
1728
+
1729
+ if (!this.moduleSystem) {
1730
+ throw new Error('Система модулей не инициализирована — moduleSystem is null!');
1731
+ }
1732
+
1733
+ const moduleExports = await this.moduleSystem.loadModule(modulePath, this.currentFilename);
1734
+
1735
+ let moduleName = stmt.alias;
1736
+ if (!moduleName) {
1737
+ const filename = modulePath.split('/').pop();
1738
+ const extIndex = filename.lastIndexOf('.');
1739
+ moduleName = extIndex > 0 ? filename.slice(0, extIndex) : filename;
1740
+ if (!moduleName) moduleName = 'module';
1741
+ }
1742
+
1743
+ const moduleObj = VladXObject.object(moduleExports);
1744
+ this.currentEnv.define(moduleName, moduleObj);
1745
+
1746
+ return VladXObject.null();
1747
+ }
1748
+ /**
1749
+ * Import expression - загрузка модуля как выражение
1750
+ */
1751
+ async evaluateImportExpression(expr) {
1752
+ const modulePath = expr.path;
1753
+
1754
+ // Используем moduleSystem для загрузки модуля
1755
+ if (this.moduleSystem) {
1756
+ const moduleExports = await this.moduleSystem.loadModule(modulePath, this.currentFilename);
1757
+ return moduleExports;
1758
+ }
1759
+
1760
+ throw new Error('Система модулей не инициализирована');
1761
+ }
1762
+
1763
+ /**
1764
+ * Export - экспорт имен из модуля
1765
+ */
1766
+ async evaluateExportStatement(stmt) {
1767
+ const exports = {};
1768
+
1769
+ for (const name of stmt.identifiers) {
1770
+ const value = this.currentEnv.get(name);
1771
+ if (value !== undefined) {
1772
+ exports[name] = value;
1773
+ }
1774
+ }
1775
+
1776
+ // Сохраняем экспорты в специальном объекте
1777
+ if (!this.currentEnv.exports) {
1778
+ this.currentEnv.exports = exports;
1779
+ } else {
1780
+ Object.assign(this.currentEnv.exports, exports);
1781
+ }
1782
+
1783
+ return VladXObject.null();
1784
+ }
1785
+
1786
+ /**
1787
+ * Тернарный оператор
1788
+ */
1789
+ async evaluateTernaryExpression(expr) {
1790
+ const condition = await this.evaluateExpression(expr.condition);
1791
+ const condValue = condition && condition.value !== undefined ? condition.value : condition;
1792
+
1793
+ if (condValue) {
1794
+ return await this.evaluateExpression(expr.thenExpr);
1795
+ }
1796
+ return await this.evaluateExpression(expr.elseExpr);
1797
+ }
1798
+
1799
+ /**
1800
+ * Последовательность (a, b, c)
1801
+ */
1802
+ async evaluateSequenceExpression(expr) {
1803
+ let result = VladXObject.null();
1804
+
1805
+ if (expr.expressions) {
1806
+ for (const e of expr.expressions) {
1807
+ result = await this.evaluateExpression(e);
1808
+ }
1809
+ }
1810
+
1811
+ return result;
1812
+ }
1813
+
1814
+ /**
1815
+ * Switch statement
1816
+ */
1817
+ async evaluateSwitchStatement(stmt) {
1818
+ const discriminant = await this.evaluateExpression(stmt.discriminant);
1819
+ const discriminantValue = discriminant && discriminant.value !== undefined ? discriminant.value : discriminant;
1820
+
1821
+ let matched = false;
1822
+ let result = VladXObject.null();
1823
+
1824
+ // Execute cases
1825
+ for (const caseStmt of stmt.cases) {
1826
+ if (!matched) {
1827
+ const caseValue = await this.evaluateExpression(caseStmt.test);
1828
+ const caseValueValue = caseValue && caseValue.value !== undefined ? caseValue.value : caseValue;
1829
+
1830
+ // Strict equality comparison
1831
+ if (caseValueValue === discriminantValue) {
1832
+ matched = true;
1833
+ }
1834
+ }
1835
+
1836
+ if (matched) {
1837
+ // Execute case body
1838
+ for (const statement of caseStmt.consequent) {
1839
+ try {
1840
+ result = await this.evaluateStatement(statement);
1841
+
1842
+ // If we encounter a return, exit switch
1843
+ if (this.isReturn) {
1844
+ return result;
1845
+ }
1846
+ } catch (e) {
1847
+ // If it's a break statement, exit entire switch
1848
+ if (e.message === 'break') {
1849
+ return result; // Exit entire switch
1850
+ } else {
1851
+ // Re-throw other errors
1852
+ throw e;
1853
+ }
1854
+ }
1855
+ }
1856
+ }
1857
+ }
1858
+
1859
+ // If no case matched and there's a default case
1860
+ if (!matched && stmt.defaultCase) {
1861
+ for (const statement of stmt.defaultCase.consequent) {
1862
+ try {
1863
+ result = await this.evaluateStatement(statement);
1864
+
1865
+ // If we encounter a return, exit switch
1866
+ if (this.isReturn) {
1867
+ return result;
1868
+ }
1869
+ } catch (e) {
1870
+ // If it's a break statement, exit entire switch
1871
+ if (e.message === 'break') {
1872
+ return result; // Exit entire switch
1873
+ } else {
1874
+ // Re-throw other errors
1875
+ throw e;
1876
+ }
1877
+ }
1878
+ }
1879
+ }
1880
+
1881
+ return result;
1882
+ }
1883
+
1884
+ /**
1885
+ * Match expression (pattern matching)
1886
+ */
1887
+ async evaluateMatchExpression(expr) {
1888
+ const value = await this.evaluateExpression(expr.value);
1889
+ const valueValue = value && value.value !== undefined ? value.value : value;
1890
+
1891
+ for (const arm of expr.arms) {
1892
+ const pattern = arm.pattern;
1893
+ const guard = arm.guard;
1894
+ const resultExpr = arm.result;
1895
+
1896
+ // Evaluate guard if exists
1897
+ if (guard) {
1898
+ const guardResult = await this.evaluateExpression(guard);
1899
+ const guardValue = guardResult && guardResult.value !== undefined ? guardResult.value : guardResult;
1900
+
1901
+ if (!guardValue) {
1902
+ continue;
1903
+ }
1904
+ }
1905
+
1906
+ // Match pattern
1907
+ if (this.matchPattern(pattern, valueValue)) {
1908
+ // Bind variables from pattern
1909
+ if (pattern.type === 'Identifier') {
1910
+ this.currentEnv.define(pattern.name, value);
1911
+ }
1912
+
1913
+ return await this.evaluateExpression(resultExpr);
1914
+ }
1915
+ }
1916
+
1917
+ // No match
1918
+ if (expr.default) {
1919
+ return await this.evaluateExpression(expr.default);
1920
+ }
1921
+
1922
+ throw new Error('Нет совпадения в match');
1923
+ }
1924
+
1925
+ /**
1926
+ * Match pattern
1927
+ */
1928
+ matchPattern(pattern, value) {
1929
+ if (!pattern) return false;
1930
+
1931
+ switch (pattern.type) {
1932
+ case 'Identifier':
1933
+ // Wildcard match
1934
+ if (pattern.name === '_') return true;
1935
+ // Variable bind
1936
+ this.currentEnv.define(pattern.name, VladXObject.fromJS(value));
1937
+ return true;
1938
+
1939
+ case 'Literal':
1940
+ return pattern.value === value;
1941
+
1942
+ case 'ArrayPattern':
1943
+ if (!Array.isArray(value)) return false;
1944
+ if (pattern.elements.length !== value.length) return false;
1945
+
1946
+ for (let i = 0; i < pattern.elements.length; i++) {
1947
+ if (!this.matchPattern(pattern.elements[i], value[i])) {
1948
+ return false;
1949
+ }
1950
+ }
1951
+ return true;
1952
+
1953
+ case 'ObjectPattern':
1954
+ if (typeof value !== 'object' || value === null) return false;
1955
+
1956
+ for (const prop of pattern.properties) {
1957
+ const key = prop.key?.value || prop.key?.name;
1958
+ if (!(key in value)) return false;
1959
+
1960
+ if (!this.matchPattern(prop.value, value[key])) {
1961
+ return false;
1962
+ }
1963
+ }
1964
+ return true;
1965
+
1966
+ default:
1967
+ return false;
1968
+ }
1969
+ }
1970
+
1971
+ // Evaluate cases
1972
+ for (const caseStmt of stmt.cases) {
1973
+ if (!matched) {
1974
+ const caseValue = await this.evaluateExpression(caseStmt.test);
1975
+ const caseValueValue = caseValue && caseValue.value !== undefined ? caseValue.value : caseValue;
1976
+
1977
+ // Strict equality comparison
1978
+ if (caseValueValue === discriminantValue) {
1979
+ matched = true;
1980
+ }
1981
+ }
1982
+
1983
+ if (matched) {
1984
+ // Execute case body
1985
+ for (const statement of caseStmt.consequent) {
1986
+ try {
1987
+ result = await this.evaluateStatement(statement);
1988
+
1989
+ // If we encounter a return, exit the switch
1990
+ if (this.isReturn) {
1991
+ return result;
1992
+ }
1993
+ } catch (e) {
1994
+ // If it's a break statement, exit the entire switch
1995
+ if (e.message === 'break') {
1996
+ return result; // Exit the entire switch
1997
+ } else {
1998
+ // Re-throw other errors
1999
+ throw e;
2000
+ }
2001
+ }
2002
+ }
2003
+ }
2004
+ }
2005
+
2006
+ // If no case matched and there's a default case
2007
+ if (!matched && stmt.defaultCase) {
2008
+ for (const statement of stmt.defaultCase.consequent) {
2009
+ try {
2010
+ result = await this.evaluateStatement(statement);
2011
+
2012
+ // If we encounter a return, exit the switch
2013
+ if (this.isReturn) {
2014
+ return result;
2015
+ }
2016
+ } catch (e) {
2017
+ // If it's a break statement, exit the entire switch
2018
+ if (e.message === 'break') {
2019
+ return result; // Exit the entire switch
2020
+ } else {
2021
+ // Re-throw other errors
2022
+ throw e;
2023
+ }
2024
+ }
2025
+ }
2026
+ }
2027
+
2028
+ return result;
2029
+ }
2030
+
2031
+ /**
2032
+ * Вычисление выражения (обёртка)
2033
+ */
2034
+ async evaluate(expr) {
2035
+ if (expr === null || expr === undefined) {
2036
+ return VladXObject.null();
2037
+ }
2038
+
2039
+ try {
2040
+ return await this.evaluateExpression(expr);
2041
+ } catch (e) {
2042
+ let errorMessage = e.message;
2043
+ if (!errorMessage) {
2044
+ errorMessage = `Ошибка типа: ${e.constructor.name}`;
2045
+ if (e.stack) {
2046
+ errorMessage += `\nСтек вызовов: ${e.stack}`;
2047
+ } else {
2048
+ errorMessage += ' (Стек вызовов недоступен)';
2049
+ }
2050
+ }
2051
+ throw new Error(errorMessage);
2052
+ }
2053
+ }
2054
+
2055
+ /**
2056
+ * Очистка состояния после выполнения
2057
+ */
2058
+ cleanup() {
2059
+ this.callStack = [];
2060
+ this.isReturn = false;
2061
+ this.returnValue = null;
2062
+ this.errorHandler = null;
2063
+ }
2064
+
2065
+ /**
2066
+ * Обёртка ошибки
2067
+ */
2068
+ wrapError(error) {
2069
+ if (error.message === 'break' || error.message === 'continue') {
2070
+ return error;
2071
+ }
2072
+
2073
+ let errorMessage = error.toString ? error.toString() : String(error);
2074
+ if (!errorMessage) {
2075
+ // Если у ошибки нет сообщения, попробуем получить более подробную информацию
2076
+ errorMessage = `Ошибка типа: ${error.constructor.name}`;
2077
+ if (error.stack) {
2078
+ errorMessage += `\nСтек вызовов: ${error.stack}`;
2079
+ } else {
2080
+ errorMessage += ' (Стек вызовов недоступен)';
2081
+ }
2082
+ }
2083
+ const stackTrace = this.callStack.length > 0 ? `Стек вызовов: ${this.callStack.join(' -> ')}` : 'Стек вызовов пуст';
2084
+ return new Error(
2085
+ `[${this.currentFilename}] Ошибка выполнения: ${errorMessage}\n` +
2086
+ stackTrace
2087
+ );
2088
+ }
2089
+
2090
+ /**
2091
+ * Выполнение catch
2092
+ */
2093
+ async executeCatch(handler, error) {
2094
+ if (!handler) return;
2095
+
2096
+ const catchEnv = this.currentEnv.child('<catch>');
2097
+ if (handler.param) {
2098
+ catchEnv.define(handler.param, VladXObject.string(error.toString ? error.toString() : String(error)));
2099
+ }
2100
+
2101
+ const previousEnv = this.currentEnv;
2102
+ this.currentEnv = catchEnv;
2103
+
2104
+ if (handler.body && handler.body.body) {
2105
+ for (const statement of handler.body.body) {
2106
+ await this.evaluateStatement(statement);
2107
+ }
2108
+ }
2109
+
2110
+ this.currentEnv = previousEnv;
2111
+ }
2112
+ }
2113
+
2114
+ export default Interpreter;