vladx 1.3.2 → 1.4.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/package.json +6 -2
- package/programs/main/index.vx +1 -2
- package/programs/main/passports/Alina.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/src/cli/cli.js +8 -6
- package/src/interpreter/interpreter.js +238 -140
- package/src/parser/ast.js +15 -3
- package/src/parser/parser.js +56 -23
- package/src/repl/repl.js +5 -5
- package/programs/main/passports/Vlad.txt +0 -1
package/bin/vladx
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vladx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "VladX - Linguaggio di programmazione con sintassi italiana",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -19,5 +19,9 @@
|
|
|
19
19
|
"interpreter"
|
|
20
20
|
],
|
|
21
21
|
"author": "VladX Team",
|
|
22
|
-
"license": "MIT"
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"better-sqlite3": "^12.6.2",
|
|
25
|
+
"mysql2": "^3.16.2"
|
|
26
|
+
}
|
|
23
27
|
}
|
package/programs/main/index.vx
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"nome":"Alina","eta":"39"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"nome":"Vlad","eta":"25"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
stampa("--- Test Async/Await ---")
|
|
2
|
+
|
|
3
|
+
asincrono funzione aspetta_e_ritorna(ms, val) {
|
|
4
|
+
stampa(`Attendo ${ms}ms...`)
|
|
5
|
+
aspetta(ms) // Nota: 'aspetta' è sincrono bloccante nella mia implementazione attuale, ma simula lavoro.
|
|
6
|
+
// Per testare await vero, avrei bisogno di funzioni async native che ritornano promise.
|
|
7
|
+
// Ma 'asincrono' funzione ritorna Promise, quindi 'attendi' dovrebbe funzionare su di essa.
|
|
8
|
+
stampa("Fatto!")
|
|
9
|
+
ritorna val
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
asincrono funzione main() {
|
|
13
|
+
stampa("Inizio main")
|
|
14
|
+
variabile p = aspetta_e_ritorna(500, 42)
|
|
15
|
+
stampa(`P è una Promise? ${tipo(p) == 'promessa'}`) // Dovrebbe essere vero (promessa)
|
|
16
|
+
|
|
17
|
+
variabile risultato = attendi p
|
|
18
|
+
stampa(`Risultato: ${risultato}`)
|
|
19
|
+
|
|
20
|
+
variabile diretto = attendi aspetta_e_ritorna(200, 100)
|
|
21
|
+
stampa(`Diretto: ${diretto}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
attendi main()
|
|
25
|
+
stampa("Fine script")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
stampa("--- Test Async Advanced ---")
|
|
2
|
+
|
|
3
|
+
// 1. Async Arrow Function
|
|
4
|
+
variabile saluta = asincrono (nome) => {
|
|
5
|
+
aspetta(100)
|
|
6
|
+
ritorna `Ciao, ${nome}!`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// 2. Async Class Method
|
|
10
|
+
classe Lavoratore {
|
|
11
|
+
funzione costruttore(nome) {
|
|
12
|
+
questo.nome = nome
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
asincrono funzione lavora(compito) {
|
|
16
|
+
stampa(`${questo.nome} sta lavorando a: ${compito}...`)
|
|
17
|
+
aspetta(300)
|
|
18
|
+
ritorna `${compito} completato`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
asincrono funzione test() {
|
|
23
|
+
stampa("Test Arrow Function...")
|
|
24
|
+
variabile promessa = saluta("Vlad")
|
|
25
|
+
stampa(`Tipo: ${tipo(promessa)}`)
|
|
26
|
+
stampa(attendi promessa)
|
|
27
|
+
|
|
28
|
+
stampa("\nTest Class Method...")
|
|
29
|
+
variabile v = nuovo Lavoratore("Vlado")
|
|
30
|
+
variabile res = attendi v.lavora("Refactoring")
|
|
31
|
+
stampa(res)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
attendi test()
|
|
35
|
+
stampa("\nFine Test Advanced")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
stampa("--- Test Base64 ---")
|
|
2
|
+
variabile testo = "Ciao VladX"
|
|
3
|
+
variabile enc = Base64.codifica(testo)
|
|
4
|
+
stampa(`Encoded: ${enc}`)
|
|
5
|
+
variabile dec = Base64.decodifica(enc)
|
|
6
|
+
stampa(`Decoded: ${dec}`)
|
|
7
|
+
|
|
8
|
+
stampa("\n--- Test Buffer ---")
|
|
9
|
+
variabile buf = Buffer.from("Hello", "utf8")
|
|
10
|
+
stampa(`Is Buffer: ${Buffer.isBuffer(buf)}`)
|
|
11
|
+
|
|
12
|
+
stampa("\n--- Test SQLite ---")
|
|
13
|
+
variabile db = SQLite.apri("test.db")
|
|
14
|
+
db.esegui("CREATE TABLE IF NOT EXISTS utenti (id INTEGER PRIMARY KEY, nome TEXT)")
|
|
15
|
+
variabile result = db.esegui("INSERT INTO utenti (nome) VALUES ('Marco')")
|
|
16
|
+
stampa(`Inserito ID: ${result.ultimoId}`)
|
|
17
|
+
variabile utenti = db.interroga("SELECT * FROM utenti")
|
|
18
|
+
stampa(`Utenti: ${JSON.stringify(utenti)}`)
|
|
19
|
+
db.chiudi()
|
|
20
|
+
Archivio.elimina("test.db")
|
|
21
|
+
|
|
22
|
+
stampa("\n--- Test MySQL (connection failure expected) ---")
|
|
23
|
+
prova {
|
|
24
|
+
variabile conn = MySQL.connetti({
|
|
25
|
+
host: "127.0.0.1",
|
|
26
|
+
user: "root",
|
|
27
|
+
password: "wrong_password",
|
|
28
|
+
connectTimeout: 2000
|
|
29
|
+
})
|
|
30
|
+
conn.chiudi()
|
|
31
|
+
} cattura (err) {
|
|
32
|
+
stampa(`Errore catturato correttamente: ${err}`)
|
|
33
|
+
}
|
package/src/cli/cli.js
CHANGED
|
@@ -52,7 +52,7 @@ function showVersion() {
|
|
|
52
52
|
console.log(`VladX v${VERSION}`);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function runFile(filePath) {
|
|
55
|
+
async function runFile(filePath) {
|
|
56
56
|
const absolutePath = path.resolve(filePath);
|
|
57
57
|
|
|
58
58
|
if (!fs.existsSync(absolutePath)) {
|
|
@@ -71,7 +71,7 @@ function runFile(filePath) {
|
|
|
71
71
|
|
|
72
72
|
const interpreter = new Interpreter();
|
|
73
73
|
interpreter.currentPath = absolutePath;
|
|
74
|
-
interpreter.interpret(ast);
|
|
74
|
+
await interpreter.interpret(ast);
|
|
75
75
|
} catch (error) {
|
|
76
76
|
console.error(colorize(`✗ Errore: ${error.message}`, 'red'));
|
|
77
77
|
process.exit(1);
|
|
@@ -129,9 +129,11 @@ function showAST(filePath) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
function run(args) {
|
|
132
|
+
async function run(args) {
|
|
133
133
|
if (args.length === 0) {
|
|
134
|
-
startREPL();
|
|
134
|
+
startREPL(); // REPL start is synchronous in loop initialization but internal loop is async, it doesn't return promise of completion?
|
|
135
|
+
// startREPL calls repl.start(). repl.start() sets up listeners and returns.
|
|
136
|
+
// It keeps process alive via readline.
|
|
135
137
|
return;
|
|
136
138
|
}
|
|
137
139
|
|
|
@@ -159,7 +161,7 @@ function run(args) {
|
|
|
159
161
|
console.error(colorize('✗ Errore: Specificare un file da eseguire', 'red'));
|
|
160
162
|
process.exit(1);
|
|
161
163
|
}
|
|
162
|
-
runFile(args[1]);
|
|
164
|
+
await runFile(args[1]);
|
|
163
165
|
break;
|
|
164
166
|
|
|
165
167
|
case 'lex':
|
|
@@ -181,7 +183,7 @@ function run(args) {
|
|
|
181
183
|
default:
|
|
182
184
|
// Assume it's a file path
|
|
183
185
|
if (command.endsWith('.vx') || fs.existsSync(command)) {
|
|
184
|
-
runFile(command);
|
|
186
|
+
await runFile(command);
|
|
185
187
|
} else {
|
|
186
188
|
console.error(colorize(`✗ Comando sconosciuto: ${command}`, 'red'));
|
|
187
189
|
console.log('Usa "vladx --help" per vedere i comandi disponibili.');
|
|
@@ -59,20 +59,31 @@ class ReturnValue {
|
|
|
59
59
|
class BreakSignal { }
|
|
60
60
|
class ContinueSignal { }
|
|
61
61
|
|
|
62
|
+
class VladXPromise {
|
|
63
|
+
constructor(promise) {
|
|
64
|
+
this.promise = promise;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async wait() {
|
|
68
|
+
return await this.promise;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
class VladXFunction {
|
|
63
|
-
constructor(declaration, closure) {
|
|
73
|
+
constructor(declaration, closure, isAsync = false) {
|
|
64
74
|
this.declaration = declaration;
|
|
65
75
|
this.closure = closure;
|
|
76
|
+
this.isAsync = isAsync;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
call(interpreter, args) {
|
|
79
|
+
async call(interpreter, args) {
|
|
69
80
|
const env = new Environment(this.closure);
|
|
70
81
|
for (let i = 0; i < this.declaration.params.length; i++) {
|
|
71
82
|
env.define(this.declaration.params[i], args[i] !== undefined ? args[i] : null);
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
try {
|
|
75
|
-
interpreter.executeBlock(this.declaration.body.body, env);
|
|
86
|
+
await interpreter.executeBlock(this.declaration.body.body, env);
|
|
76
87
|
} catch (e) {
|
|
77
88
|
if (e instanceof ReturnValue) {
|
|
78
89
|
return e.value;
|
|
@@ -84,13 +95,15 @@ class VladXFunction {
|
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
class ArrowFunc {
|
|
87
|
-
constructor(params, body, closure) {
|
|
98
|
+
constructor(params, body, closure, isAsync = false) {
|
|
88
99
|
this.params = params;
|
|
89
100
|
this.body = body;
|
|
90
101
|
this.closure = closure;
|
|
102
|
+
this.isAsync = isAsync;
|
|
91
103
|
}
|
|
104
|
+
// ... call unchanged except it returns result ...
|
|
92
105
|
|
|
93
|
-
call(interpreter, args) {
|
|
106
|
+
async call(interpreter, args) {
|
|
94
107
|
const env = new Environment(this.closure);
|
|
95
108
|
for (let i = 0; i < this.params.length; i++) {
|
|
96
109
|
env.define(this.params[i], args[i] !== undefined ? args[i] : null);
|
|
@@ -98,7 +111,7 @@ class ArrowFunc {
|
|
|
98
111
|
|
|
99
112
|
if (this.body.type === 'BlockStatement') {
|
|
100
113
|
try {
|
|
101
|
-
interpreter.executeBlock(this.body.body, env);
|
|
114
|
+
await interpreter.executeBlock(this.body.body, env);
|
|
102
115
|
} catch (e) {
|
|
103
116
|
if (e instanceof ReturnValue) {
|
|
104
117
|
return e.value;
|
|
@@ -107,11 +120,7 @@ class ArrowFunc {
|
|
|
107
120
|
}
|
|
108
121
|
return null;
|
|
109
122
|
} else {
|
|
110
|
-
|
|
111
|
-
interpreter.environment = env;
|
|
112
|
-
const result = interpreter.evaluate(this.body);
|
|
113
|
-
interpreter.environment = previousEnv;
|
|
114
|
-
return result;
|
|
123
|
+
return await interpreter.evaluate(this.body, env);
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
}
|
|
@@ -163,7 +172,11 @@ class BoundMethod {
|
|
|
163
172
|
this.instance = instance;
|
|
164
173
|
}
|
|
165
174
|
|
|
166
|
-
|
|
175
|
+
get isAsync() {
|
|
176
|
+
return this.method.isAsync;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async call(interpreter, args) {
|
|
167
180
|
const env = new Environment(this.method.closure);
|
|
168
181
|
env.define('questo', this.instance);
|
|
169
182
|
for (let i = 0; i < this.method.declaration.params.length; i++) {
|
|
@@ -171,7 +184,7 @@ class BoundMethod {
|
|
|
171
184
|
}
|
|
172
185
|
|
|
173
186
|
try {
|
|
174
|
-
interpreter.executeBlock(this.method.declaration.body.body, env);
|
|
187
|
+
await interpreter.executeBlock(this.method.declaration.body.body, env);
|
|
175
188
|
} catch (e) {
|
|
176
189
|
if (e instanceof ReturnValue) {
|
|
177
190
|
return e.value;
|
|
@@ -187,6 +200,8 @@ const path = require('path');
|
|
|
187
200
|
const http = require('http');
|
|
188
201
|
const https = require('https');
|
|
189
202
|
const { execSync } = require('child_process');
|
|
203
|
+
const sqlite3 = require('better-sqlite3');
|
|
204
|
+
const mysql = require('mysql2/promise');
|
|
190
205
|
const { Lexer } = require('../lexer/lexer.js');
|
|
191
206
|
const { Parser } = require('../parser/parser.js');
|
|
192
207
|
|
|
@@ -210,7 +225,11 @@ class Interpreter {
|
|
|
210
225
|
});
|
|
211
226
|
|
|
212
227
|
this.globals.define('tipo', {
|
|
213
|
-
call: (_, args) =>
|
|
228
|
+
call: (_, args) => {
|
|
229
|
+
const val = args[0];
|
|
230
|
+
if (val instanceof VladXPromise) return 'promessa';
|
|
231
|
+
return typeof val;
|
|
232
|
+
}
|
|
214
233
|
});
|
|
215
234
|
|
|
216
235
|
this.globals.define('numero', {
|
|
@@ -242,6 +261,21 @@ class Interpreter {
|
|
|
242
261
|
}
|
|
243
262
|
});
|
|
244
263
|
|
|
264
|
+
this.globals.define('Buffer', {
|
|
265
|
+
alloc: { call: (_, args) => Buffer.alloc(args[0]) },
|
|
266
|
+
from: { call: (_, args) => Buffer.from(args[0], args[1]) },
|
|
267
|
+
isBuffer: { call: (_, args) => Buffer.isBuffer(args[0]) }
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
this.globals.define('Base64', {
|
|
271
|
+
codifica: {
|
|
272
|
+
call: (_, args) => Buffer.from(String(args[0])).toString('base64')
|
|
273
|
+
},
|
|
274
|
+
decodifica: {
|
|
275
|
+
call: (_, args) => Buffer.from(String(args[0]), 'base64').toString('utf8')
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
245
279
|
this.globals.define('aspetta', {
|
|
246
280
|
call: (_, args) => {
|
|
247
281
|
const ms = args[0];
|
|
@@ -306,6 +340,68 @@ class Interpreter {
|
|
|
306
340
|
};
|
|
307
341
|
this.globals.define('Archivio', Archivio);
|
|
308
342
|
|
|
343
|
+
// --- DATABASE (SQLite) ---
|
|
344
|
+
const SQLite = {
|
|
345
|
+
apri: {
|
|
346
|
+
call: (_, args) => {
|
|
347
|
+
const db = new sqlite3(args[0]);
|
|
348
|
+
return {
|
|
349
|
+
interroga: {
|
|
350
|
+
call: (_, qArgs) => {
|
|
351
|
+
try {
|
|
352
|
+
return db.prepare(qArgs[0]).all(qArgs.slice(1));
|
|
353
|
+
} catch (e) {
|
|
354
|
+
throw new Error(`Errore query SQLite: ${e.message}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
esegui: {
|
|
359
|
+
call: (_, qArgs) => {
|
|
360
|
+
try {
|
|
361
|
+
const result = db.prepare(qArgs[0]).run(qArgs.slice(1));
|
|
362
|
+
return {
|
|
363
|
+
modifiche: result.changes,
|
|
364
|
+
ultimoId: result.lastInsertRowid
|
|
365
|
+
};
|
|
366
|
+
} catch (e) {
|
|
367
|
+
throw new Error(`Errore esecuzione SQLite: ${e.message}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
chiudi: { call: () => db.close() }
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
this.globals.define('SQLite', SQLite);
|
|
377
|
+
|
|
378
|
+
// --- DATABASE (MySQL) ---
|
|
379
|
+
const MySQL = {
|
|
380
|
+
connetti: {
|
|
381
|
+
call: async (_, args) => {
|
|
382
|
+
try {
|
|
383
|
+
const conn = await mysql.createConnection(args[0]);
|
|
384
|
+
return {
|
|
385
|
+
interroga: {
|
|
386
|
+
call: async (_, qArgs) => {
|
|
387
|
+
try {
|
|
388
|
+
const [rows] = await conn.execute(qArgs[0], qArgs.slice(1));
|
|
389
|
+
return rows;
|
|
390
|
+
} catch (e) {
|
|
391
|
+
throw new Error(`Errore query MySQL: ${e.message}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
chiudi: { call: async () => await conn.end() }
|
|
396
|
+
};
|
|
397
|
+
} catch (e) {
|
|
398
|
+
throw new Error(`Errore connessione MySQL: ${e.message}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
this.globals.define('MySQL', MySQL);
|
|
404
|
+
|
|
309
405
|
// --- SISTEMA (Process/OS) ---
|
|
310
406
|
const Sistema = {
|
|
311
407
|
esegui: {
|
|
@@ -376,11 +472,11 @@ class Interpreter {
|
|
|
376
472
|
this.globals.define('Rete', Rete);
|
|
377
473
|
}
|
|
378
474
|
|
|
379
|
-
interpret(program) {
|
|
475
|
+
async interpret(program) {
|
|
380
476
|
this.output = [];
|
|
381
477
|
try {
|
|
382
478
|
for (const statement of program.body) {
|
|
383
|
-
this.execute(statement);
|
|
479
|
+
await this.execute(statement, this.globals);
|
|
384
480
|
}
|
|
385
481
|
} catch (error) {
|
|
386
482
|
if (error instanceof ReturnValue) {
|
|
@@ -391,59 +487,59 @@ class Interpreter {
|
|
|
391
487
|
return this.output;
|
|
392
488
|
}
|
|
393
489
|
|
|
394
|
-
execute(node) {
|
|
490
|
+
async execute(node, env) {
|
|
395
491
|
switch (node.type) {
|
|
396
492
|
case 'VariableDeclaration':
|
|
397
|
-
return this.executeVariableDeclaration(node);
|
|
493
|
+
return await this.executeVariableDeclaration(node, env);
|
|
398
494
|
case 'FunctionDeclaration':
|
|
399
|
-
return this.executeFunctionDeclaration(node);
|
|
495
|
+
return this.executeFunctionDeclaration(node, env);
|
|
400
496
|
case 'ClassDeclaration':
|
|
401
|
-
return this.executeClassDeclaration(node);
|
|
497
|
+
return this.executeClassDeclaration(node, env);
|
|
402
498
|
case 'BlockStatement':
|
|
403
|
-
return this.executeBlock(node.body, new Environment(
|
|
499
|
+
return await this.executeBlock(node.body, new Environment(env));
|
|
404
500
|
case 'IfStatement':
|
|
405
|
-
return this.executeIfStatement(node);
|
|
501
|
+
return await this.executeIfStatement(node, env);
|
|
406
502
|
case 'WhileStatement':
|
|
407
|
-
return this.executeWhileStatement(node);
|
|
503
|
+
return await this.executeWhileStatement(node, env);
|
|
408
504
|
case 'ForStatement':
|
|
409
|
-
return this.executeForStatement(node);
|
|
505
|
+
return await this.executeForStatement(node, env);
|
|
410
506
|
case 'ReturnStatement':
|
|
411
|
-
throw new ReturnValue(node.argument ? this.evaluate(node.argument) : null);
|
|
507
|
+
throw new ReturnValue(node.argument ? await this.evaluate(node.argument, env) : null);
|
|
412
508
|
case 'BreakStatement':
|
|
413
509
|
throw new BreakSignal();
|
|
414
510
|
case 'ContinueStatement':
|
|
415
511
|
throw new ContinueSignal();
|
|
416
512
|
case 'TryStatement':
|
|
417
|
-
return this.executeTryStatement(node);
|
|
513
|
+
return await this.executeTryStatement(node, env);
|
|
418
514
|
case 'ThrowStatement':
|
|
419
|
-
return this.executeThrowStatement(node);
|
|
515
|
+
return await this.executeThrowStatement(node, env);
|
|
420
516
|
case 'ExportNamedDeclaration':
|
|
421
|
-
return this.executeExportNamedDeclaration(node);
|
|
517
|
+
return await this.executeExportNamedDeclaration(node, env);
|
|
422
518
|
case 'ImportDeclaration':
|
|
423
|
-
return this.executeImportDeclaration(node);
|
|
519
|
+
return await this.executeImportDeclaration(node, env);
|
|
424
520
|
case 'PrintStatement':
|
|
425
|
-
return this.executePrintStatement(node);
|
|
521
|
+
return await this.executePrintStatement(node, env);
|
|
426
522
|
case 'ExpressionStatement':
|
|
427
|
-
return this.evaluate(node.expression);
|
|
523
|
+
return await this.evaluate(node.expression, env);
|
|
428
524
|
default:
|
|
429
525
|
throw new Error(`Tipo statement sconosciuto: ${node.type}`);
|
|
430
526
|
}
|
|
431
527
|
}
|
|
432
528
|
|
|
433
|
-
executeVariableDeclaration(node) {
|
|
434
|
-
const value = node.value ? this.evaluate(node.value) : null;
|
|
435
|
-
|
|
529
|
+
async executeVariableDeclaration(node, env) {
|
|
530
|
+
const value = node.value ? await this.evaluate(node.value, env) : null;
|
|
531
|
+
env.define(node.name, value, node.isConstant);
|
|
436
532
|
}
|
|
437
533
|
|
|
438
|
-
executeFunctionDeclaration(node) {
|
|
439
|
-
const func = new VladXFunction(node,
|
|
440
|
-
|
|
534
|
+
executeFunctionDeclaration(node, env) {
|
|
535
|
+
const func = new VladXFunction(node, env, node.isAsync);
|
|
536
|
+
env.define(node.name, func);
|
|
441
537
|
}
|
|
442
538
|
|
|
443
|
-
executeClassDeclaration(node) {
|
|
539
|
+
executeClassDeclaration(node, env) {
|
|
444
540
|
let superclass = null;
|
|
445
541
|
if (node.superclass) {
|
|
446
|
-
const superclassValue =
|
|
542
|
+
const superclassValue = env.get(node.superclass);
|
|
447
543
|
if (!(superclassValue instanceof VladXClass)) {
|
|
448
544
|
throw new Error(`"${node.superclass}" non è una classe`);
|
|
449
545
|
}
|
|
@@ -452,40 +548,34 @@ class Interpreter {
|
|
|
452
548
|
|
|
453
549
|
const methods = new Map();
|
|
454
550
|
if (node.constructor) {
|
|
455
|
-
methods.set('costruttore', new VladXFunction(node.constructor,
|
|
551
|
+
methods.set('costruttore', new VladXFunction(node.constructor, env, false));
|
|
456
552
|
}
|
|
457
553
|
for (const method of node.methods) {
|
|
458
|
-
methods.set(method.name, new VladXFunction(method,
|
|
554
|
+
methods.set(method.name, new VladXFunction(method, env, method.isAsync));
|
|
459
555
|
}
|
|
460
556
|
|
|
461
557
|
const klass = new VladXClass(node.name, superclass, node.constructor, methods);
|
|
462
|
-
|
|
558
|
+
env.define(node.name, klass);
|
|
463
559
|
}
|
|
464
560
|
|
|
465
|
-
executeBlock(statements, env) {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
try {
|
|
469
|
-
for (const statement of statements) {
|
|
470
|
-
this.execute(statement);
|
|
471
|
-
}
|
|
472
|
-
} finally {
|
|
473
|
-
this.environment = previousEnv;
|
|
561
|
+
async executeBlock(statements, env) {
|
|
562
|
+
for (const statement of statements) {
|
|
563
|
+
await this.execute(statement, env);
|
|
474
564
|
}
|
|
475
565
|
}
|
|
476
566
|
|
|
477
|
-
executeIfStatement(node) {
|
|
478
|
-
if (this.isTruthy(this.evaluate(node.condition))) {
|
|
479
|
-
this.execute(node.consequent);
|
|
567
|
+
async executeIfStatement(node, env) {
|
|
568
|
+
if (this.isTruthy(await this.evaluate(node.condition, env))) {
|
|
569
|
+
await this.execute(node.consequent, env);
|
|
480
570
|
} else if (node.alternate) {
|
|
481
|
-
this.execute(node.alternate);
|
|
571
|
+
await this.execute(node.alternate, env);
|
|
482
572
|
}
|
|
483
573
|
}
|
|
484
574
|
|
|
485
|
-
executeWhileStatement(node) {
|
|
486
|
-
while (this.isTruthy(this.evaluate(node.condition))) {
|
|
575
|
+
async executeWhileStatement(node, env) {
|
|
576
|
+
while (this.isTruthy(await this.evaluate(node.condition, env))) {
|
|
487
577
|
try {
|
|
488
|
-
this.execute(node.body);
|
|
578
|
+
await this.execute(node.body, env);
|
|
489
579
|
} catch (e) {
|
|
490
580
|
if (e instanceof BreakSignal) break;
|
|
491
581
|
if (e instanceof ContinueSignal) continue;
|
|
@@ -494,35 +584,33 @@ class Interpreter {
|
|
|
494
584
|
}
|
|
495
585
|
}
|
|
496
586
|
|
|
497
|
-
executeForStatement(node) {
|
|
498
|
-
const forEnv = new Environment(
|
|
499
|
-
const previousEnv = this.environment;
|
|
500
|
-
this.environment = forEnv;
|
|
587
|
+
async executeForStatement(node, env) {
|
|
588
|
+
const forEnv = new Environment(env);
|
|
501
589
|
|
|
502
590
|
try {
|
|
503
|
-
if (node.init) this.execute(node.init);
|
|
591
|
+
if (node.init) await this.execute(node.init, forEnv);
|
|
504
592
|
|
|
505
|
-
while (!node.condition || this.isTruthy(this.evaluate(node.condition))) {
|
|
593
|
+
while (!node.condition || this.isTruthy(await this.evaluate(node.condition, forEnv))) {
|
|
506
594
|
try {
|
|
507
|
-
this.execute(node.body);
|
|
595
|
+
await this.execute(node.body, forEnv);
|
|
508
596
|
} catch (e) {
|
|
509
597
|
if (e instanceof BreakSignal) break;
|
|
510
598
|
if (e instanceof ContinueSignal) {
|
|
511
|
-
if (node.update) this.evaluate(node.update);
|
|
599
|
+
if (node.update) await this.evaluate(node.update, forEnv);
|
|
512
600
|
continue;
|
|
513
601
|
}
|
|
514
602
|
throw e;
|
|
515
603
|
}
|
|
516
|
-
if (node.update) this.evaluate(node.update);
|
|
604
|
+
if (node.update) await this.evaluate(node.update, forEnv);
|
|
517
605
|
}
|
|
518
606
|
} finally {
|
|
519
|
-
this.environment
|
|
607
|
+
// No need to restore since we didn't mutate this.environment
|
|
520
608
|
}
|
|
521
609
|
}
|
|
522
610
|
|
|
523
|
-
executeTryStatement(node) {
|
|
611
|
+
async executeTryStatement(node, env) {
|
|
524
612
|
try {
|
|
525
|
-
this.execute(node.block);
|
|
613
|
+
await this.execute(node.block, env);
|
|
526
614
|
} catch (error) {
|
|
527
615
|
// Se è un ReturnValue o Break/Continue Signal, non catturarlo qui
|
|
528
616
|
if (error instanceof ReturnValue || error instanceof BreakSignal || error instanceof ContinueSignal) {
|
|
@@ -530,41 +618,36 @@ class Interpreter {
|
|
|
530
618
|
}
|
|
531
619
|
|
|
532
620
|
if (node.handler) {
|
|
533
|
-
const catchEnv = new Environment(
|
|
621
|
+
const catchEnv = new Environment(env);
|
|
534
622
|
// Il valore errore può essere quello lanciato da LANCIA o un errore JS
|
|
623
|
+
// Se è un errore JS, potrebbe essere un Error object o stringa
|
|
535
624
|
const errorValue = error instanceof Error ? error.message : error;
|
|
536
625
|
catchEnv.define(node.handler.param, errorValue);
|
|
537
626
|
|
|
538
|
-
|
|
539
|
-
this.environment = catchEnv;
|
|
540
|
-
try {
|
|
541
|
-
this.execute(node.handler.body);
|
|
542
|
-
} finally {
|
|
543
|
-
this.environment = previousEnv;
|
|
544
|
-
}
|
|
627
|
+
await this.execute(node.handler.body, catchEnv);
|
|
545
628
|
} else if (!node.finalizer) {
|
|
546
629
|
throw error;
|
|
547
630
|
}
|
|
548
631
|
} finally {
|
|
549
632
|
if (node.finalizer) {
|
|
550
|
-
this.execute(node.finalizer);
|
|
633
|
+
await this.execute(node.finalizer, env);
|
|
551
634
|
}
|
|
552
635
|
}
|
|
553
636
|
}
|
|
554
637
|
|
|
555
|
-
executeThrowStatement(node) {
|
|
556
|
-
const value = this.evaluate(node.argument);
|
|
638
|
+
async executeThrowStatement(node, env) {
|
|
639
|
+
const value = await this.evaluate(node.argument, env);
|
|
557
640
|
throw value;
|
|
558
641
|
}
|
|
559
642
|
|
|
560
|
-
executeExportNamedDeclaration(node) {
|
|
561
|
-
this.execute(node.declaration);
|
|
643
|
+
async executeExportNamedDeclaration(node, env) {
|
|
644
|
+
await this.execute(node.declaration, env);
|
|
562
645
|
const name = node.declaration.name;
|
|
563
|
-
const value =
|
|
646
|
+
const value = env.get(name);
|
|
564
647
|
this.exportedSymbols.set(name, value);
|
|
565
648
|
}
|
|
566
649
|
|
|
567
|
-
executeImportDeclaration(node) {
|
|
650
|
+
async executeImportDeclaration(node, env) {
|
|
568
651
|
const sourcePath = node.source;
|
|
569
652
|
let fullPath;
|
|
570
653
|
|
|
@@ -601,7 +684,7 @@ class Interpreter {
|
|
|
601
684
|
subInterpreter.modules = this.modules;
|
|
602
685
|
subInterpreter.currentPath = fullPath;
|
|
603
686
|
|
|
604
|
-
subInterpreter.interpret(program);
|
|
687
|
+
await subInterpreter.interpret(program);
|
|
605
688
|
moduleExports = subInterpreter.exportedSymbols;
|
|
606
689
|
this.modules.set(fullPath, moduleExports);
|
|
607
690
|
}
|
|
@@ -609,21 +692,21 @@ class Interpreter {
|
|
|
609
692
|
// Import specified symbols
|
|
610
693
|
for (const specifier of node.specifiers) {
|
|
611
694
|
if (moduleExports.has(specifier)) {
|
|
612
|
-
|
|
695
|
+
env.define(specifier, moduleExports.get(specifier));
|
|
613
696
|
} else {
|
|
614
697
|
throw new Error(`Il modulo "${sourcePath}" non esporta "${specifier}"`);
|
|
615
698
|
}
|
|
616
699
|
}
|
|
617
700
|
}
|
|
618
701
|
|
|
619
|
-
executePrintStatement(node) {
|
|
620
|
-
const value = this.evaluate(node.argument);
|
|
702
|
+
async executePrintStatement(node, env) {
|
|
703
|
+
const value = await this.evaluate(node.argument, env);
|
|
621
704
|
const output = this.stringify(value);
|
|
622
705
|
console.log(output);
|
|
623
706
|
this.output.push(output);
|
|
624
707
|
}
|
|
625
708
|
|
|
626
|
-
evaluate(node) {
|
|
709
|
+
async evaluate(node, env) {
|
|
627
710
|
switch (node.type) {
|
|
628
711
|
case 'NumericLiteral':
|
|
629
712
|
return node.value;
|
|
@@ -634,47 +717,57 @@ class Interpreter {
|
|
|
634
717
|
case 'NullLiteral':
|
|
635
718
|
return null;
|
|
636
719
|
case 'Identifier':
|
|
637
|
-
return
|
|
720
|
+
return env.get(node.name);
|
|
638
721
|
case 'ThisExpression':
|
|
639
|
-
return
|
|
722
|
+
return env.get('questo');
|
|
640
723
|
case 'ArrayLiteral':
|
|
641
|
-
return node.elements.map(el => this.evaluate(el));
|
|
724
|
+
return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
|
|
642
725
|
case 'ObjectLiteral':
|
|
643
726
|
const obj = {};
|
|
644
727
|
for (const prop of node.properties) {
|
|
645
|
-
obj[prop.key] = this.evaluate(prop.value);
|
|
728
|
+
obj[prop.key] = await this.evaluate(prop.value, env);
|
|
646
729
|
}
|
|
647
730
|
return obj;
|
|
648
731
|
case 'BinaryExpression':
|
|
649
|
-
return this.evaluateBinaryExpression(node);
|
|
732
|
+
return await this.evaluateBinaryExpression(node, env);
|
|
650
733
|
case 'LogicalExpression':
|
|
651
|
-
return this.evaluateLogicalExpression(node);
|
|
734
|
+
return await this.evaluateLogicalExpression(node, env);
|
|
652
735
|
case 'UnaryExpression':
|
|
653
|
-
return this.evaluateUnaryExpression(node);
|
|
736
|
+
return await this.evaluateUnaryExpression(node, env);
|
|
654
737
|
case 'AssignmentExpression':
|
|
655
|
-
return this.evaluateAssignment(node);
|
|
738
|
+
return await this.evaluateAssignment(node, env);
|
|
656
739
|
case 'UpdateExpression':
|
|
657
|
-
return this.evaluateUpdateExpression(node);
|
|
740
|
+
return await this.evaluateUpdateExpression(node, env);
|
|
658
741
|
case 'CallExpression':
|
|
659
|
-
return this.evaluateCallExpression(node);
|
|
742
|
+
return await this.evaluateCallExpression(node, env);
|
|
660
743
|
case 'NewExpression':
|
|
661
|
-
return this.evaluateNewExpression(node);
|
|
744
|
+
return await this.evaluateNewExpression(node, env);
|
|
662
745
|
case 'MemberExpression':
|
|
663
|
-
return this.evaluateMemberExpression(node);
|
|
746
|
+
return await this.evaluateMemberExpression(node, env);
|
|
664
747
|
case 'ArrowFunction':
|
|
665
|
-
return new ArrowFunc(node.params, node.body,
|
|
748
|
+
return new ArrowFunc(node.params, node.body, env, node.isAsync);
|
|
666
749
|
case 'TemplateLiteral':
|
|
667
|
-
return this.evaluateTemplateLiteral(node);
|
|
750
|
+
return await this.evaluateTemplateLiteral(node, env);
|
|
751
|
+
case 'AwaitExpression':
|
|
752
|
+
return await this.evaluateAwaitExpression(node, env);
|
|
668
753
|
default:
|
|
669
754
|
throw new Error(`Tipo espressione sconosciuto: ${node.type}`);
|
|
670
755
|
}
|
|
671
756
|
}
|
|
672
757
|
|
|
673
|
-
|
|
758
|
+
async evaluateAwaitExpression(node, env) {
|
|
759
|
+
const value = await this.evaluate(node.argument, env);
|
|
760
|
+
if (value instanceof VladXPromise) {
|
|
761
|
+
return await value.wait();
|
|
762
|
+
}
|
|
763
|
+
return value;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
async evaluateTemplateLiteral(node, env) {
|
|
674
767
|
let result = node.quasis[0];
|
|
675
768
|
|
|
676
769
|
for (let i = 0; i < node.expressions.length; i++) {
|
|
677
|
-
const value = this.evaluate(node.expressions[i]);
|
|
770
|
+
const value = await this.evaluate(node.expressions[i], env);
|
|
678
771
|
result += this.stringify(value);
|
|
679
772
|
result += node.quasis[i + 1];
|
|
680
773
|
}
|
|
@@ -682,9 +775,9 @@ class Interpreter {
|
|
|
682
775
|
return result;
|
|
683
776
|
}
|
|
684
777
|
|
|
685
|
-
evaluateBinaryExpression(node) {
|
|
686
|
-
const left = this.evaluate(node.left);
|
|
687
|
-
const right = this.evaluate(node.right);
|
|
778
|
+
async evaluateBinaryExpression(node, env) {
|
|
779
|
+
const left = await this.evaluate(node.left, env);
|
|
780
|
+
const right = await this.evaluate(node.right, env);
|
|
688
781
|
|
|
689
782
|
switch (node.operator) {
|
|
690
783
|
case '+':
|
|
@@ -709,17 +802,17 @@ class Interpreter {
|
|
|
709
802
|
}
|
|
710
803
|
}
|
|
711
804
|
|
|
712
|
-
evaluateLogicalExpression(node) {
|
|
713
|
-
const left = this.evaluate(node.left);
|
|
805
|
+
async evaluateLogicalExpression(node, env) {
|
|
806
|
+
const left = await this.evaluate(node.left, env);
|
|
714
807
|
if (node.operator === '||') {
|
|
715
|
-
return this.isTruthy(left) ? left : this.evaluate(node.right);
|
|
808
|
+
return this.isTruthy(left) ? left : await this.evaluate(node.right, env);
|
|
716
809
|
} else {
|
|
717
|
-
return !this.isTruthy(left) ? left : this.evaluate(node.right);
|
|
810
|
+
return !this.isTruthy(left) ? left : await this.evaluate(node.right, env);
|
|
718
811
|
}
|
|
719
812
|
}
|
|
720
813
|
|
|
721
|
-
evaluateUnaryExpression(node) {
|
|
722
|
-
const argument = this.evaluate(node.argument);
|
|
814
|
+
async evaluateUnaryExpression(node, env) {
|
|
815
|
+
const argument = await this.evaluate(node.argument, env);
|
|
723
816
|
switch (node.operator) {
|
|
724
817
|
case '-': return -argument;
|
|
725
818
|
case '!': return !this.isTruthy(argument);
|
|
@@ -728,20 +821,20 @@ class Interpreter {
|
|
|
728
821
|
}
|
|
729
822
|
}
|
|
730
823
|
|
|
731
|
-
evaluateAssignment(node) {
|
|
732
|
-
let value = this.evaluate(node.right);
|
|
824
|
+
async evaluateAssignment(node, env) {
|
|
825
|
+
let value = await this.evaluate(node.right, env);
|
|
733
826
|
|
|
734
827
|
if (node.operator === '+=' || node.operator === '-=') {
|
|
735
|
-
const current = this.evaluate(node.left);
|
|
828
|
+
const current = await this.evaluate(node.left, env);
|
|
736
829
|
value = node.operator === '+=' ? current + value : current - value;
|
|
737
830
|
}
|
|
738
831
|
|
|
739
832
|
if (node.left.type === 'Identifier') {
|
|
740
|
-
|
|
833
|
+
env.set(node.left.name, value);
|
|
741
834
|
} else if (node.left.type === 'MemberExpression') {
|
|
742
|
-
const obj = this.evaluate(node.left.object);
|
|
835
|
+
const obj = await this.evaluate(node.left.object, env);
|
|
743
836
|
const prop = node.left.computed
|
|
744
|
-
? this.evaluate(node.left.property)
|
|
837
|
+
? await this.evaluate(node.left.property, env)
|
|
745
838
|
: node.left.property.name;
|
|
746
839
|
|
|
747
840
|
// Handle class instances
|
|
@@ -755,30 +848,36 @@ class Interpreter {
|
|
|
755
848
|
return value;
|
|
756
849
|
}
|
|
757
850
|
|
|
758
|
-
evaluateUpdateExpression(node) {
|
|
759
|
-
const current = this.evaluate(node.argument);
|
|
851
|
+
async evaluateUpdateExpression(node, env) {
|
|
852
|
+
const current = await this.evaluate(node.argument, env);
|
|
760
853
|
const newValue = node.operator === '++' ? current + 1 : current - 1;
|
|
761
854
|
|
|
762
855
|
if (node.argument.type === 'Identifier') {
|
|
763
|
-
|
|
856
|
+
env.set(node.argument.name, newValue);
|
|
764
857
|
}
|
|
765
858
|
|
|
766
859
|
return node.prefix ? newValue : current;
|
|
767
860
|
}
|
|
768
861
|
|
|
769
|
-
evaluateCallExpression(node) {
|
|
770
|
-
const callee = this.evaluate(node.callee);
|
|
771
|
-
const args = node.arguments.map(
|
|
862
|
+
async evaluateCallExpression(node, env) {
|
|
863
|
+
const callee = await this.evaluate(node.callee, env);
|
|
864
|
+
const args = await Promise.all(node.arguments.map(el => this.evaluate(el, env)));
|
|
772
865
|
|
|
773
866
|
if (callee && typeof callee.call === 'function') {
|
|
774
|
-
|
|
867
|
+
const result = callee.call(this, args);
|
|
868
|
+
if (callee.isAsync) {
|
|
869
|
+
// If async, return the Promise wrapped in a box
|
|
870
|
+
// to prevent the interpreter's automatic await
|
|
871
|
+
return new VladXPromise(result);
|
|
872
|
+
}
|
|
873
|
+
return await result;
|
|
775
874
|
}
|
|
776
875
|
|
|
777
876
|
throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
|
|
778
877
|
}
|
|
779
878
|
|
|
780
|
-
evaluateNewExpression(node) {
|
|
781
|
-
const klass = this.evaluate(node.callee);
|
|
879
|
+
async evaluateNewExpression(node, env) {
|
|
880
|
+
const klass = await this.evaluate(node.callee, env);
|
|
782
881
|
|
|
783
882
|
if (!(klass instanceof VladXClass)) {
|
|
784
883
|
throw new Error(`"${node.callee.name || 'valore'}" non è una classe`);
|
|
@@ -789,23 +888,23 @@ class Interpreter {
|
|
|
789
888
|
|
|
790
889
|
if (constructor) {
|
|
791
890
|
const boundConstructor = new BoundMethod(constructor, instance);
|
|
792
|
-
const args = node.arguments.map(
|
|
793
|
-
boundConstructor.call(this, args);
|
|
891
|
+
const args = await Promise.all(node.arguments.map(el => this.evaluate(el, env)));
|
|
892
|
+
await boundConstructor.call(this, args);
|
|
794
893
|
}
|
|
795
894
|
|
|
796
895
|
return instance;
|
|
797
896
|
}
|
|
798
897
|
|
|
799
|
-
evaluateMemberExpression(node) {
|
|
800
|
-
const obj = this.evaluate(node.object);
|
|
801
|
-
if (obj === null || obj === undefined) {
|
|
802
|
-
throw new Error('Impossibile accedere a proprietà di null/undefined');
|
|
803
|
-
}
|
|
804
|
-
|
|
898
|
+
async evaluateMemberExpression(node, env) {
|
|
899
|
+
const obj = await this.evaluate(node.object, env);
|
|
805
900
|
const prop = node.computed
|
|
806
|
-
? this.evaluate(node.property)
|
|
901
|
+
? await this.evaluate(node.property, env)
|
|
807
902
|
: node.property.name;
|
|
808
903
|
|
|
904
|
+
if (obj === undefined || obj === null) {
|
|
905
|
+
throw new Error(`Impossibile leggere proprietà "${prop}" di ${obj}`);
|
|
906
|
+
}
|
|
907
|
+
|
|
809
908
|
// Handle array methods
|
|
810
909
|
if (Array.isArray(obj)) {
|
|
811
910
|
if (prop === 'aggiungi' || prop === 'push') {
|
|
@@ -819,7 +918,6 @@ class Interpreter {
|
|
|
819
918
|
}
|
|
820
919
|
}
|
|
821
920
|
|
|
822
|
-
// Handle class instances
|
|
823
921
|
if (obj instanceof VladXInstance) {
|
|
824
922
|
return obj.get(prop);
|
|
825
923
|
}
|
package/src/parser/ast.js
CHANGED
|
@@ -28,11 +28,12 @@ class VariableDeclaration extends ASTNode {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
class FunctionDeclaration extends ASTNode {
|
|
31
|
-
constructor(name, params, body) {
|
|
31
|
+
constructor(name, params, body, isAsync = false) {
|
|
32
32
|
super('FunctionDeclaration');
|
|
33
33
|
this.name = name;
|
|
34
34
|
this.params = params;
|
|
35
35
|
this.body = body;
|
|
36
|
+
this.isAsync = isAsync;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -60,6 +61,13 @@ class NewExpression extends ASTNode {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
class AwaitExpression extends ASTNode {
|
|
65
|
+
constructor(argument) {
|
|
66
|
+
super('AwaitExpression');
|
|
67
|
+
this.argument = argument;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
// === Statements ===
|
|
64
72
|
class BlockStatement extends ASTNode {
|
|
65
73
|
constructor(body) {
|
|
@@ -76,6 +84,8 @@ class IfStatement extends ASTNode {
|
|
|
76
84
|
this.alternate = alternate;
|
|
77
85
|
}
|
|
78
86
|
}
|
|
87
|
+
// ... skip ...
|
|
88
|
+
|
|
79
89
|
|
|
80
90
|
class WhileStatement extends ASTNode {
|
|
81
91
|
constructor(condition, body) {
|
|
@@ -263,10 +273,11 @@ class MemberExpression extends ASTNode {
|
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
class ArrowFunction extends ASTNode {
|
|
266
|
-
constructor(params, body) {
|
|
276
|
+
constructor(params, body, isAsync = false) {
|
|
267
277
|
super('ArrowFunction');
|
|
268
278
|
this.params = params;
|
|
269
279
|
this.body = body;
|
|
280
|
+
this.isAsync = isAsync;
|
|
270
281
|
}
|
|
271
282
|
}
|
|
272
283
|
|
|
@@ -350,5 +361,6 @@ module.exports = {
|
|
|
350
361
|
LogicalExpression,
|
|
351
362
|
ThisExpression,
|
|
352
363
|
NewExpression,
|
|
353
|
-
TemplateLiteral
|
|
364
|
+
TemplateLiteral,
|
|
365
|
+
AwaitExpression
|
|
354
366
|
};
|
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) {
|
|
@@ -92,25 +98,16 @@ class Parser {
|
|
|
92
98
|
return new AST.VariableDeclaration(name, value, isConstant);
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "importa"');
|
|
97
|
-
const specifiers = [];
|
|
98
|
-
if (!this.check(TokenType.GRAFFA_CHIUSA)) {
|
|
99
|
-
do {
|
|
100
|
-
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome import atteso').value;
|
|
101
|
-
specifiers.push(name);
|
|
102
|
-
} while (this.match(TokenType.VIRGOLA));
|
|
103
|
-
}
|
|
104
|
-
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo i nomi importati');
|
|
105
|
-
this.consume(TokenType.DA, 'Attesa parola chiave "da" dopo gli import');
|
|
106
|
-
const source = this.consume(TokenType.STRINGA, 'Percorso modulo atteso come stringa').value;
|
|
107
|
-
this.match(TokenType.PUNTO_VIRGOLA);
|
|
108
|
-
return new AST.ImportDeclaration(specifiers, source);
|
|
109
|
-
}
|
|
101
|
+
// ... imports ...
|
|
110
102
|
|
|
111
103
|
exportDeclaration() {
|
|
104
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
105
|
+
this.consume(TokenType.FUNZIONE, 'Atteso "funzione" dopo "asincrono"');
|
|
106
|
+
const decl = this.functionDeclaration(true);
|
|
107
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
108
|
+
}
|
|
112
109
|
if (this.match(TokenType.FUNZIONE)) {
|
|
113
|
-
const decl = this.functionDeclaration();
|
|
110
|
+
const decl = this.functionDeclaration(false);
|
|
114
111
|
return new AST.ExportNamedDeclaration(decl);
|
|
115
112
|
}
|
|
116
113
|
if (this.match(TokenType.CLASSE)) {
|
|
@@ -128,7 +125,7 @@ class Parser {
|
|
|
128
125
|
throw new ParserError('Atteso funzione, classe o variabile dopo "esporta"', this.peek());
|
|
129
126
|
}
|
|
130
127
|
|
|
131
|
-
functionDeclaration() {
|
|
128
|
+
functionDeclaration(isAsync = false) {
|
|
132
129
|
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome funzione atteso').value;
|
|
133
130
|
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome funzione');
|
|
134
131
|
const params = [];
|
|
@@ -140,7 +137,7 @@ class Parser {
|
|
|
140
137
|
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo parametri');
|
|
141
138
|
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" prima del corpo funzione');
|
|
142
139
|
const body = this.blockStatement();
|
|
143
|
-
return new AST.FunctionDeclaration(name, params, body);
|
|
140
|
+
return new AST.FunctionDeclaration(name, params, body, isAsync);
|
|
144
141
|
}
|
|
145
142
|
|
|
146
143
|
classDeclaration() {
|
|
@@ -164,6 +161,11 @@ class Parser {
|
|
|
164
161
|
const methods = [];
|
|
165
162
|
|
|
166
163
|
while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
164
|
+
let isAsync = false;
|
|
165
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
166
|
+
isAsync = true;
|
|
167
|
+
}
|
|
168
|
+
|
|
167
169
|
if (this.match(TokenType.FUNZIONE)) {
|
|
168
170
|
const funcName = this.consume(TokenType.IDENTIFICATORE, 'Nome metodo atteso').value;
|
|
169
171
|
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome metodo');
|
|
@@ -178,11 +180,13 @@ class Parser {
|
|
|
178
180
|
const body = this.blockStatement();
|
|
179
181
|
|
|
180
182
|
if (funcName === 'costruttore') {
|
|
181
|
-
|
|
183
|
+
if (isAsync) throw new ParserError('Il costruttore non può essere asincrono', this.previous());
|
|
184
|
+
constructor = new AST.FunctionDeclaration('costruttore', params, body, false);
|
|
182
185
|
} else {
|
|
183
|
-
methods.push(new AST.FunctionDeclaration(funcName, params, body));
|
|
186
|
+
methods.push(new AST.FunctionDeclaration(funcName, params, body, isAsync));
|
|
184
187
|
}
|
|
185
188
|
} else {
|
|
189
|
+
if (isAsync) throw new ParserError('Atteso metodo dopo "asincrono"', this.peek());
|
|
186
190
|
throw new ParserError('Atteso metodo nella classe', this.peek());
|
|
187
191
|
}
|
|
188
192
|
}
|
|
@@ -426,6 +430,11 @@ class Parser {
|
|
|
426
430
|
}
|
|
427
431
|
|
|
428
432
|
unary() {
|
|
433
|
+
if (this.match(TokenType.ATTENDI)) {
|
|
434
|
+
const argument = this.unary();
|
|
435
|
+
return new AST.AwaitExpression(argument);
|
|
436
|
+
}
|
|
437
|
+
|
|
429
438
|
if (this.match(TokenType.NON, TokenType.MENO)) {
|
|
430
439
|
const operator = this.previous().value === 'non' ? '!' : this.previous().value;
|
|
431
440
|
const argument = this.unary();
|
|
@@ -548,6 +557,30 @@ class Parser {
|
|
|
548
557
|
return this.objectLiteral();
|
|
549
558
|
}
|
|
550
559
|
|
|
560
|
+
if (this.match(TokenType.ASINCRONO)) {
|
|
561
|
+
// Async Arrow Function: asincrono (param) => ...
|
|
562
|
+
if (this.match(TokenType.PARENTESI_APERTA)) {
|
|
563
|
+
const params = [];
|
|
564
|
+
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
565
|
+
do {
|
|
566
|
+
params.push(this.consume(TokenType.IDENTIFICATORE, 'Nome parametro atteso').value);
|
|
567
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
568
|
+
}
|
|
569
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo parametri');
|
|
570
|
+
|
|
571
|
+
if (this.match(TokenType.FRECCIA)) {
|
|
572
|
+
if (this.match(TokenType.GRAFFA_APERTA)) {
|
|
573
|
+
const body = this.blockStatement();
|
|
574
|
+
return new AST.ArrowFunction(params, body, true);
|
|
575
|
+
} else {
|
|
576
|
+
const body = this.expression();
|
|
577
|
+
return new AST.ArrowFunction(params, body, true);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
throw new ParserError('Atteso funzione freccia asincrona', this.peek());
|
|
582
|
+
}
|
|
583
|
+
|
|
551
584
|
if (this.match(TokenType.PARENTESI_APERTA)) {
|
|
552
585
|
// Potrebbe essere una arrow function o un'espressione raggruppata
|
|
553
586
|
const startPos = this.current - 1;
|
|
@@ -573,10 +606,10 @@ class Parser {
|
|
|
573
606
|
// È una arrow function
|
|
574
607
|
if (this.match(TokenType.GRAFFA_APERTA)) {
|
|
575
608
|
const body = this.blockStatement();
|
|
576
|
-
return new AST.ArrowFunction(params, body);
|
|
609
|
+
return new AST.ArrowFunction(params, body, false);
|
|
577
610
|
} else {
|
|
578
611
|
const body = this.expression();
|
|
579
|
-
return new AST.ArrowFunction(params, body);
|
|
612
|
+
return new AST.ArrowFunction(params, body, false);
|
|
580
613
|
}
|
|
581
614
|
} else {
|
|
582
615
|
// 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
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"nome":"Vlad","eta":"12"}
|