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
package/bin/vlad.js ADDED
@@ -0,0 +1,539 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * VladX CLI — Производственная версия, написанная с нуля
5
+ * Максимум функций, максимум стабильности, ноль утечек
6
+ */
7
+ // Принудительно включаем цвета в терминале
8
+ process.env.FORCE_COLOR = '1';
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import os from 'os';
12
+ import { fileURLToPath } from 'url';
13
+ import { VladXEngine } from '../src/engine/vladx-engine.js';
14
+
15
+ // Создаём экземпляр движка
16
+ let engine = new VladXEngine();
17
+
18
+ // ==================== КОНСТАНТЫ ====================
19
+ import pkg from '../package.json' assert { type: 'json' };
20
+ const VERSION = pkg.version;
21
+
22
+ const HISTORY_PATH = path.join(os.homedir(), '.vladx', 'repl-history.txt');
23
+ const MAX_HISTORY = 1000;
24
+ const MAX_BUFFER_SIZE = 50000; // Ограничение на размер буфера (защита от OOM)
25
+ const MAX_EXECUTION_TIME = 30000; // Ограничение времени выполнения
26
+
27
+ // ==================== УТИЛИТЫ ПАРСИНГА АРГУМЕНТОВ ====================
28
+ function parseCommandLine(argv) {
29
+ const args = argv.slice(2);
30
+ const options = {
31
+ mode: 'repl', // 'repl', 'file', 'eval', 'compile'
32
+ file: null,
33
+ evalCode: null,
34
+ outputPath: null,
35
+ debug: false,
36
+ timeout: MAX_EXECUTION_TIME,
37
+ help: false,
38
+ version: false,
39
+ programArgs: []
40
+ };
41
+
42
+ for (let i = 0; i < args.length; i++) {
43
+ const arg = args[i];
44
+
45
+ if (arg === '-h' || arg === '--help') {
46
+ options.help = true;
47
+ continue;
48
+ }
49
+
50
+ if (arg === '-v' || arg === '--version') {
51
+ options.version = true;
52
+ continue;
53
+ }
54
+
55
+ if (arg === '-r' || arg === '--repl') {
56
+ options.mode = 'repl';
57
+ continue;
58
+ }
59
+
60
+ if (arg === '-e' || arg === '--eval') {
61
+ options.mode = 'eval';
62
+ options.evalCode = args[++i];
63
+ continue;
64
+ }
65
+
66
+ if (arg === '-c' || arg === '--compile') {
67
+ options.mode = 'compile';
68
+ options.file = args[++i];
69
+ continue;
70
+ }
71
+
72
+ if (arg === '-o' || arg === '--output') {
73
+ options.outputPath = args[++i];
74
+ continue;
75
+ }
76
+
77
+ if (arg === '--debug') {
78
+ options.debug = true;
79
+ continue;
80
+ }
81
+
82
+ if (arg === '--no-timeout') {
83
+ options.timeout = 0;
84
+ continue;
85
+ }
86
+
87
+ if (arg === '--stack-size') {
88
+ i++; // Пропускаем значение
89
+ continue;
90
+ }
91
+
92
+ // Файл для запуска
93
+ if (!options.file && !arg.startsWith('-')) {
94
+ const ext = path.extname(arg);
95
+ if (['.vx', '.vladx'].includes(ext)) {
96
+ options.file = arg;
97
+ if (options.mode === 'repl') options.mode = 'file';
98
+ } else {
99
+ options.programArgs.push(arg);
100
+ }
101
+ } else {
102
+ options.programArgs.push(arg);
103
+ }
104
+ }
105
+
106
+ return options;
107
+ }
108
+
109
+ // ==================== УТИЛИТЫ ИСТОРИИ ====================
110
+ function loadHistory() {
111
+ try {
112
+ if (fs.existsSync(HISTORY_PATH)) {
113
+ return fs.readFileSync(HISTORY_PATH, 'utf-8')
114
+ .split('\n')
115
+ .filter(l => l.trim())
116
+ .slice(-MAX_HISTORY);
117
+ }
118
+ } catch (error) {
119
+ // Игнорируем ошибки загрузки истории
120
+ }
121
+ return [];
122
+ }
123
+
124
+ function saveHistory(history) {
125
+ try {
126
+ ensureDir(path.dirname(HISTORY_PATH));
127
+ const cleaned = [...new Set(history.filter(l => l && l.trim()))].slice(-MAX_HISTORY);
128
+ fs.writeFileSync(HISTORY_PATH, cleaned.join('\n'));
129
+ } catch (error) {
130
+ // Игнорируем ошибки сохранения истории
131
+ }
132
+ }
133
+
134
+ function ensureDir(dir) {
135
+ if (!fs.existsSync(dir)) {
136
+ fs.mkdirSync(dir, { recursive: true });
137
+ }
138
+ }
139
+
140
+ // ==================== КЛАСС REPL (СЕРДЦЕ СИСТЕМЫ) ====================
141
+ class VladXRepl {
142
+ constructor(engine, options = {}) {
143
+ this.engine = engine;
144
+ this.debug = options.debug || false;
145
+ this.reset();
146
+ }
147
+
148
+ reset() {
149
+ this.buffer = '';
150
+ this.depth = { braces: 0, parens: 0, brackets: 0 };
151
+ this.inString = false;
152
+ this.stringChar = null;
153
+ this.escapeNext = false;
154
+ this.isMultiline = false;
155
+ this.executionCount = 0;
156
+ // Не очищаем history - readline internally хранит её
157
+ // this.history = [];
158
+ }
159
+
160
+ /**
161
+ * Проверка, завершен ли ввод кода
162
+ */
163
+ isCodeComplete() {
164
+ // Быстрая проверка: все скобки закрыты и нет незакрытой строки
165
+ const complete = this.depth.braces === 0 &&
166
+ this.depth.parens === 0 &&
167
+ this.depth.brackets === 0 &&
168
+ !this.inString;
169
+
170
+ // Дополнительная проверка: буфер не должен быть пустым
171
+ if (complete && this.buffer.trim().length === 0) {
172
+ return false;
173
+ }
174
+
175
+ return complete;
176
+ }
177
+
178
+ /**
179
+ * Анализ строки на предмет скобок и строк
180
+ */
181
+ analyzeLine(line) {
182
+ // Предварительная проверка на максимальный размер
183
+ if (this.buffer.length + line.length > MAX_BUFFER_SIZE) {
184
+ throw new Error(`Превышен максимальный размер буфера: ${MAX_BUFFER_SIZE} символов`);
185
+ }
186
+
187
+ for (let i = 0; i < line.length; i++) {
188
+ const char = line[i];
189
+
190
+ // Обработка escape-последовательностей
191
+ if (this.escapeNext) {
192
+ this.escapeNext = false;
193
+ continue;
194
+ }
195
+
196
+ if (char === '\\') {
197
+ this.escapeNext = true;
198
+ continue;
199
+ }
200
+
201
+ // Строковые литералы
202
+ if (!this.inString && (char === '"' || char === "'" || char === '`')) {
203
+ this.inString = true;
204
+ this.stringChar = char;
205
+ } else if (this.inString && char === this.stringChar) {
206
+ this.inString = false;
207
+ this.stringChar = null;
208
+ }
209
+
210
+ // Скобки (только вне строк)
211
+ if (!this.inString) {
212
+ switch (char) {
213
+ case '{': this.depth.braces++; break;
214
+ case '(': this.depth.parens++; break;
215
+ case '[': this.depth.brackets++; break;
216
+ case '}': this.depth.braces = Math.max(0, this.depth.braces - 1); break;
217
+ case ')': this.depth.parens = Math.max(0, this.depth.parens - 1); break;
218
+ case ']': this.depth.brackets = Math.max(0, this.depth.brackets - 1); break;
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Получить текущий промпт
226
+ */
227
+ getPrompt() {
228
+ if (!this.isMultiline) {
229
+ return '\x1b[36mvladx\x1b[0m \x1b[33m»\x1b[0m ';
230
+ }
231
+
232
+ const level = this.depth.braces + this.depth.parens + this.depth.brackets;
233
+ const dots = '··'.repeat(Math.max(1, level));
234
+ return `\x1b[90m${dots}\x1b[0m `;
235
+ }
236
+
237
+ /**
238
+ * Запуск REPL
239
+ */
240
+ async start() {
241
+ const readline = await import('readline');
242
+ this.history = loadHistory();
243
+
244
+ this.rl = readline.createInterface({
245
+ input: process.stdin,
246
+ output: process.stdout,
247
+ completer: (line) => {
248
+ const hits = this.history.filter(h => h.startsWith(line));
249
+ if (hits.length > 0) {
250
+ return [hits, line];
251
+ }
252
+ return [[], line];
253
+ },
254
+ history: this.history,
255
+ historySize: MAX_HISTORY
256
+ });
257
+
258
+ this.rl.on('line', this.handleLine.bind(this));
259
+ this.rl.on('close', this.onClose.bind(this));
260
+ this.rl.on('SIGINT', this.onSigint.bind(this));
261
+
262
+ console.log(`\x1b[32mVladX v${VERSION}\x1b[0m — Интерпретируемый язык программирования с русским синтаксисом`);
263
+ console.log('Введите "помощь" для получения справки или "выход" для выхода.\n');
264
+
265
+ this.rl.setPrompt(this.getPrompt());
266
+ this.rl.prompt();
267
+ }
268
+
269
+ /**
270
+ * Обработка строки ввода
271
+ */
272
+ async handleLine(line) {
273
+ const trimmed = line.trim();
274
+
275
+ // Специальные команды (только в начале)
276
+ if (!this.buffer) {
277
+ if (trimmed === 'выход' || trimmed === 'exit' || trimmed === 'quit') {
278
+ return this.exit();
279
+ }
280
+
281
+ if (trimmed === 'помощь' || trimmed === 'help') {
282
+ console.log(`\nКоманды REPL:
283
+ помощь - показать справку
284
+ выход - выйти из REPL
285
+ .clear - очистить буфер многострочного ввода
286
+ Ctrl+C - прервать ввод или выйти
287
+
288
+ Синтаксис:
289
+ пусть x = 5
290
+ объект = { имя: "Влад", возраст: 25 }
291
+ функция привет(имя) { вернуть "Привет, " + имя }
292
+ класс Человек { конструктор(имя) { это.имя = имя } }
293
+ если (x > 0) { печать("положительно") } иначе { печать("отрицательно") }
294
+ пока (i < 10) { i = i + 1 }
295
+ для (пусть i = 0; i < 5; i = i + 1) { печать(i) }
296
+ `);
297
+ this.rl.prompt();
298
+ return;
299
+ }
300
+ }
301
+
302
+ if (trimmed === '.clear') {
303
+ const wasMultiline = this.isMultiline;
304
+ this.reset();
305
+
306
+ if (wasMultiline) {
307
+ console.log('\x1b[90mМногострочный ввод очищен\x1b[0m');
308
+ } else {
309
+ console.log('\x1b[90mБуфер очищен\x1b[0m');
310
+ }
311
+ this.rl.setPrompt('\x1b[36mvladx\x1b[0m \x1b[33m»\x1b[0m ');
312
+ this.rl.prompt();
313
+ return;
314
+ }
315
+
316
+ // Анализируем строку
317
+ try {
318
+ this.analyzeLine(line);
319
+ } catch (e) {
320
+ console.error('\n\x1b[31m❌ Ошибка:\x1b[0m', e.message);
321
+ this.reset();
322
+ this.rl.setPrompt('\x1b[36mvladx\x1b[0m \x1b[33m»\x1b[0m ');
323
+ this.rl.prompt();
324
+ return;
325
+ }
326
+
327
+ // Добавляем в буфер
328
+ this.buffer += line + '\n';
329
+
330
+ // Проверяем, завершен ли код
331
+ if (this.isCodeComplete()) {
332
+ if (this.buffer.trim()) {
333
+ // Выполняем
334
+ try {
335
+ this.executionCount++;
336
+ const result = await this.engine.execute(this.buffer, { filename: `<repl:${this.executionCount}>` });
337
+
338
+ if (result && result.type !== 'null' && result.value !== undefined) {
339
+ console.log(' \x1b[90m→\x1b[0m', JSON.stringify(result.value, null, 2));
340
+ }
341
+ } catch (error) {
342
+ console.error('\n\x1b[31m❌ Ошибка:\x1b[0m', error.toString ? error.toString() : String(error));
343
+ }
344
+ }
345
+
346
+ // Сбрасываем состояние
347
+ this.reset();
348
+ this.rl.setPrompt('\x1b[36mvladx\x1b[0m \x1b[33m»\x1b[0m ');
349
+ } else {
350
+ // Продолжаем многострочный ввод
351
+ if (!this.isMultiline) {
352
+ this.isMultiline = true;
353
+ }
354
+ this.rl.setPrompt(this.getPrompt());
355
+ }
356
+
357
+ this.rl.prompt();
358
+ }
359
+
360
+ onClose() {
361
+ saveHistory(this.history);
362
+ console.log('\n\x1b[90mДо встречи!\x1b[0m\n');
363
+ process.exit(0);
364
+ }
365
+
366
+ onSigint() {
367
+ if (this.buffer) {
368
+ console.log('\n\x1b[90m(прервано)\x1b[0m');
369
+ this.reset();
370
+ this.rl.setPrompt('\x1b[36mvladx\x1b[0m \x1b[33m»\x1b[0m ');
371
+ this.rl.prompt();
372
+ } else {
373
+ this.exit();
374
+ }
375
+ }
376
+
377
+ exit() {
378
+ saveHistory(this.history);
379
+ console.log('\n\x1b[90mВыход...\x1b[0m\n');
380
+ process.exit(0);
381
+ }
382
+ }
383
+
384
+ // ==================== ОСТАЛЬНЫЕ РЕЖИМЫ РАБОТЫ ====================
385
+ async function runFile(filepath, args = []) {
386
+ if (!fs.existsSync(filepath)) {
387
+ console.error(`❌ Ошибка: Файл не найден: ${filepath}`);
388
+ process.exit(1);
389
+ }
390
+
391
+ const source = fs.readFileSync(filepath, 'utf-8');
392
+
393
+ try {
394
+ const startTime = Date.now();
395
+ const result = await engine.execute(source, {
396
+ filename: filepath,
397
+ modulePath: path.dirname(filepath)
398
+ });
399
+ const duration = Date.now() - startTime;
400
+
401
+ console.log(`\n\x1b[90mВыполнено за ${duration}мс\x1b[0m`);
402
+ } catch (error) {
403
+ let errorMessage = error.toString ? error.toString() : String(error);
404
+ if (!errorMessage) {
405
+ errorMessage = `Ошибка типа: ${error.constructor.name}`;
406
+ if (error.stack) {
407
+ errorMessage += `\nСтек вызовов: ${error.stack}`;
408
+ } else {
409
+ errorMessage += ' (Стек вызовов недоступен)';
410
+ }
411
+ }
412
+ console.error('\n\x1b[31m❌ Ошибка выполнения:\x1b[0m', errorMessage);
413
+ if (error.stack) {
414
+ console.error('\nСтек вызовов:');
415
+ console.error(error.stack);
416
+ }
417
+ process.exit(1);
418
+ }
419
+ }
420
+
421
+ async function executeCode(code) {
422
+ try {
423
+ await engine.execute(code, {
424
+ filename: '<eval>',
425
+ modulePath: process.cwd()
426
+ });
427
+ } catch (error) {
428
+ let errorMessage = error.toString ? error.toString() : String(error);
429
+ if (!errorMessage) {
430
+ errorMessage = `Ошибка типа: ${error.constructor.name}`;
431
+ if (error.stack) {
432
+ errorMessage += `\nСтек вызовов: ${error.stack}`;
433
+ } else {
434
+ errorMessage += ' (Стек вызовов недоступен)';
435
+ }
436
+ }
437
+ console.error('❌ Ошибка:', errorMessage);
438
+ if (error.stack) {
439
+ console.error('\nСтек вызовов:');
440
+ console.error(error.stack);
441
+ }
442
+ process.exit(1);
443
+ }
444
+ }
445
+
446
+ async function compileFile(inputPath, outputPath = null) {
447
+ if (!fs.existsSync(inputPath)) {
448
+ console.error(`❌ Ошибка: Файл не найден: ${inputPath}`);
449
+ process.exit(1);
450
+ }
451
+
452
+ const source = fs.readFileSync(inputPath, 'utf-8');
453
+
454
+ try {
455
+ const jsCode = engine.compile(source);
456
+
457
+ if (outputPath) {
458
+ fs.writeFileSync(outputPath, jsCode, 'utf-8');
459
+ console.log(`✅ Скомпилировано в: ${outputPath}`);
460
+ } else {
461
+ console.log(jsCode);
462
+ }
463
+ } catch (error) {
464
+ console.error('❌ Ошибка компиляции:', error.toString ? error.toString() : String(error));
465
+ process.exit(1);
466
+ }
467
+ }
468
+
469
+ // ==================== ГЛАВНАЯ ФУНКЦИЯ ====================
470
+ async function main() {
471
+ const options = parseCommandLine(process.argv);
472
+ if (options.version) {
473
+ console.log(`VladX v${VERSION}`);
474
+ process.exit(0);
475
+ }
476
+ if (options.help) {
477
+ console.log(`
478
+ VladX — интерпретируемый язык программирования
479
+
480
+ Использование:
481
+ vlad Запуск REPL
482
+ vlad file.vx Выполнить файл
483
+ vlad -e "код" Выполнить код
484
+ vlad -c file.vx -o out.js Скомпилировать в JS
485
+
486
+ Опции:
487
+ -h, --help Справка
488
+ -v, --version Версия
489
+ --debug Режим отладки
490
+ --no-timeout Отключить лимит времени
491
+ `);
492
+ process.exit(0);
493
+ }
494
+
495
+ // Настройка движка
496
+ engine.debug = options.debug;
497
+ engine.maxExecutionTime = options.timeout;
498
+
499
+ switch (options.mode) {
500
+ case 'eval':
501
+ if (options.mode === 'eval' && !options.evalCode) {
502
+ console.error('❌ Не указан код для --eval');
503
+ process.exit(1);
504
+ }
505
+
506
+ await executeCode(options.evalCode);
507
+ break;
508
+ case 'compile':
509
+ await compileFile(options.file, options.outputPath);
510
+ break;
511
+ case 'file':
512
+ await runFile(options.file, options.programArgs);
513
+ break;
514
+ case 'repl':
515
+ default:
516
+ const repl = new VladXRepl(engine);
517
+ await repl.start();
518
+ break;
519
+ }
520
+ }
521
+
522
+ // ==================== ЗАПУСК ====================
523
+ main().catch(error => {
524
+ let errorMessage = error.toString ? error.toString() : String(error);
525
+ if (!errorMessage) {
526
+ errorMessage = `Ошибка типа: ${error.constructor.name}`;
527
+ if (error.stack) {
528
+ errorMessage += `\nСтек вызовов: ${error.stack}`;
529
+ } else {
530
+ errorMessage += ' (Стек вызовов недоступен)';
531
+ }
532
+ }
533
+ console.error('\n❌ Критическая ошибка:', errorMessage);
534
+ if (error.stack) {
535
+ console.error('\nСтек вызовов:');
536
+ console.error(error.stack);
537
+ }
538
+ process.exit(1);
539
+ });