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.
- package/README.md +256 -0
- package/bin/cli.js +486 -0
- package/bin/vlad.js +539 -0
- package/bin/vladpm.js +710 -0
- package/bin/vladx.js +491 -0
- package/package.json +57 -0
- package/src/engine/jit-compiler.js +285 -0
- package/src/engine/vladx-engine.js +941 -0
- package/src/index.js +44 -0
- package/src/interpreter/interpreter.js +2114 -0
- package/src/lexer/lexer.js +658 -0
- package/src/lexer/optimized-lexer.js +106 -0
- package/src/lexer/regex-cache.js +83 -0
- package/src/parser/ast-nodes.js +472 -0
- package/src/parser/parser.js +1408 -0
- package/src/runtime/advanced-type-system.js +209 -0
- package/src/runtime/async-manager.js +252 -0
- package/src/runtime/builtins.js +143 -0
- package/src/runtime/bundler.js +422 -0
- package/src/runtime/cache-manager.js +126 -0
- package/src/runtime/data-structures.js +612 -0
- package/src/runtime/debugger.js +260 -0
- package/src/runtime/enhanced-module-system.js +196 -0
- package/src/runtime/environment-enhanced.js +272 -0
- package/src/runtime/environment.js +140 -0
- package/src/runtime/event-emitter.js +232 -0
- package/src/runtime/formatter.js +280 -0
- package/src/runtime/functional.js +359 -0
- package/src/runtime/io-operations.js +390 -0
- package/src/runtime/linter.js +374 -0
- package/src/runtime/logging.js +314 -0
- package/src/runtime/minifier.js +242 -0
- package/src/runtime/module-system.js +377 -0
- package/src/runtime/network-operations.js +373 -0
- package/src/runtime/profiler.js +295 -0
- package/src/runtime/repl.js +336 -0
- package/src/runtime/security-manager.js +244 -0
- package/src/runtime/source-map-generator.js +208 -0
- package/src/runtime/test-runner.js +394 -0
- package/src/runtime/transformer.js +277 -0
- package/src/runtime/type-system.js +244 -0
- package/src/runtime/vladx-object.js +250 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VladX Lexer — Лексический анализатор
|
|
3
|
+
* Преобразует исходный код в поток токенов
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class Lexer {
|
|
7
|
+
constructor(source, filename = '<anonymous>') {
|
|
8
|
+
this.source = source;
|
|
9
|
+
this.filename = filename;
|
|
10
|
+
this.pos = 0;
|
|
11
|
+
this.line = 1;
|
|
12
|
+
this.column = 0;
|
|
13
|
+
this.tokens = [];
|
|
14
|
+
this.currentChar = this.source[0] || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Основной метод токенизации
|
|
19
|
+
*/
|
|
20
|
+
tokenize() {
|
|
21
|
+
try {
|
|
22
|
+
while (this.currentChar !== null) {
|
|
23
|
+
// Пропускаем пробелы и табы (но не переводы строк)
|
|
24
|
+
if (this.currentChar === ' ' || this.currentChar === '\t') {
|
|
25
|
+
this.advance();
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Перевод строки
|
|
30
|
+
if (this.currentChar === '\n') {
|
|
31
|
+
this.tokens.push({
|
|
32
|
+
type: 'NEWLINE',
|
|
33
|
+
value: '\n',
|
|
34
|
+
line: this.line,
|
|
35
|
+
column: this.column,
|
|
36
|
+
filename: this.filename
|
|
37
|
+
});
|
|
38
|
+
this.line++;
|
|
39
|
+
this.column = 0;
|
|
40
|
+
this.advance();
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Комментарии
|
|
45
|
+
if (this.currentChar === '#') {
|
|
46
|
+
this.skipComment();
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (this.currentChar === '/' && (this.peek() === '*' || this.peek() === '/')) {
|
|
50
|
+
this.skipComment();
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Строковые литералы
|
|
55
|
+
if (this.currentChar === '"' || this.currentChar === "'") {
|
|
56
|
+
const token = this.readStringLiteral();
|
|
57
|
+
this.tokens.push(token);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Идентификаторы и ключевые слова
|
|
62
|
+
if (this.isAlpha(this.currentChar) || this.currentChar === '_') {
|
|
63
|
+
const token = this.readIdentifier();
|
|
64
|
+
this.tokens.push(token);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Шаблонные строки (начинаются с `)
|
|
69
|
+
if (this.currentChar === '`') {
|
|
70
|
+
const token = this.readTemplateLiteral();
|
|
71
|
+
this.tokens.push(token);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Числа
|
|
76
|
+
if (this.isDigit(this.currentChar)) {
|
|
77
|
+
const token = this.readNumber();
|
|
78
|
+
this.tokens.push(token);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Операторы и символы
|
|
83
|
+
const token = this.readOperator();
|
|
84
|
+
if (token) {
|
|
85
|
+
this.tokens.push(token);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Неизвестный символ
|
|
90
|
+
throw new Error(`[${this.filename}:${this.line}:${this.column}] Неизвестный символ: '${this.currentChar}' (код: ${this.currentChar.charCodeAt(0)})`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Добавляем EOF токен
|
|
94
|
+
this.tokens.push({
|
|
95
|
+
type: 'EOF',
|
|
96
|
+
value: null,
|
|
97
|
+
line: this.line,
|
|
98
|
+
column: this.column,
|
|
99
|
+
filename: this.filename
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return this.tokens;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (this.debug) {
|
|
105
|
+
console.error('[Lexer] Error during tokenization:', error);
|
|
106
|
+
console.error('[Lexer] Position:', this.pos, 'Char:', this.currentChar);
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Продвижение по строке
|
|
114
|
+
*/
|
|
115
|
+
advance() {
|
|
116
|
+
this.pos++;
|
|
117
|
+
this.column++;
|
|
118
|
+
this.currentChar = this.source[this.pos] || null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Пропуск комментариев
|
|
123
|
+
*/
|
|
124
|
+
skipComment() {
|
|
125
|
+
if (this.currentChar === '#') {
|
|
126
|
+
// Однострочный комментарий
|
|
127
|
+
while (this.currentChar !== null && this.currentChar !== '\n') {
|
|
128
|
+
this.advance();
|
|
129
|
+
}
|
|
130
|
+
} else if (this.currentChar === '/' && this.peek() === '*') {
|
|
131
|
+
// Многострочный комментарий /* ... */
|
|
132
|
+
this.advance(); // пропускаем '/'
|
|
133
|
+
this.advance(); // пропускаем '*'
|
|
134
|
+
while (this.currentChar !== null) {
|
|
135
|
+
if (this.currentChar === '\n') {
|
|
136
|
+
this.line++;
|
|
137
|
+
this.column = 0;
|
|
138
|
+
} else {
|
|
139
|
+
this.column++;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (this.currentChar === '*' && this.peek() === '/') {
|
|
143
|
+
this.advance(); // пропускаем '*'
|
|
144
|
+
this.advance(); // пропускаем '/'
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
this.advance();
|
|
148
|
+
}
|
|
149
|
+
} else if (this.currentChar === '/' && this.peek() === '/') {
|
|
150
|
+
// Однострочный комментарий // ...
|
|
151
|
+
this.advance(); // пропускаем '/'
|
|
152
|
+
this.advance(); // пропускаем '/'
|
|
153
|
+
while (this.currentChar !== null && this.currentChar !== '\n') {
|
|
154
|
+
this.advance();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Чтение строкового литерала
|
|
161
|
+
*/
|
|
162
|
+
readStringLiteral() {
|
|
163
|
+
const quote = this.currentChar;
|
|
164
|
+
this.advance(); // Пропускаем открывающую кавычку
|
|
165
|
+
|
|
166
|
+
let value = '';
|
|
167
|
+
const startLine = this.line;
|
|
168
|
+
const startColumn = this.column;
|
|
169
|
+
|
|
170
|
+
while (this.currentChar !== null && this.currentChar !== quote) {
|
|
171
|
+
if (this.currentChar === '\\') {
|
|
172
|
+
this.advance();
|
|
173
|
+
if (this.currentChar === null) {
|
|
174
|
+
throw new Error("Незавершённая escape-последовательность в строке");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
switch (this.currentChar) {
|
|
178
|
+
case 'n':
|
|
179
|
+
value += '\n';
|
|
180
|
+
break;
|
|
181
|
+
case 't':
|
|
182
|
+
value += '\t';
|
|
183
|
+
break;
|
|
184
|
+
case 'r':
|
|
185
|
+
value += '\r';
|
|
186
|
+
break;
|
|
187
|
+
case '"':
|
|
188
|
+
value += '"';
|
|
189
|
+
break;
|
|
190
|
+
case "'":
|
|
191
|
+
value += "'";
|
|
192
|
+
break;
|
|
193
|
+
case '\\':
|
|
194
|
+
value += '\\';
|
|
195
|
+
break;
|
|
196
|
+
case 'x': // \xHH — hex код
|
|
197
|
+
this.advance();
|
|
198
|
+
const hex1 = this.currentChar;
|
|
199
|
+
this.advance();
|
|
200
|
+
const hex2 = this.currentChar;
|
|
201
|
+
if (hex1 && hex2 && /[0-9a-fA-F]{2}/.test(hex1 + hex2)) {
|
|
202
|
+
value += String.fromCharCode(parseInt(hex1 + hex2, 16));
|
|
203
|
+
} else {
|
|
204
|
+
throw new Error(`Неверный \\x escape: \\x${hex1 || ''}${hex2 || ''}`);
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case 'u': // \uHHHH — unicode
|
|
208
|
+
this.advance();
|
|
209
|
+
let unicode = '';
|
|
210
|
+
for (let i = 0; i < 4; i++) {
|
|
211
|
+
if (this.currentChar && /[0-9a-fA-F]/.test(this.currentChar)) {
|
|
212
|
+
unicode += this.currentChar;
|
|
213
|
+
this.advance();
|
|
214
|
+
} else {
|
|
215
|
+
throw new Error(`Неверный \\u escape: \\u${unicode}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
value += String.fromCharCode(parseInt(unicode, 16));
|
|
219
|
+
this.advance(); // пропустить после \uHHHH
|
|
220
|
+
continue; // чтобы не добавлять последний символ как обычный
|
|
221
|
+
default:
|
|
222
|
+
value += '\\' + this.currentChar; // неизвестный escape — оставляем как есть
|
|
223
|
+
}
|
|
224
|
+
this.advance();
|
|
225
|
+
continue;
|
|
226
|
+
} else {
|
|
227
|
+
value += this.currentChar;
|
|
228
|
+
this.advance();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this.advance(); // Пропускаем закрывающую кавычку
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
type: 'STRING',
|
|
236
|
+
value: value,
|
|
237
|
+
line: startLine,
|
|
238
|
+
column: startColumn,
|
|
239
|
+
filename: this.filename
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Обработка escape-последовательностей
|
|
245
|
+
*/
|
|
246
|
+
escapeChar(char) {
|
|
247
|
+
const escapes = {
|
|
248
|
+
'n': '\n',
|
|
249
|
+
't': '\t',
|
|
250
|
+
'r': '\r',
|
|
251
|
+
'\\': '\\',
|
|
252
|
+
'"': '"',
|
|
253
|
+
"'": "'"
|
|
254
|
+
};
|
|
255
|
+
return escapes[char] || char;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Чтение шаблонной строки
|
|
260
|
+
*/
|
|
261
|
+
readTemplateLiteral() {
|
|
262
|
+
this.advance(); // Пропускаем открывающую обратную кавычку
|
|
263
|
+
|
|
264
|
+
let value = '';
|
|
265
|
+
const startLine = this.line;
|
|
266
|
+
const startColumn = this.column;
|
|
267
|
+
|
|
268
|
+
while (this.currentChar !== null && this.currentChar !== '`') {
|
|
269
|
+
if (this.currentChar === '\\') {
|
|
270
|
+
this.advance();
|
|
271
|
+
if (this.currentChar === null) {
|
|
272
|
+
throw new Error("Незавершённая escape-последовательность в шаблонной строке");
|
|
273
|
+
}
|
|
274
|
+
value += this.escapeChar(this.currentChar);
|
|
275
|
+
this.advance();
|
|
276
|
+
} else if (this.currentChar === '$' && this.peek() === '{') {
|
|
277
|
+
// Это начало интерполяции ${...}
|
|
278
|
+
value += this.currentChar; // Добавляем $
|
|
279
|
+
this.advance(); // Пропускаем $
|
|
280
|
+
value += this.currentChar; // Добавляем {
|
|
281
|
+
this.advance(); // Пропускаем {
|
|
282
|
+
} else {
|
|
283
|
+
value += this.currentChar;
|
|
284
|
+
this.advance();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (this.currentChar !== '`') {
|
|
289
|
+
throw new Error(`Незавершённая шаблонная строка, ожидалась обратная кавычка`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this.advance(); // Пропускаем закрывающую обратную кавычку
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
type: 'TEMPLATE_LITERAL',
|
|
296
|
+
value: value,
|
|
297
|
+
line: startLine,
|
|
298
|
+
column: startColumn,
|
|
299
|
+
filename: this.filename
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Чтение идентификатора или ключевого слова
|
|
305
|
+
*/
|
|
306
|
+
readIdentifier() {
|
|
307
|
+
let value = '';
|
|
308
|
+
const startLine = this.line;
|
|
309
|
+
const startColumn = this.column;
|
|
310
|
+
|
|
311
|
+
while (this.currentChar !== null && (this.isAlphaNumeric(this.currentChar) || this.currentChar === '_')) {
|
|
312
|
+
value += this.currentChar;
|
|
313
|
+
this.advance();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Проверка на ключевые слова - маппинг русских слов к латинским типам токенов
|
|
317
|
+
const keywordToTokenType = {
|
|
318
|
+
'let': 'LET',
|
|
319
|
+
'пусть': 'LET',
|
|
320
|
+
'const': 'CONST',
|
|
321
|
+
'конст': 'CONST',
|
|
322
|
+
'константа': 'CONST',
|
|
323
|
+
'if': 'IF',
|
|
324
|
+
'если': 'IF',
|
|
325
|
+
'else': 'ELSE',
|
|
326
|
+
'иначе': 'ELSE',
|
|
327
|
+
'elseif': 'ELSEIF',
|
|
328
|
+
'иначеесли': 'ELSEIF',
|
|
329
|
+
'илиесли': 'ИЛИЕСЛИ',
|
|
330
|
+
'while': 'WHILE',
|
|
331
|
+
'пока': 'WHILE',
|
|
332
|
+
'for': 'FOR',
|
|
333
|
+
'для': 'FOR',
|
|
334
|
+
'function': 'FUNCTION',
|
|
335
|
+
'функция': 'FUNCTION',
|
|
336
|
+
'return': 'RETURN',
|
|
337
|
+
'вернуть': 'RETURN',
|
|
338
|
+
'class': 'CLASS',
|
|
339
|
+
'класс': 'CLASS',
|
|
340
|
+
'class': 'CLASS',
|
|
341
|
+
'this': 'THIS',
|
|
342
|
+
'это': 'THIS',
|
|
343
|
+
'super': 'SUPER',
|
|
344
|
+
'супер': 'SUPER',
|
|
345
|
+
'new': 'NEW',
|
|
346
|
+
'новый': 'NEW',
|
|
347
|
+
'extends': 'EXTENDS',
|
|
348
|
+
'расширяет': 'EXTENDS',
|
|
349
|
+
'try': 'TRY',
|
|
350
|
+
'попытка': 'TRY',
|
|
351
|
+
'catch': 'CATCH',
|
|
352
|
+
'перехват': 'CATCH',
|
|
353
|
+
'исключение': 'CATCH',
|
|
354
|
+
'finally': 'FINALLY',
|
|
355
|
+
'наконец': 'FINALLY',
|
|
356
|
+
'throw': 'THROW',
|
|
357
|
+
'бросить': 'THROW',
|
|
358
|
+
'break': 'BREAK',
|
|
359
|
+
'прервать': 'BREAK',
|
|
360
|
+
'continue': 'CONTINUE',
|
|
361
|
+
'продолжить': 'CONTINUE',
|
|
362
|
+
'static': 'STATIC',
|
|
363
|
+
'СТАТИЧЕСКИЙ': 'STATIC',
|
|
364
|
+
'статический': 'STATIC',
|
|
365
|
+
'get': 'GET',
|
|
366
|
+
'set': 'SET',
|
|
367
|
+
'true': 'TRUE',
|
|
368
|
+
'истина': 'TRUE',
|
|
369
|
+
'false': 'FALSE',
|
|
370
|
+
'ложь': 'FALSE',
|
|
371
|
+
'null': 'NULL',
|
|
372
|
+
'ничто': 'NULL',
|
|
373
|
+
'nothing': 'NULL',
|
|
374
|
+
'import': 'IMPORT',
|
|
375
|
+
'импорт': 'IMPORT',
|
|
376
|
+
'export': 'EXPORT',
|
|
377
|
+
'экспорт': 'EXPORT',
|
|
378
|
+
'from': 'FROM',
|
|
379
|
+
'из': 'FROM',
|
|
380
|
+
'as': 'AS',
|
|
381
|
+
'как': 'AS',
|
|
382
|
+
'and': 'AND',
|
|
383
|
+
'и': 'AND',
|
|
384
|
+
'or': 'OR',
|
|
385
|
+
'или': 'OR',
|
|
386
|
+
'with': 'WITH',
|
|
387
|
+
'с': 'WITH',
|
|
388
|
+
'to': 'TO',
|
|
389
|
+
'до': 'TO',
|
|
390
|
+
'switch': 'SWITCH',
|
|
391
|
+
'выбор': 'SWITCH',
|
|
392
|
+
'case': 'CASE',
|
|
393
|
+
'когда': 'CASE',
|
|
394
|
+
'default': 'DEFAULT',
|
|
395
|
+
'поумолчанию': 'DEFAULT',
|
|
396
|
+
'async': 'ASYNC',
|
|
397
|
+
'асинх': 'ASYNC',
|
|
398
|
+
'await': 'AWAIT',
|
|
399
|
+
'ожидать': 'AWAIT',
|
|
400
|
+
'не': 'NOT'
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const lowerValue = value.toLowerCase();
|
|
404
|
+
const type = Object.prototype.hasOwnProperty.call(keywordToTokenType, lowerValue) ? keywordToTokenType[lowerValue] : 'IDENTIFIER';
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
type: type,
|
|
408
|
+
value: value,
|
|
409
|
+
line: startLine,
|
|
410
|
+
column: startColumn,
|
|
411
|
+
filename: this.filename
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Чтение числа
|
|
416
|
+
*/
|
|
417
|
+
readNumber() {
|
|
418
|
+
let value = '';
|
|
419
|
+
const startLine = this.line;
|
|
420
|
+
const startColumn = this.column;
|
|
421
|
+
let hasDecimal = false;
|
|
422
|
+
|
|
423
|
+
while (this.currentChar !== null) {
|
|
424
|
+
if (this.isDigit(this.currentChar)) {
|
|
425
|
+
value += this.currentChar;
|
|
426
|
+
this.advance();
|
|
427
|
+
} else if (this.currentChar === '.' && !hasDecimal) {
|
|
428
|
+
hasDecimal = true;
|
|
429
|
+
value += this.currentChar;
|
|
430
|
+
this.advance();
|
|
431
|
+
} else {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
type: 'INT',
|
|
438
|
+
value: hasDecimal ? parseFloat(value) : parseInt(value, 10),
|
|
439
|
+
line: startLine,
|
|
440
|
+
column: startColumn,
|
|
441
|
+
filename: this.filename
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Чтение операторов и символов
|
|
447
|
+
*/
|
|
448
|
+
readOperator() {
|
|
449
|
+
const startLine = this.line;
|
|
450
|
+
const startColumn = this.column;
|
|
451
|
+
const char = this.currentChar;
|
|
452
|
+
|
|
453
|
+
// Многосимвольные операторы
|
|
454
|
+
if (char === '=' && this.peek() === '=') {
|
|
455
|
+
this.advance();
|
|
456
|
+
this.advance();
|
|
457
|
+
return { type: 'EQEQ', value: '==', line: startLine, column: startColumn, filename: this.filename };
|
|
458
|
+
}
|
|
459
|
+
if (char === '!' && this.peek() === '=') {
|
|
460
|
+
this.advance();
|
|
461
|
+
this.advance();
|
|
462
|
+
return { type: 'NEQ', value: '!=', line: startLine, column: startColumn, filename: this.filename };
|
|
463
|
+
}
|
|
464
|
+
if (char === '<' && this.peek() === '=') {
|
|
465
|
+
this.advance();
|
|
466
|
+
this.advance();
|
|
467
|
+
return { type: 'LTE', value: '<=', line: startLine, column: startColumn, filename: this.filename };
|
|
468
|
+
}
|
|
469
|
+
if (char === '>' && this.peek() === '=') {
|
|
470
|
+
this.advance();
|
|
471
|
+
this.advance();
|
|
472
|
+
return { type: 'GTE', value: '>=', line: startLine, column: startColumn, filename: this.filename };
|
|
473
|
+
}
|
|
474
|
+
if (char === '&' && this.peek() === '&') {
|
|
475
|
+
this.advance();
|
|
476
|
+
this.advance();
|
|
477
|
+
return { type: 'AND', value: '&&', line: startLine, column: startColumn, filename: this.filename };
|
|
478
|
+
}
|
|
479
|
+
if (char === '|' && this.peek() === '|') {
|
|
480
|
+
this.advance();
|
|
481
|
+
this.advance();
|
|
482
|
+
return { type: 'OR', value: '||', line: startLine, column: startColumn, filename: this.filename };
|
|
483
|
+
}
|
|
484
|
+
if (char === '*' && this.peek() === '*') {
|
|
485
|
+
this.advance();
|
|
486
|
+
this.advance();
|
|
487
|
+
return { type: 'EXP', value: '**', line: startLine, column: startColumn, filename: this.filename };
|
|
488
|
+
}
|
|
489
|
+
// Добавляем дополнительные операторы
|
|
490
|
+
if (char === '+' && this.peek() === '=') {
|
|
491
|
+
this.advance();
|
|
492
|
+
this.advance();
|
|
493
|
+
return { type: 'PLUSEQ', value: '+=', line: startLine, column: startColumn, filename: this.filename };
|
|
494
|
+
}
|
|
495
|
+
if (char === '-' && this.peek() === '=') {
|
|
496
|
+
this.advance();
|
|
497
|
+
this.advance();
|
|
498
|
+
return { type: 'MINUSEQ', value: '-=', line: startLine, column: startColumn, filename: this.filename };
|
|
499
|
+
}
|
|
500
|
+
if (char === '*' && this.peek() === '=') {
|
|
501
|
+
this.advance();
|
|
502
|
+
this.advance();
|
|
503
|
+
return { type: 'MULTEQ', value: '*=', line: startLine, column: startColumn, filename: this.filename };
|
|
504
|
+
}
|
|
505
|
+
if (char === '/' && this.peek() === '=') {
|
|
506
|
+
this.advance();
|
|
507
|
+
this.advance();
|
|
508
|
+
return { type: 'DIVEQ', value: '/=', line: startLine, column: startColumn, filename: this.filename };
|
|
509
|
+
}
|
|
510
|
+
if (char === '%' && this.peek() === '=') {
|
|
511
|
+
this.advance();
|
|
512
|
+
this.advance();
|
|
513
|
+
return { type: 'MODEQ', value: '%=', line: startLine, column: startColumn, filename: this.filename };
|
|
514
|
+
}
|
|
515
|
+
if (char === '<' && this.peek() === '<') {
|
|
516
|
+
this.advance();
|
|
517
|
+
this.advance();
|
|
518
|
+
return { type: 'LEFTSHIFT', value: '<<', line: startLine, column: startColumn, filename: this.filename };
|
|
519
|
+
}
|
|
520
|
+
if (char === '>' && this.peek() === '>') {
|
|
521
|
+
this.advance();
|
|
522
|
+
this.advance();
|
|
523
|
+
return { type: 'RIGHTSHIFT', value: '>>', line: startLine, column: startColumn, filename: this.filename };
|
|
524
|
+
}
|
|
525
|
+
if (char === '=' && this.peek() === '>') {
|
|
526
|
+
this.advance();
|
|
527
|
+
this.advance();
|
|
528
|
+
return { type: 'FATARROW', value: '=>', line: startLine, column: startColumn, filename: this.filename };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Импорт/экспорт модулей
|
|
532
|
+
if (char === 'i' && this.peek() === 'm' && this.peek(1) === 'p' && this.peek(2) === 'o' && this.peek(3) === 'r' && this.peek(4) === 't') {
|
|
533
|
+
this.advance(); this.advance(); this.advance(); this.advance(); this.advance();
|
|
534
|
+
return { type: 'IMPORT', value: 'import', line: startLine, column: startColumn, filename: this.filename };
|
|
535
|
+
}
|
|
536
|
+
if (char === 'э' && this.peek() === 'к' && this.peek(1) === 'с' && this.peek(2) === 'п' && this.peek(3) === 'о' && this.peek(4) === 'р' && this.peek(5) === 'т') {
|
|
537
|
+
for (let i = 0; i < 7; i++) this.advance();
|
|
538
|
+
return { type: 'EXPORT', value: 'экспорт', line: startLine, column: startColumn, filename: this.filename };
|
|
539
|
+
}
|
|
540
|
+
if (char === 'e' && this.peek() === 'x' && this.peek(1) === 'p' && this.peek(2) === 'o' && this.peek(3) === 'r' && this.peek(4) === 't') {
|
|
541
|
+
this.advance(); this.advance(); this.advance(); this.advance(); this.advance();
|
|
542
|
+
return { type: 'EXPORT', value: 'export', line: startLine, column: startColumn, filename: this.filename };
|
|
543
|
+
}
|
|
544
|
+
if (char === 'и' && this.peek() === 'м' && this.peek(1) === 'п' && this.peek(2) === 'о' && this.peek(3) === 'р' && this.peek(4) === 'т') {
|
|
545
|
+
for (let i = 0; i < 5; i++) this.advance();
|
|
546
|
+
return { type: 'IMPORT', value: 'импорт', line: startLine, column: startColumn, filename: this.filename };
|
|
547
|
+
}
|
|
548
|
+
// Поддержка "как" и "as" в импорте
|
|
549
|
+
if (char === 'к' && this.peek() === 'а' && this.peek(1) === 'к') {
|
|
550
|
+
this.advance(); this.advance(); this.advance();
|
|
551
|
+
return { type: 'AS', value: 'как', line: startLine, column: startColumn, filename: this.filename };
|
|
552
|
+
}
|
|
553
|
+
if (char === 'a' && this.peek() === 's') {
|
|
554
|
+
this.advance(); this.advance();
|
|
555
|
+
return { type: 'AS', value: 'as', line: startLine, column: startColumn, filename: this.filename };
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Проверяем, не является ли '/' началом комментария
|
|
559
|
+
if (char === '/') {
|
|
560
|
+
if (this.peek() === '*') {
|
|
561
|
+
// Это начало многострочного комментария, не обрабатываем здесь
|
|
562
|
+
return null;
|
|
563
|
+
} else {
|
|
564
|
+
this.advance();
|
|
565
|
+
return {
|
|
566
|
+
type: 'DIV',
|
|
567
|
+
value: '/',
|
|
568
|
+
line: startLine,
|
|
569
|
+
column: startColumn,
|
|
570
|
+
filename: this.filename
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Многосимвольные операторы (до одиночных)
|
|
576
|
+
if (char === '.' && this.peek() === '.' && this.peek(1) === '.') {
|
|
577
|
+
this.advance();
|
|
578
|
+
this.advance();
|
|
579
|
+
this.advance();
|
|
580
|
+
return { type: 'SPREAD', value: '...', line: startLine, column: startColumn, filename: this.filename };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Одиночные символы
|
|
584
|
+
const singleChars = {
|
|
585
|
+
'+': 'PLUS',
|
|
586
|
+
'-': 'MINUS',
|
|
587
|
+
'*': 'MULT',
|
|
588
|
+
'%': 'MOD',
|
|
589
|
+
'=': 'ASSIGN',
|
|
590
|
+
'.': 'DOT',
|
|
591
|
+
'(': 'LPAREN',
|
|
592
|
+
')': 'RPAREN',
|
|
593
|
+
'{': 'LBRACE',
|
|
594
|
+
'}': 'RBRACE',
|
|
595
|
+
'[': 'LBRACKET',
|
|
596
|
+
']': 'RBRACKET',
|
|
597
|
+
',': 'COMMA',
|
|
598
|
+
':': 'COLON',
|
|
599
|
+
';': 'SEMICOLON',
|
|
600
|
+
'<': 'LT',
|
|
601
|
+
'>': 'GT',
|
|
602
|
+
'!': 'NOT',
|
|
603
|
+
'?': 'TERNARY'
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
if (singleChars[char]) {
|
|
607
|
+
this.advance();
|
|
608
|
+
return {
|
|
609
|
+
type: singleChars[char],
|
|
610
|
+
value: char,
|
|
611
|
+
line: startLine,
|
|
612
|
+
column: startColumn,
|
|
613
|
+
filename: this.filename
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Подглядывание следующего символа
|
|
622
|
+
*/
|
|
623
|
+
peek(offset = 1) {
|
|
624
|
+
return this.source[this.pos + offset] || null;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Проверка на букву (включая русские буквы)
|
|
629
|
+
*/
|
|
630
|
+
isAlpha(char) {
|
|
631
|
+
const code = char.charCodeAt(0);
|
|
632
|
+
// Латинские буквы
|
|
633
|
+
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || char === '_') {
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
// Русские буквы (А-Я, а-я, Ё, ё)
|
|
637
|
+
if ((code >= 1040 && code <= 1103) || code === 1025 || code === 1105) {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Проверка на цифру
|
|
645
|
+
*/
|
|
646
|
+
isDigit(char) {
|
|
647
|
+
return char >= '0' && char <= '9';
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Проверка на букву или цифру
|
|
652
|
+
*/
|
|
653
|
+
isAlphaNumeric(char) {
|
|
654
|
+
return this.isAlpha(char) || this.isDigit(char);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
export default Lexer;
|