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,374 @@
1
+ /**
2
+ * Linter — Линтер для кода VladX
3
+ */
4
+
5
+ import { Lexer } from '../lexer/lexer.js';
6
+ import { Parser } from '../parser/parser.js';
7
+
8
+ export class Linter {
9
+ constructor(options = {}) {
10
+ this.rules = new Map();
11
+ this.config = options.config || {};
12
+ this.severity = options.severity || 'error'; // error, warning, info
13
+ this.autoFix = options.autoFix || false;
14
+
15
+ this.registerDefaultRules();
16
+ }
17
+
18
+ /**
19
+ * Добавить правило
20
+ */
21
+ addRule(name, rule) {
22
+ this.rules.set(name, rule);
23
+ return this;
24
+ }
25
+
26
+ /**
27
+ * Удалить правило
28
+ */
29
+ removeRule(name) {
30
+ return this.rules.delete(name);
31
+ }
32
+
33
+ /**
34
+ * Проверить файл
35
+ */
36
+ lint(source, filename = '<anonymous>') {
37
+ const results = [];
38
+
39
+ // Лексический анализ
40
+ const lexer = new Lexer(source, filename);
41
+ const tokens = lexer.tokenize();
42
+
43
+ // Синтаксический анализ
44
+ const parser = new Parser(tokens);
45
+ const ast = parser.parse();
46
+
47
+ // Применить правила
48
+ for (const [ruleName, rule] of this.rules) {
49
+ if (rule.checkTokens) {
50
+ const tokenResults = rule.checkTokens(tokens, filename);
51
+ results.push(...tokenResults.map(r => ({ ...r, rule: ruleName })));
52
+ }
53
+
54
+ if (rule.checkAST) {
55
+ const astResults = rule.checkAST(ast, filename);
56
+ results.push(...astResults.map(r => ({ ...r, rule: ruleName })));
57
+ }
58
+
59
+ if (rule.checkSource) {
60
+ const sourceResults = rule.checkSource(source, filename);
61
+ results.push(...sourceResults.map(r => ({ ...r, rule: ruleName })));
62
+ }
63
+ }
64
+
65
+ return {
66
+ errors: results.filter(r => r.severity === 'error'),
67
+ warnings: results.filter(r => r.severity === 'warning'),
68
+ info: results.filter(r => r.severity === 'info'),
69
+ all: results,
70
+ hasErrors: results.some(r => r.severity === 'error')
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Автофикс проблем
76
+ */
77
+ fix(source, filename = '<anonymous>') {
78
+ const results = this.lint(source, filename);
79
+ let fixedSource = source;
80
+
81
+ if (!this.autoFix) {
82
+ return {
83
+ source,
84
+ results,
85
+ fixed: false
86
+ };
87
+ }
88
+
89
+ for (const [ruleName, rule] of this.rules) {
90
+ if (rule.fix && results.some(r => r.rule === ruleName)) {
91
+ fixedSource = rule.fix(fixedSource, filename);
92
+ }
93
+ }
94
+
95
+ const newResults = this.lint(fixedSource, filename);
96
+
97
+ return {
98
+ source: fixedSource,
99
+ originalResults: results,
100
+ results: newResults,
101
+ fixed: newResults.all.length < results.all.length
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Зарегистрировать правила по умолчанию
107
+ */
108
+ registerDefaultRules() {
109
+ // Правило: unused variables
110
+ this.addRule('no-unused-vars', {
111
+ checkAST: (ast, filename) => {
112
+ const results = [];
113
+ const usedVars = new Set();
114
+ const declaredVars = new Set();
115
+
116
+ const traverse = (node) => {
117
+ if (!node) return;
118
+
119
+ if (node.type === 'LetStatement' || node.type === 'ConstStatement') {
120
+ declaredVars.add(node.name);
121
+ }
122
+
123
+ if (node.type === 'Identifier') {
124
+ usedVars.add(node.name);
125
+ }
126
+
127
+ for (const key in node) {
128
+ if (Array.isArray(node[key])) {
129
+ node[key].forEach(traverse);
130
+ } else if (typeof node[key] === 'object') {
131
+ traverse(node[key]);
132
+ }
133
+ }
134
+ };
135
+
136
+ traverse(ast);
137
+
138
+ for (const varName of declaredVars) {
139
+ if (!usedVars.has(varName)) {
140
+ results.push({
141
+ message: `Переменная "${varName}" не используется`,
142
+ line: ast.body?.[0]?.line || 0,
143
+ column: 0,
144
+ severity: 'warning',
145
+ filename
146
+ });
147
+ }
148
+ }
149
+
150
+ return results;
151
+ }
152
+ });
153
+
154
+ // Правило: console.log в production
155
+ this.addRule('no-console', {
156
+ checkAST: (ast, filename) => {
157
+ const results = [];
158
+
159
+ const traverse = (node) => {
160
+ if (!node) return;
161
+
162
+ if (node.type === 'CallExpression') {
163
+ if (node.callee === 'печать' || node.callee === 'console.log') {
164
+ results.push({
165
+ message: 'Не используйте console.log в production коде',
166
+ line: node.line || 0,
167
+ column: 0,
168
+ severity: 'warning',
169
+ filename
170
+ });
171
+ }
172
+ }
173
+
174
+ for (const key in node) {
175
+ if (Array.isArray(node[key])) {
176
+ node[key].forEach(traverse);
177
+ } else if (typeof node[key] === 'object') {
178
+ traverse(node[key]);
179
+ }
180
+ }
181
+ };
182
+
183
+ traverse(ast);
184
+ return results;
185
+ }
186
+ });
187
+
188
+ // Правило: empty blocks
189
+ this.addRule('no-empty-blocks', {
190
+ checkAST: (ast, filename) => {
191
+ const results = [];
192
+
193
+ const traverse = (node) => {
194
+ if (!node) return;
195
+
196
+ if (node.type === 'IfStatement' || node.type === 'WhileStatement') {
197
+ const body = node.thenBranch?.body || node.body;
198
+ if (body && body.length === 0) {
199
+ results.push({
200
+ message: 'Пустой блок кода',
201
+ line: node.line || 0,
202
+ column: 0,
203
+ severity: 'warning',
204
+ filename
205
+ });
206
+ }
207
+ }
208
+
209
+ for (const key in node) {
210
+ if (Array.isArray(node[key])) {
211
+ node[key].forEach(traverse);
212
+ } else if (typeof node[key] === 'object') {
213
+ traverse(node[key]);
214
+ }
215
+ }
216
+ };
217
+
218
+ traverse(ast);
219
+ return results;
220
+ },
221
+ fix: (source) => {
222
+ return source.replace(/(?:если|if)\s*\([^)]*\)\s*{\s*}/g, '');
223
+ }
224
+ });
225
+
226
+ // Правило: trailing whitespace
227
+ this.addRule('no-trailing-whitespace', {
228
+ checkSource: (source, filename) => {
229
+ const results = [];
230
+ const lines = source.split('\n');
231
+
232
+ lines.forEach((line, index) => {
233
+ if (line !== line.trimEnd()) {
234
+ results.push({
235
+ message: 'Trailing whitespace в конце строки',
236
+ line: index + 1,
237
+ column: line.length,
238
+ severity: 'info',
239
+ filename
240
+ });
241
+ }
242
+ });
243
+
244
+ return results;
245
+ },
246
+ fix: (source) => {
247
+ return source.split('\n').map(line => line.trimEnd()).join('\n');
248
+ }
249
+ });
250
+
251
+ // Правило: line length
252
+ this.addRule('max-line-length', {
253
+ checkSource: (source, filename) => {
254
+ const results = [];
255
+ const maxLength = this.config.maxLineLength || 100;
256
+ const lines = source.split('\n');
257
+
258
+ lines.forEach((line, index) => {
259
+ if (line.length > maxLength) {
260
+ results.push({
261
+ message: `Строка слишком длинная: ${line.length} > ${maxLength}`,
262
+ line: index + 1,
263
+ column: maxLength,
264
+ severity: 'warning',
265
+ filename
266
+ });
267
+ }
268
+ });
269
+
270
+ return results;
271
+ }
272
+ });
273
+
274
+ // Правило: no var
275
+ this.addRule('no-var', {
276
+ checkTokens: (tokens, filename) => {
277
+ const results = [];
278
+
279
+ tokens.forEach(token => {
280
+ if (token.type === 'VAR' || token.value === 'переменная') {
281
+ results.push({
282
+ message: 'Используйте "пусть" или "константа" вместо "переменная"',
283
+ line: token.line,
284
+ column: token.column,
285
+ severity: 'error',
286
+ filename
287
+ });
288
+ }
289
+ });
290
+
291
+ return results;
292
+ }
293
+ });
294
+
295
+ // Правило: curly braces
296
+ this.addRule('curly', {
297
+ checkAST: (ast, filename) => {
298
+ const results = [];
299
+
300
+ const traverse = (node) => {
301
+ if (!node) return;
302
+
303
+ if (node.type === 'IfStatement') {
304
+ const thenBranch = node.thenBranch;
305
+ if (thenBranch && thenBranch.type !== 'BlockStatement') {
306
+ results.push({
307
+ message: 'Используйте фигурные скобки для if блоков',
308
+ line: node.line || 0,
309
+ column: 0,
310
+ severity: 'error',
311
+ filename
312
+ });
313
+ }
314
+ }
315
+
316
+ for (const key in node) {
317
+ if (Array.isArray(node[key])) {
318
+ node[key].forEach(traverse);
319
+ } else if (typeof node[key] === 'object') {
320
+ traverse(node[key]);
321
+ }
322
+ }
323
+ };
324
+
325
+ traverse(ast);
326
+ return results;
327
+ }
328
+ });
329
+ }
330
+
331
+ /**
332
+ * Получить результаты в формате JSON
333
+ */
334
+ getResultsJSON(lintResults) {
335
+ return JSON.stringify(lintResults, null, 2);
336
+ }
337
+
338
+ /**
339
+ * Получить результаты в формате JUnit
340
+ */
341
+ getResultsJUnit(lintResults) {
342
+ const errors = lintResults.errors;
343
+ const warnings = lintResults.warnings;
344
+
345
+ let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
346
+ xml += `<testsuites errors="${errors.length}" failures="0" tests="${errors.length + warnings.length}">\n`;
347
+ xml += ' <testsuite name="VladX Linter">\n';
348
+
349
+ for (const error of errors) {
350
+ xml += ` <testcase name="${error.rule}">\n`;
351
+ xml += ` <error message="${error.message}" line="${error.line}"/>\n`;
352
+ xml += ' </testcase>\n';
353
+ }
354
+
355
+ for (const warning of warnings) {
356
+ xml += ` <testcase name="${warning.rule}"/>\n`;
357
+ }
358
+
359
+ xml += ' </testsuite>\n';
360
+ xml += '</testsuites>';
361
+
362
+ return xml;
363
+ }
364
+
365
+ /**
366
+ * Очистить правила
367
+ */
368
+ clearRules() {
369
+ this.rules.clear();
370
+ return this;
371
+ }
372
+ }
373
+
374
+ export default Linter;
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Logging — Система логирования
3
+ */
4
+
5
+ import { existsSync, writeFileSync, appendFileSync, mkdirSync } from 'fs';
6
+ import { dirname } from 'path';
7
+
8
+ export class Logging {
9
+ constructor(options = {}) {
10
+ this.level = options.level || 'info'; // debug, info, warn, error
11
+ this.format = options.format || 'text'; // text, json
12
+ this.file = options.file || null;
13
+ this.console = options.console !== false;
14
+ this.colors = options.colors !== false;
15
+ this.timestamp = options.timestamp !== false;
16
+
17
+ this.levels = {
18
+ debug: 0,
19
+ info: 1,
20
+ warn: 2,
21
+ error: 3
22
+ };
23
+
24
+ this.colorsMap = {
25
+ debug: '\x1b[36m', // cyan
26
+ info: '\x1b[32m', // green
27
+ warn: '\x1b[33m', // yellow
28
+ error: '\x1b[31m', // red
29
+ reset: '\x1b[0m'
30
+ };
31
+
32
+ this.logBuffer = [];
33
+ this.maxBufferSize = options.maxBufferSize || 1000;
34
+ }
35
+
36
+ /**
37
+ * Логирование
38
+ */
39
+ log(level, message, context = {}) {
40
+ if (this.levels[level] < this.levels[this.level]) {
41
+ return;
42
+ }
43
+
44
+ const logEntry = {
45
+ level,
46
+ message,
47
+ context,
48
+ timestamp: this.timestamp ? new Date().toISOString() : undefined
49
+ };
50
+
51
+ this.logBuffer.push(logEntry);
52
+
53
+ if (this.logBuffer.length > this.maxBufferSize) {
54
+ this.logBuffer.shift();
55
+ }
56
+
57
+ const formatted = this.formatLogEntry(logEntry);
58
+
59
+ if (this.console) {
60
+ console.log(formatted);
61
+ }
62
+
63
+ if (this.file) {
64
+ this.writeToFile(formatted);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Format log entry
70
+ */
71
+ formatLogEntry(entry) {
72
+ if (this.format === 'json') {
73
+ return JSON.stringify(entry);
74
+ }
75
+
76
+ let output = '';
77
+
78
+ // Timestamp
79
+ if (entry.timestamp) {
80
+ output += `[${entry.timestamp}] `;
81
+ }
82
+
83
+ // Level
84
+ const levelUpper = entry.level.toUpperCase();
85
+ if (this.colors) {
86
+ output += `${this.colorsMap[entry.level]}${levelUpper}${this.colorsMap.reset} `;
87
+ } else {
88
+ output += `${levelUpper} `;
89
+ }
90
+
91
+ // Message
92
+ output += entry.message;
93
+
94
+ // Context
95
+ if (Object.keys(entry.context).length > 0) {
96
+ output += ` ${JSON.stringify(entry.context)}`;
97
+ }
98
+
99
+ return output;
100
+ }
101
+
102
+ /**
103
+ * Записать в файл
104
+ */
105
+ writeToFile(formatted) {
106
+ try {
107
+ if (!existsSync(this.file)) {
108
+ mkdirSync(dirname(this.file), { recursive: true });
109
+ }
110
+ appendFileSync(this.file, formatted + '\n', 'utf-8');
111
+ } catch (error) {
112
+ console.error('Ошибка записи лога:', error);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Debug
118
+ */
119
+ debug(message, context) {
120
+ this.log('debug', message, context);
121
+ }
122
+
123
+ /**
124
+ * Info
125
+ */
126
+ info(message, context) {
127
+ this.log('info', message, context);
128
+ }
129
+
130
+ /**
131
+ * Warn
132
+ */
133
+ warn(message, context) {
134
+ this.log('warn', message, context);
135
+ }
136
+
137
+ /**
138
+ * Error
139
+ */
140
+ error(message, context) {
141
+ this.log('error', message, context);
142
+ }
143
+
144
+ /**
145
+ * Создать child logger с контекстом
146
+ */
147
+ child(context) {
148
+ const child = new Logging({
149
+ level: this.level,
150
+ format: this.format,
151
+ file: this.file,
152
+ console: this.console,
153
+ colors: this.colors,
154
+ timestamp: this.timestamp
155
+ });
156
+
157
+ child.defaultContext = { ...this.defaultContext, ...context };
158
+
159
+ const originalLog = child.log.bind(child);
160
+ child.log = (level, message, context) => {
161
+ originalLog(level, message, { ...this.defaultContext, ...context });
162
+ };
163
+
164
+ return child;
165
+ }
166
+
167
+ /**
168
+ * Создать logger с уровнем
169
+ */
170
+ withLevel(level) {
171
+ const logger = new Logging({
172
+ level,
173
+ format: this.format,
174
+ file: this.file,
175
+ console: this.console,
176
+ colors: this.colors,
177
+ timestamp: this.timestamp
178
+ });
179
+
180
+ return logger;
181
+ }
182
+
183
+ /**
184
+ * Установить уровень логирования
185
+ */
186
+ setLevel(level) {
187
+ this.level = level;
188
+ return this;
189
+ }
190
+
191
+ /**
192
+ * Получить буфер логов
193
+ */
194
+ getLogs() {
195
+ return [...this.logBuffer];
196
+ }
197
+
198
+ /**
199
+ * Очистить буфер логов
200
+ */
201
+ clearLogs() {
202
+ this.logBuffer = [];
203
+ return this;
204
+ }
205
+
206
+ /**
207
+ * Экспорт логов
208
+ */
209
+ exportLogs(format = 'json') {
210
+ if (format === 'json') {
211
+ return JSON.stringify(this.logBuffer, null, 2);
212
+ } else {
213
+ return this.logBuffer.map(entry => this.formatLogEntry(entry)).join('\n');
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Фильтр логов по уровню
219
+ */
220
+ filterLogs(level) {
221
+ const minLevel = this.levels[level];
222
+ return this.logBuffer.filter(entry => this.levels[entry.level] >= minLevel);
223
+ }
224
+
225
+ /**
226
+ * Фильтр логов по времени
227
+ */
228
+ filterLogsByTime(startTime, endTime) {
229
+ return this.logBuffer.filter(entry => {
230
+ if (!entry.timestamp) return true;
231
+ const time = new Date(entry.timestamp).getTime();
232
+ return time >= startTime.getTime() && time <= endTime.getTime();
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Создать logger с метриками
238
+ */
239
+ static withMetrics(logger) {
240
+ const metrics = {
241
+ debug: 0,
242
+ info: 0,
243
+ warn: 0,
244
+ error: 0
245
+ };
246
+
247
+ const originalLog = logger.log.bind(logger);
248
+ logger.log = (level, message, context) => {
249
+ metrics[level]++;
250
+ originalLog(level, message, context);
251
+ };
252
+
253
+ logger.getMetrics = () => metrics;
254
+
255
+ return logger;
256
+ }
257
+
258
+ /**
259
+ * Создать logger с ротацией файлов
260
+ */
261
+ static withFileRotation(logger, options = {}) {
262
+ const maxSize = options.maxSize || 1024 * 1024; // 1MB
263
+ const maxFiles = options.maxFiles || 5;
264
+
265
+ const checkAndRotate = () => {
266
+ if (!logger.file) return;
267
+
268
+ try {
269
+ const stats = require('fs').statSync(logger.file);
270
+ if (stats.size > maxSize) {
271
+ // Ротация файлов
272
+ for (let i = maxFiles - 1; i >= 1; i--) {
273
+ const oldFile = i === 1 ? logger.file : `${logger.file}.${i - 1}`;
274
+ const newFile = `${logger.file}.${i}`;
275
+
276
+ try {
277
+ require('fs').renameSync(oldFile, newFile);
278
+ } catch (e) {}
279
+ }
280
+
281
+ // Создать новый файл
282
+ require('fs').writeFileSync(logger.file, '', 'utf-8');
283
+ }
284
+ } catch (e) {}
285
+ };
286
+
287
+ // Перехватить writeToFile
288
+ const originalWrite = logger.writeToFile.bind(logger);
289
+ logger.writeToFile = (formatted) => {
290
+ checkAndRotate();
291
+ originalWrite(formatted);
292
+ };
293
+
294
+ return logger;
295
+ }
296
+
297
+ /**
298
+ * Создать logger с цветным выводом
299
+ */
300
+ static withColors(logger) {
301
+ logger.colors = true;
302
+ return logger;
303
+ }
304
+
305
+ /**
306
+ * Создать logger без цветов
307
+ */
308
+ static withoutColors(logger) {
309
+ logger.colors = false;
310
+ return logger;
311
+ }
312
+ }
313
+
314
+ export default Logging;