vladx 1.3.2 → 1.5.1
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/bin/vladx +4 -1
- package/examples/stdlib/ambiente.vx +8 -0
- package/examples/stdlib/archivio_extra.vx +11 -0
- package/examples/stdlib/asserzione.vx +11 -0
- package/examples/stdlib/client_web.vx +7 -0
- package/examples/stdlib/codifica.vx +8 -0
- package/examples/stdlib/collezioni.vx +11 -0
- package/examples/stdlib/compressione.vx +10 -0
- package/examples/stdlib/console.vx +14 -0
- package/examples/stdlib/cripto.vx +8 -0
- package/examples/stdlib/dati.vx +21 -0
- package/examples/stdlib/eventi.vx +10 -0
- package/examples/stdlib/json_extra.vx +7 -0
- package/examples/stdlib/matematica.vx +9 -0
- package/examples/stdlib/percorso.vx +9 -0
- package/examples/stdlib/processo.vx +9 -0
- package/examples/stdlib/server_web.vx +10 -0
- package/examples/stdlib/sql_helper.vx +7 -0
- package/examples/stdlib/tempo.vx +9 -0
- package/examples/stdlib/testo.vx +10 -0
- package/examples/stdlib/url.vx +11 -0
- package/examples/stdlib/validazione.vx +5 -0
- package/package.json +6 -2
- package/programs/1/index.vx +16 -4
- package/programs/main/index.vx +11 -8
- package/programs/main/passports/Alina.json +1 -0
- package/programs/main/passports/Tanya.json +1 -0
- package/programs/main/passports/vlad.json +1 -0
- package/programs/main/test_async.vx +25 -0
- package/programs/main/test_async_advanced.vx +35 -0
- package/programs/main/test_backend.vx +33 -0
- package/programs/tests/test_new_features.vx +53 -0
- package/programs/tests/test_stdlib.vx +26 -0
- package/programs/tests/test_templates.vx +8 -0
- package/src/cli/cli.js +8 -6
- package/src/interpreter/interpreter.js +388 -170
- package/src/lexer/lexer.js +5 -1
- package/src/lexer/tokens.js +9 -1
- package/src/parser/ast.js +59 -4
- package/src/parser/parser.js +152 -18
- package/src/repl/repl.js +5 -5
- package/src/stdlib/registry.js +301 -0
- /package/programs/main/passports/{Vlad.txt → Vlad.json} +0 -0
package/src/lexer/lexer.js
CHANGED
|
@@ -270,7 +270,11 @@ class Lexer {
|
|
|
270
270
|
case ';': this.addToken(TokenType.PUNTO_VIRGOLA, ';'); break;
|
|
271
271
|
case '.': this.addToken(TokenType.PUNTO, '.'); break;
|
|
272
272
|
case ':': this.addToken(TokenType.DUE_PUNTI, ':'); break;
|
|
273
|
-
case '?':
|
|
273
|
+
case '?':
|
|
274
|
+
if (this.match('?')) this.addToken(TokenType.NULLISH, '??');
|
|
275
|
+
else if (this.match('.')) this.addToken(TokenType.OPTIONAL_CHAINING, '?.');
|
|
276
|
+
else this.addToken(TokenType.PUNTO_INTERROGATIVO, '?');
|
|
277
|
+
break;
|
|
274
278
|
|
|
275
279
|
case '+':
|
|
276
280
|
if (this.match('+')) this.addToken(TokenType.INCREMENTA, '++');
|
package/src/lexer/tokens.js
CHANGED
|
@@ -39,6 +39,9 @@ const TokenType = {
|
|
|
39
39
|
ATTENDI: 'ATTENDI',
|
|
40
40
|
INTERROMPI: 'INTERROMPI',
|
|
41
41
|
CONTINUA: 'CONTINUA',
|
|
42
|
+
SCEGLI: 'SCEGLI',
|
|
43
|
+
CASO: 'CASO',
|
|
44
|
+
PREDEFINITO: 'PREDEFINITO',
|
|
42
45
|
|
|
43
46
|
// Operators
|
|
44
47
|
PIU: 'PIU',
|
|
@@ -62,6 +65,8 @@ const TokenType = {
|
|
|
62
65
|
FRECCIA: 'FRECCIA',
|
|
63
66
|
AND: 'AND',
|
|
64
67
|
OR: 'OR',
|
|
68
|
+
NULLISH: 'NULLISH',
|
|
69
|
+
OPTIONAL_CHAINING: 'OPTIONAL_CHAINING',
|
|
65
70
|
|
|
66
71
|
// Delimiters
|
|
67
72
|
PARENTESI_APERTA: 'PARENTESI_APERTA',
|
|
@@ -116,7 +121,10 @@ const KEYWORDS = {
|
|
|
116
121
|
'asincrono': TokenType.ASINCRONO,
|
|
117
122
|
'attendi': TokenType.ATTENDI,
|
|
118
123
|
'interrompi': TokenType.INTERROMPI,
|
|
119
|
-
'continua': TokenType.CONTINUA
|
|
124
|
+
'continua': TokenType.CONTINUA,
|
|
125
|
+
'scegli': TokenType.SCEGLI,
|
|
126
|
+
'caso': TokenType.CASO,
|
|
127
|
+
'predefinito': TokenType.PREDEFINITO
|
|
120
128
|
};
|
|
121
129
|
|
|
122
130
|
module.exports = { TokenType, KEYWORDS };
|
package/src/parser/ast.js
CHANGED
|
@@ -27,12 +27,27 @@ class VariableDeclaration extends ASTNode {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
class ArrayPattern extends ASTNode {
|
|
31
|
+
constructor(elements) {
|
|
32
|
+
super('ArrayPattern');
|
|
33
|
+
this.elements = elements;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class ObjectPattern extends ASTNode {
|
|
38
|
+
constructor(properties) {
|
|
39
|
+
super('ObjectPattern');
|
|
40
|
+
this.properties = properties;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
class FunctionDeclaration extends ASTNode {
|
|
31
|
-
constructor(name, params, body) {
|
|
45
|
+
constructor(name, params, body, isAsync = false) {
|
|
32
46
|
super('FunctionDeclaration');
|
|
33
47
|
this.name = name;
|
|
34
48
|
this.params = params;
|
|
35
49
|
this.body = body;
|
|
50
|
+
this.isAsync = isAsync;
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
53
|
|
|
@@ -60,6 +75,13 @@ class NewExpression extends ASTNode {
|
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
|
|
78
|
+
class AwaitExpression extends ASTNode {
|
|
79
|
+
constructor(argument) {
|
|
80
|
+
super('AwaitExpression');
|
|
81
|
+
this.argument = argument;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
63
85
|
// === Statements ===
|
|
64
86
|
class BlockStatement extends ASTNode {
|
|
65
87
|
constructor(body) {
|
|
@@ -68,6 +90,22 @@ class BlockStatement extends ASTNode {
|
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
92
|
|
|
93
|
+
class SwitchStatement extends ASTNode {
|
|
94
|
+
constructor(discriminant, cases) {
|
|
95
|
+
super('SwitchStatement');
|
|
96
|
+
this.discriminant = discriminant;
|
|
97
|
+
this.cases = cases;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class SwitchCase extends ASTNode {
|
|
102
|
+
constructor(test, consequent) {
|
|
103
|
+
super('SwitchCase');
|
|
104
|
+
this.test = test; // null se predefinito
|
|
105
|
+
this.consequent = consequent;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
71
109
|
class IfStatement extends ASTNode {
|
|
72
110
|
constructor(condition, consequent, alternate = null) {
|
|
73
111
|
super('IfStatement');
|
|
@@ -254,19 +292,21 @@ class CallExpression extends ASTNode {
|
|
|
254
292
|
}
|
|
255
293
|
|
|
256
294
|
class MemberExpression extends ASTNode {
|
|
257
|
-
constructor(object, property, computed = false) {
|
|
295
|
+
constructor(object, property, computed = false, optional = false) {
|
|
258
296
|
super('MemberExpression');
|
|
259
297
|
this.object = object;
|
|
260
298
|
this.property = property;
|
|
261
299
|
this.computed = computed;
|
|
300
|
+
this.optional = optional;
|
|
262
301
|
}
|
|
263
302
|
}
|
|
264
303
|
|
|
265
304
|
class ArrowFunction extends ASTNode {
|
|
266
|
-
constructor(params, body) {
|
|
305
|
+
constructor(params, body, isAsync = false) {
|
|
267
306
|
super('ArrowFunction');
|
|
268
307
|
this.params = params;
|
|
269
308
|
this.body = body;
|
|
309
|
+
this.isAsync = isAsync;
|
|
270
310
|
}
|
|
271
311
|
}
|
|
272
312
|
|
|
@@ -279,6 +319,15 @@ class UpdateExpression extends ASTNode {
|
|
|
279
319
|
}
|
|
280
320
|
}
|
|
281
321
|
|
|
322
|
+
class ConditionalExpression extends ASTNode {
|
|
323
|
+
constructor(test, consequent, alternate) {
|
|
324
|
+
super('ConditionalExpression');
|
|
325
|
+
this.test = test;
|
|
326
|
+
this.consequent = consequent;
|
|
327
|
+
this.alternate = alternate;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
282
331
|
class LogicalExpression extends ASTNode {
|
|
283
332
|
constructor(operator, left, right) {
|
|
284
333
|
super('LogicalExpression');
|
|
@@ -350,5 +399,11 @@ module.exports = {
|
|
|
350
399
|
LogicalExpression,
|
|
351
400
|
ThisExpression,
|
|
352
401
|
NewExpression,
|
|
353
|
-
TemplateLiteral
|
|
402
|
+
TemplateLiteral,
|
|
403
|
+
AwaitExpression,
|
|
404
|
+
SwitchStatement,
|
|
405
|
+
SwitchCase,
|
|
406
|
+
ConditionalExpression,
|
|
407
|
+
ArrayPattern,
|
|
408
|
+
ObjectPattern
|
|
354
409
|
};
|
package/src/parser/parser.js
CHANGED
|
@@ -73,7 +73,13 @@ class Parser {
|
|
|
73
73
|
if (this.match(TokenType.ESPORTA)) return this.exportDeclaration();
|
|
74
74
|
if (this.match(TokenType.VARIABILE)) return this.variableDeclaration(false);
|
|
75
75
|
if (this.match(TokenType.COSTANTE)) return this.variableDeclaration(true);
|
|
76
|
-
|
|
76
|
+
|
|
77
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
78
|
+
this.consume(TokenType.FUNZIONE, 'Atteso "funzione" dopo "asincrono"');
|
|
79
|
+
return this.functionDeclaration(true);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this.match(TokenType.FUNZIONE)) return this.functionDeclaration(false);
|
|
77
83
|
if (this.match(TokenType.CLASSE)) return this.classDeclaration();
|
|
78
84
|
return this.statement();
|
|
79
85
|
} catch (error) {
|
|
@@ -83,6 +89,34 @@ class Parser {
|
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
variableDeclaration(isConstant) {
|
|
92
|
+
if (this.match(TokenType.QUADRA_APERTA)) {
|
|
93
|
+
const elements = [];
|
|
94
|
+
if (!this.check(TokenType.QUADRA_CHIUSA)) {
|
|
95
|
+
do {
|
|
96
|
+
elements.push(this.consume(TokenType.IDENTIFICATORE, 'Nome variabile atteso in destrutturazione array').value);
|
|
97
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
98
|
+
}
|
|
99
|
+
this.consume(TokenType.QUADRA_CHIUSA, 'Atteso "]" dopo destrutturazione array');
|
|
100
|
+
this.consume(TokenType.ASSEGNA, 'Inizializzazione richiesta per destrutturazione');
|
|
101
|
+
const value = this.expression();
|
|
102
|
+
this.match(TokenType.PUNTO_VIRGOLA);
|
|
103
|
+
return new AST.VariableDeclaration(new AST.ArrayPattern(elements), value, isConstant);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this.match(TokenType.GRAFFA_APERTA)) {
|
|
107
|
+
const properties = [];
|
|
108
|
+
if (!this.check(TokenType.GRAFFA_CHIUSA)) {
|
|
109
|
+
do {
|
|
110
|
+
properties.push(this.consume(TokenType.IDENTIFICATORE, 'Nome proprietà atteso in destrutturazione oggetto').value);
|
|
111
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
112
|
+
}
|
|
113
|
+
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo destrutturazione oggetto');
|
|
114
|
+
this.consume(TokenType.ASSEGNA, 'Inizializzazione richiesta per destrutturazione');
|
|
115
|
+
const value = this.expression();
|
|
116
|
+
this.match(TokenType.PUNTO_VIRGOLA);
|
|
117
|
+
return new AST.VariableDeclaration(new AST.ObjectPattern(properties), value, isConstant);
|
|
118
|
+
}
|
|
119
|
+
|
|
86
120
|
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome variabile atteso').value;
|
|
87
121
|
let value = null;
|
|
88
122
|
if (this.match(TokenType.ASSEGNA)) {
|
|
@@ -97,20 +131,24 @@ class Parser {
|
|
|
97
131
|
const specifiers = [];
|
|
98
132
|
if (!this.check(TokenType.GRAFFA_CHIUSA)) {
|
|
99
133
|
do {
|
|
100
|
-
|
|
101
|
-
specifiers.push(name);
|
|
134
|
+
specifiers.push(this.consume(TokenType.IDENTIFICATORE, 'Nome simbolo atteso').value);
|
|
102
135
|
} while (this.match(TokenType.VIRGOLA));
|
|
103
136
|
}
|
|
104
|
-
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo i
|
|
105
|
-
this.consume(TokenType.DA, '
|
|
106
|
-
const source = this.consume(TokenType.STRINGA, '
|
|
137
|
+
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo i simboli');
|
|
138
|
+
this.consume(TokenType.DA, 'Atteso "da" dopo i simboli importati');
|
|
139
|
+
const source = this.consume(TokenType.STRINGA, 'Atteso percorso modulo').value;
|
|
107
140
|
this.match(TokenType.PUNTO_VIRGOLA);
|
|
108
141
|
return new AST.ImportDeclaration(specifiers, source);
|
|
109
142
|
}
|
|
110
143
|
|
|
111
144
|
exportDeclaration() {
|
|
145
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
146
|
+
this.consume(TokenType.FUNZIONE, 'Atteso "funzione" dopo "asincrono"');
|
|
147
|
+
const decl = this.functionDeclaration(true);
|
|
148
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
149
|
+
}
|
|
112
150
|
if (this.match(TokenType.FUNZIONE)) {
|
|
113
|
-
const decl = this.functionDeclaration();
|
|
151
|
+
const decl = this.functionDeclaration(false);
|
|
114
152
|
return new AST.ExportNamedDeclaration(decl);
|
|
115
153
|
}
|
|
116
154
|
if (this.match(TokenType.CLASSE)) {
|
|
@@ -128,7 +166,7 @@ class Parser {
|
|
|
128
166
|
throw new ParserError('Atteso funzione, classe o variabile dopo "esporta"', this.peek());
|
|
129
167
|
}
|
|
130
168
|
|
|
131
|
-
functionDeclaration() {
|
|
169
|
+
functionDeclaration(isAsync = false) {
|
|
132
170
|
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome funzione atteso').value;
|
|
133
171
|
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome funzione');
|
|
134
172
|
const params = [];
|
|
@@ -140,7 +178,7 @@ class Parser {
|
|
|
140
178
|
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo parametri');
|
|
141
179
|
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" prima del corpo funzione');
|
|
142
180
|
const body = this.blockStatement();
|
|
143
|
-
return new AST.FunctionDeclaration(name, params, body);
|
|
181
|
+
return new AST.FunctionDeclaration(name, params, body, isAsync);
|
|
144
182
|
}
|
|
145
183
|
|
|
146
184
|
classDeclaration() {
|
|
@@ -164,6 +202,11 @@ class Parser {
|
|
|
164
202
|
const methods = [];
|
|
165
203
|
|
|
166
204
|
while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
205
|
+
let isAsync = false;
|
|
206
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
207
|
+
isAsync = true;
|
|
208
|
+
}
|
|
209
|
+
|
|
167
210
|
if (this.match(TokenType.FUNZIONE)) {
|
|
168
211
|
const funcName = this.consume(TokenType.IDENTIFICATORE, 'Nome metodo atteso').value;
|
|
169
212
|
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome metodo');
|
|
@@ -178,11 +221,13 @@ class Parser {
|
|
|
178
221
|
const body = this.blockStatement();
|
|
179
222
|
|
|
180
223
|
if (funcName === 'costruttore') {
|
|
181
|
-
|
|
224
|
+
if (isAsync) throw new ParserError('Il costruttore non può essere asincrono', this.previous());
|
|
225
|
+
constructor = new AST.FunctionDeclaration('costruttore', params, body, false);
|
|
182
226
|
} else {
|
|
183
|
-
methods.push(new AST.FunctionDeclaration(funcName, params, body));
|
|
227
|
+
methods.push(new AST.FunctionDeclaration(funcName, params, body, isAsync));
|
|
184
228
|
}
|
|
185
229
|
} else {
|
|
230
|
+
if (isAsync) throw new ParserError('Atteso metodo dopo "asincrono"', this.peek());
|
|
186
231
|
throw new ParserError('Atteso metodo nella classe', this.peek());
|
|
187
232
|
}
|
|
188
233
|
}
|
|
@@ -193,6 +238,7 @@ class Parser {
|
|
|
193
238
|
|
|
194
239
|
statement() {
|
|
195
240
|
if (this.match(TokenType.SE)) return this.ifStatement();
|
|
241
|
+
if (this.match(TokenType.SCEGLI)) return this.switchStatement();
|
|
196
242
|
if (this.match(TokenType.MENTRE)) return this.whileStatement();
|
|
197
243
|
if (this.match(TokenType.PER)) return this.forStatement();
|
|
198
244
|
if (this.match(TokenType.RITORNA)) return this.returnStatement();
|
|
@@ -205,6 +251,38 @@ class Parser {
|
|
|
205
251
|
return this.expressionStatement();
|
|
206
252
|
}
|
|
207
253
|
|
|
254
|
+
switchStatement() {
|
|
255
|
+
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo "scegli"');
|
|
256
|
+
const discriminant = this.expression();
|
|
257
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo discriminante');
|
|
258
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "scegli"');
|
|
259
|
+
|
|
260
|
+
const cases = [];
|
|
261
|
+
while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
262
|
+
if (this.match(TokenType.CASO)) {
|
|
263
|
+
const test = this.expression();
|
|
264
|
+
this.consume(TokenType.DUE_PUNTI, 'Atteso ":" dopo il caso');
|
|
265
|
+
const consequent = [];
|
|
266
|
+
while (!this.check(TokenType.CASO) && !this.check(TokenType.PREDEFINITO) && !this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
267
|
+
consequent.push(this.declaration());
|
|
268
|
+
}
|
|
269
|
+
cases.push(new AST.SwitchCase(test, consequent));
|
|
270
|
+
} else if (this.match(TokenType.PREDEFINITO)) {
|
|
271
|
+
this.consume(TokenType.DUE_PUNTI, 'Atteso ":" dopo "predefinito"');
|
|
272
|
+
const consequent = [];
|
|
273
|
+
while (!this.check(TokenType.CASO) && !this.check(TokenType.PREDEFINITO) && !this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
274
|
+
consequent.push(this.declaration());
|
|
275
|
+
}
|
|
276
|
+
cases.push(new AST.SwitchCase(null, consequent));
|
|
277
|
+
} else {
|
|
278
|
+
throw new ParserError('Atteso "caso" o "predefinito" all\'interno di "scegli"', this.peek());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo "scegli"');
|
|
283
|
+
return new AST.SwitchStatement(discriminant, cases);
|
|
284
|
+
}
|
|
285
|
+
|
|
208
286
|
blockStatement() {
|
|
209
287
|
const statements = [];
|
|
210
288
|
while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
@@ -342,7 +420,7 @@ class Parser {
|
|
|
342
420
|
}
|
|
343
421
|
|
|
344
422
|
assignment() {
|
|
345
|
-
const expr = this.
|
|
423
|
+
const expr = this.conditional();
|
|
346
424
|
|
|
347
425
|
if (this.match(TokenType.ASSEGNA, TokenType.PIU_ASSEGNA, TokenType.MENO_ASSEGNA)) {
|
|
348
426
|
const operator = this.previous().value;
|
|
@@ -353,18 +431,42 @@ class Parser {
|
|
|
353
431
|
return expr;
|
|
354
432
|
}
|
|
355
433
|
|
|
434
|
+
conditional() {
|
|
435
|
+
let expr = this.logicalOr();
|
|
436
|
+
|
|
437
|
+
if (this.match(TokenType.PUNTO_INTERROGATIVO)) {
|
|
438
|
+
const consequent = this.expression();
|
|
439
|
+
this.consume(TokenType.DUE_PUNTI, 'Atteso ":" dopo espressione del ternario');
|
|
440
|
+
const alternate = this.conditional();
|
|
441
|
+
return new AST.ConditionalExpression(expr, consequent, alternate);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return expr;
|
|
445
|
+
}
|
|
446
|
+
|
|
356
447
|
logicalOr() {
|
|
357
|
-
let expr = this.
|
|
448
|
+
let expr = this.nullishCoalescing();
|
|
358
449
|
|
|
359
450
|
while (this.match(TokenType.O, TokenType.OR)) {
|
|
360
451
|
const operator = '||';
|
|
361
|
-
const right = this.
|
|
452
|
+
const right = this.nullishCoalescing();
|
|
362
453
|
expr = new AST.LogicalExpression(operator, expr, right);
|
|
363
454
|
}
|
|
364
455
|
|
|
365
456
|
return expr;
|
|
366
457
|
}
|
|
367
458
|
|
|
459
|
+
nullishCoalescing() {
|
|
460
|
+
let expr = this.logicalAnd();
|
|
461
|
+
|
|
462
|
+
while (this.match(TokenType.NULLISH)) {
|
|
463
|
+
const right = this.logicalAnd();
|
|
464
|
+
expr = new AST.LogicalExpression('??', expr, right);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return expr;
|
|
468
|
+
}
|
|
469
|
+
|
|
368
470
|
logicalAnd() {
|
|
369
471
|
let expr = this.equality();
|
|
370
472
|
|
|
@@ -426,6 +528,11 @@ class Parser {
|
|
|
426
528
|
}
|
|
427
529
|
|
|
428
530
|
unary() {
|
|
531
|
+
if (this.match(TokenType.ATTENDI)) {
|
|
532
|
+
const argument = this.unary();
|
|
533
|
+
return new AST.AwaitExpression(argument);
|
|
534
|
+
}
|
|
535
|
+
|
|
429
536
|
if (this.match(TokenType.NON, TokenType.MENO)) {
|
|
430
537
|
const operator = this.previous().value === 'non' ? '!' : this.previous().value;
|
|
431
538
|
const argument = this.unary();
|
|
@@ -454,11 +561,14 @@ class Parser {
|
|
|
454
561
|
expr = this.finishCall(expr);
|
|
455
562
|
} else if (this.match(TokenType.PUNTO)) {
|
|
456
563
|
const property = this.consume(TokenType.IDENTIFICATORE, 'Nome proprietà atteso dopo "."');
|
|
457
|
-
expr = new AST.MemberExpression(expr, new AST.Identifier(property.value), false);
|
|
564
|
+
expr = new AST.MemberExpression(expr, new AST.Identifier(property.value), false, false);
|
|
565
|
+
} else if (this.match(TokenType.OPTIONAL_CHAINING)) {
|
|
566
|
+
const property = this.consume(TokenType.IDENTIFICATORE, 'Nome proprietà atteso dopo "?."');
|
|
567
|
+
expr = new AST.MemberExpression(expr, new AST.Identifier(property.value), false, true);
|
|
458
568
|
} else if (this.match(TokenType.QUADRA_APERTA)) {
|
|
459
569
|
const property = this.expression();
|
|
460
570
|
this.consume(TokenType.QUADRA_CHIUSA, 'Atteso "]" dopo indice');
|
|
461
|
-
expr = new AST.MemberExpression(expr, property, true);
|
|
571
|
+
expr = new AST.MemberExpression(expr, property, true, false);
|
|
462
572
|
} else {
|
|
463
573
|
break;
|
|
464
574
|
}
|
|
@@ -548,6 +658,30 @@ class Parser {
|
|
|
548
658
|
return this.objectLiteral();
|
|
549
659
|
}
|
|
550
660
|
|
|
661
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
662
|
+
// Async Arrow Function: asincrono (param) => ...
|
|
663
|
+
if (this.match(TokenType.PARENTESI_APERTA)) {
|
|
664
|
+
const params = [];
|
|
665
|
+
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
666
|
+
do {
|
|
667
|
+
params.push(this.consume(TokenType.IDENTIFICATORE, 'Nome parametro atteso').value);
|
|
668
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
669
|
+
}
|
|
670
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo parametri');
|
|
671
|
+
|
|
672
|
+
if (this.match(TokenType.FRECCIA)) {
|
|
673
|
+
if (this.match(TokenType.GRAFFA_APERTA)) {
|
|
674
|
+
const body = this.blockStatement();
|
|
675
|
+
return new AST.ArrowFunction(params, body, true);
|
|
676
|
+
} else {
|
|
677
|
+
const body = this.expression();
|
|
678
|
+
return new AST.ArrowFunction(params, body, true);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
throw new ParserError('Atteso funzione freccia asincrona', this.peek());
|
|
683
|
+
}
|
|
684
|
+
|
|
551
685
|
if (this.match(TokenType.PARENTESI_APERTA)) {
|
|
552
686
|
// Potrebbe essere una arrow function o un'espressione raggruppata
|
|
553
687
|
const startPos = this.current - 1;
|
|
@@ -573,10 +707,10 @@ class Parser {
|
|
|
573
707
|
// È una arrow function
|
|
574
708
|
if (this.match(TokenType.GRAFFA_APERTA)) {
|
|
575
709
|
const body = this.blockStatement();
|
|
576
|
-
return new AST.ArrowFunction(params, body);
|
|
710
|
+
return new AST.ArrowFunction(params, body, false);
|
|
577
711
|
} else {
|
|
578
712
|
const body = this.expression();
|
|
579
|
-
return new AST.ArrowFunction(params, body);
|
|
713
|
+
return new AST.ArrowFunction(params, body, false);
|
|
580
714
|
}
|
|
581
715
|
} else {
|
|
582
716
|
// Non è una arrow function, torna indietro
|
package/src/repl/repl.js
CHANGED
|
@@ -48,7 +48,7 @@ class REPL {
|
|
|
48
48
|
|
|
49
49
|
rl.prompt();
|
|
50
50
|
|
|
51
|
-
rl.on('line', (line) => {
|
|
51
|
+
rl.on('line', async (line) => {
|
|
52
52
|
const trimmed = line.trim();
|
|
53
53
|
|
|
54
54
|
// Handle special commands
|
|
@@ -92,7 +92,7 @@ class REPL {
|
|
|
92
92
|
|
|
93
93
|
if (code) {
|
|
94
94
|
this.history.push(code);
|
|
95
|
-
this.execute(code);
|
|
95
|
+
await this.execute(code);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
rl.setPrompt(colorize('vladx> ', 'green'));
|
|
@@ -105,7 +105,7 @@ class REPL {
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
execute(code) {
|
|
108
|
+
async execute(code) {
|
|
109
109
|
try {
|
|
110
110
|
const lexer = new Lexer(code);
|
|
111
111
|
const tokens = lexer.tokenize();
|
|
@@ -113,7 +113,7 @@ class REPL {
|
|
|
113
113
|
const parser = new Parser(tokens);
|
|
114
114
|
const ast = parser.parse();
|
|
115
115
|
|
|
116
|
-
const result = this.interpreter.interpret(ast);
|
|
116
|
+
const result = await this.interpreter.interpret(ast);
|
|
117
117
|
|
|
118
118
|
// Show result if it's not just print statements
|
|
119
119
|
if (result.length === 0 && ast.body.length > 0) {
|
|
@@ -130,7 +130,7 @@ class REPL {
|
|
|
130
130
|
tempInterpreter.environment = this.interpreter.environment;
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
|
-
const value = tempInterpreter.evaluate(lastStmt.expression);
|
|
133
|
+
const value = await tempInterpreter.evaluate(lastStmt.expression);
|
|
134
134
|
if (value !== undefined && value !== null) {
|
|
135
135
|
console.log(colorize('→ ', 'dim') + colorize(this.interpreter.stringify(value), 'yellow'));
|
|
136
136
|
}
|