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 CHANGED
@@ -7,4 +7,7 @@
7
7
 
8
8
  const { run } = require('../src/cli/cli.js');
9
9
 
10
- run(process.argv.slice(2));
10
+ run(process.argv.slice(2)).catch(err => {
11
+ console.error(err);
12
+ process.exit(1);
13
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vladx",
3
- "version": "1.3.2",
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
  }
@@ -7,6 +7,5 @@ se(passport.eta >= 18) {
7
7
  } altrimenti {
8
8
  stampa("Sei minorenne")
9
9
  }
10
- stampa(passport)
11
10
 
12
- Archivio.scrivi(`passports/${passport.nome}.txt`, JSON.stringify(passport))
11
+ Archivio.scrivi(`passports/${passport.nome}.json`, JSON.stringify(passport))
@@ -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
- const previousEnv = interpreter.environment;
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
- call(interpreter, args) {
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) => typeof args[0]
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(this.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
- this.environment.define(node.name, value, node.isConstant);
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, this.environment);
440
- this.environment.define(node.name, func);
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 = this.environment.get(node.superclass);
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, this.environment));
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, this.environment));
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
- this.environment.define(node.name, klass);
558
+ env.define(node.name, klass);
463
559
  }
464
560
 
465
- executeBlock(statements, env) {
466
- const previousEnv = this.environment;
467
- this.environment = env;
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(this.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 = previousEnv;
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(this.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
- const previousEnv = this.environment;
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 = this.environment.get(name);
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
- this.environment.define(specifier, moduleExports.get(specifier));
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 this.environment.get(node.name);
720
+ return env.get(node.name);
638
721
  case 'ThisExpression':
639
- return this.environment.get('questo');
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, this.environment);
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
- evaluateTemplateLiteral(node) {
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
- this.environment.set(node.left.name, value);
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
- this.environment.set(node.argument.name, newValue);
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(arg => this.evaluate(arg));
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
- return callee.call(this, args);
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(arg => this.evaluate(arg));
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
  };
@@ -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
- if (this.match(TokenType.FUNZIONE)) return this.functionDeclaration();
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
- importDeclaration() {
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
- constructor = new AST.FunctionDeclaration('costruttore', params, body);
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"}