vladx 1.4.1 → 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.
Files changed (36) hide show
  1. package/examples/stdlib/ambiente.vx +8 -0
  2. package/examples/stdlib/archivio_extra.vx +11 -0
  3. package/examples/stdlib/asserzione.vx +11 -0
  4. package/examples/stdlib/client_web.vx +7 -0
  5. package/examples/stdlib/codifica.vx +8 -0
  6. package/examples/stdlib/collezioni.vx +11 -0
  7. package/examples/stdlib/compressione.vx +10 -0
  8. package/examples/stdlib/console.vx +14 -0
  9. package/examples/stdlib/cripto.vx +8 -0
  10. package/examples/stdlib/dati.vx +21 -0
  11. package/examples/stdlib/eventi.vx +10 -0
  12. package/examples/stdlib/json_extra.vx +7 -0
  13. package/examples/stdlib/matematica.vx +9 -0
  14. package/examples/stdlib/percorso.vx +9 -0
  15. package/examples/stdlib/processo.vx +9 -0
  16. package/examples/stdlib/server_web.vx +10 -0
  17. package/examples/stdlib/sql_helper.vx +7 -0
  18. package/examples/stdlib/tempo.vx +9 -0
  19. package/examples/stdlib/testo.vx +10 -0
  20. package/examples/stdlib/url.vx +11 -0
  21. package/examples/stdlib/validazione.vx +5 -0
  22. package/package.json +1 -1
  23. package/programs/1/index.vx +16 -4
  24. package/programs/main/index.vx +11 -7
  25. package/programs/main/passports/Tanya.json +1 -0
  26. package/programs/main/passports/Vlad.json +1 -1
  27. package/programs/main/passports/vlad.json +1 -0
  28. package/programs/tests/test_new_features.vx +53 -0
  29. package/programs/tests/test_stdlib.vx +26 -0
  30. package/programs/tests/test_templates.vx +8 -0
  31. package/src/interpreter/interpreter.js +155 -35
  32. package/src/lexer/lexer.js +5 -1
  33. package/src/lexer/tokens.js +9 -1
  34. package/src/parser/ast.js +47 -4
  35. package/src/parser/parser.js +107 -6
  36. package/src/stdlib/registry.js +301 -0
@@ -0,0 +1,8 @@
1
+ importa { prendi } da "@std/Ambiente";
2
+
3
+ variabile path = prendi("PATH");
4
+ stampa("Tuo percorso di sistema (PATH):");
5
+ stampa(path);
6
+
7
+ variabile user = prendi("USER") ?? prendi("USERNAME");
8
+ stampa("\nUtente corrente: " + user);
@@ -0,0 +1,11 @@
1
+ importa { elencaTutto } da "@std/ArchivioExtra";
2
+
3
+ stampa("Analisi completa cartella 'src':");
4
+ variabile fileSrc = elencaTutto("src");
5
+
6
+ // Mostro solo i primi 5 per brevità
7
+ variabile i = 0;
8
+ per (variabile f di fileSrc) {
9
+ se (i < 5) stampa("- " + f);
10
+ i++;
11
+ }
@@ -0,0 +1,11 @@
1
+ importa { uguale, vero } da "@std/Asserzione";
2
+
3
+ stampa("Esecuzione test unitari...");
4
+
5
+ prova {
6
+ uguale(10 + 10, 20);
7
+ vero(5 > 0);
8
+ stampa("Test passati!");
9
+ } cattura (e) {
10
+ stampa("Errore asserzione: " + e);
11
+ }
@@ -0,0 +1,7 @@
1
+ importa { get } da "@std/ClientWeb";
2
+
3
+ stampa("Recupero dati da API esterna...");
4
+ variabile dati = get("https://jsonplaceholder.typicode.com/todos/1");
5
+
6
+ stampa("Risposta ricevuta:");
7
+ stampa(dati);
@@ -0,0 +1,8 @@
1
+ importa { base64, daBase64, hex, uri } da "@std/Codifica";
2
+
3
+ variabile t = "VladX Core";
4
+
5
+ stampa("Testo: " + t);
6
+ stampa("Base64: " + base64(t));
7
+ stampa("Hex: " + hex(t));
8
+ stampa("URI encoded: " + uri("https://vlad.it?n=vlad x"));
@@ -0,0 +1,11 @@
1
+ importa { CreaMappa, CreaInsieme, chiavi, valori } da "@std/Collezioni";
2
+
3
+ stampa("--- MAPPA ---");
4
+ variabile m = CreaMappa();
5
+ m.set("chiave", "valore");
6
+ stampa("Contenuto mappa: " + m.get("chiave"));
7
+
8
+ stampa("\n--- OGGETTI ---");
9
+ variabile obj = { nome: "Vlad", versione: 1.4 };
10
+ stampa("Chiavi: " + chiavi(obj));
11
+ stampa("Valori: " + valori(obj));
@@ -0,0 +1,10 @@
1
+ importa { comprimi, decomprimi } da "@std/Compressione";
2
+
3
+ variabile segreto = "Questo è un messaggio molto lungo che vogliamo comprimere.";
4
+ stampa("Originale (" + lunghezza(segreto) + " byte): " + segreto);
5
+
6
+ variabile compresso = comprimi(segreto);
7
+ stampa("Compresso (Base64): " + compresso);
8
+
9
+ variabile decompresso = decomprimi(compresso);
10
+ stampa("Decompresso: " + decompresso);
@@ -0,0 +1,14 @@
1
+ importa { log, errore, avviso, successo, tabella } da "@std/Console";
2
+
3
+ log("Semplice log di sistema");
4
+ successo("Operazione completata con successo!");
5
+ avviso("Attenzione: valore instabile.");
6
+ errore("Errore critico rilevato!");
7
+
8
+ variabile dati = [
9
+ { id: 1, nome: "Vlad" },
10
+ { id: 2, nome: "Rex" }
11
+ ];
12
+
13
+ stampa("\nVisualizzazione tabella:");
14
+ tabella(dati);
@@ -0,0 +1,8 @@
1
+ importa { sha256, md5, uuid, casuale } da "@std/Cripto";
2
+
3
+ stampa("Generazione UUID: " + uuid());
4
+ stampa("Token casuale (Hex): " + casuale(32));
5
+
6
+ variabile password = "vlad_password_sicura";
7
+ stampa("SHA256: " + sha256(password));
8
+ stampa("MD5: " + md5(password));
@@ -0,0 +1,21 @@
1
+ importa { clona, fondi, raggruppa } da "@std/Dati";
2
+
3
+ stampa("--- CLONE ---");
4
+ variabile a = { x: 1 };
5
+ variabile b = clona(a);
6
+ b.x = 2;
7
+ stampa("a.x (dovrebbe essere 1): " + a.x);
8
+
9
+ stampa("\n--- MERGE ---");
10
+ variabile base = { nome: "Vlad" };
11
+ variabile extra = { eta: 25 };
12
+ fondi(base, extra);
13
+ stampa(base);
14
+
15
+ stampa("\n--- GROUP BY ---");
16
+ variabile persone = [
17
+ { nome: "Luca", citta: "Roma" },
18
+ { nome: "Sara", citta: "Milano" },
19
+ { nome: "Marco", citta: "Roma" }
20
+ ];
21
+ stampa(raggruppa(persone, "citta"));
@@ -0,0 +1,10 @@
1
+ importa { CreaEmitter } da "@std/Eventi";
2
+
3
+ variabile bus = CreaEmitter();
4
+
5
+ bus.su("benvenuto", (dati) => {
6
+ stampa("Evento ricevuto! Dati: " + dati);
7
+ });
8
+
9
+ stampa("Lancio evento...");
10
+ attendi bus.lancia("benvenuto", "Ciao da VladX");
@@ -0,0 +1,7 @@
1
+ importa { codifica, analizzaSicuro } da "@std/JsonExtra";
2
+
3
+ variabile dati = { api: "vladx", opzioni: [1, 2, 3] };
4
+ stampa(codifica(dati));
5
+
6
+ variabile str = '{"valido": vero}';
7
+ stampa("Safe Parse: " + analizzaSicuro(str).valido);
@@ -0,0 +1,9 @@
1
+ importa { piGreco, radice, casuale, rotonda, max } da "@std/Matematica";
2
+
3
+ stampa("Valore di PI: " + piGreco);
4
+ stampa("Radice di 144: " + radice(144));
5
+ stampa("Arrotonda 4.7: " + rotonda(4.7));
6
+ stampa("Massimo tra 10, 50, 20: " + max(10, 50, 20));
7
+
8
+ variabile n = casuale() * 10;
9
+ stampa("Numero casuale 0-10: " + rotonda(n));
@@ -0,0 +1,9 @@
1
+ importa { unisci, base, cartella, estensione, assoluto } da "@std/Percorso";
2
+
3
+ variabile mioFile = "/home/utente/progetti/test.vx";
4
+
5
+ stampa("File: " + mioFile);
6
+ stampa("Base: " + base(mioFile));
7
+ stampa("Cartella: " + cartella(mioFile));
8
+ stampa("Estensione: " + estensione(mioFile));
9
+ stampa("Percorso assoluto di 'src': " + assoluto("src"));
@@ -0,0 +1,9 @@
1
+ importa { id, memoria, piattaforma, versioneNode } da "@std/Processo";
2
+
3
+ stampa("--- INFO PROCESSO ---");
4
+ stampa("ID: " + id);
5
+ stampa("Piattaforma: " + piattaforma);
6
+ stampa("Node Version: " + versioneNode);
7
+
8
+ stampa("\nUso memoria:");
9
+ stampa(memoria());
@@ -0,0 +1,10 @@
1
+ importa { rispondiJson, errore } da "@std/ServerWeb";
2
+
3
+ // Esempio d'uso teorico con Rete.server (vedi documentazione)
4
+ // gestore = (richiesta) => {
5
+ // ritorna rispondiJson({ stato: "ok", msg: "Ciao da VladX" });
6
+ // };
7
+
8
+ variabile resp = rispondiJson({ id: 1 });
9
+ stampa("Esempio risposta JSON:");
10
+ stampa(resp);
@@ -0,0 +1,7 @@
1
+ importa { generaInsert } da "@std/SqlHelper";
2
+
3
+ variabile utente = { nome: "Vlad", eta: 25, ruolo: "Admin" };
4
+ variabile sql = generaInsert("utenti", utente);
5
+
6
+ stampa("SQL generato:");
7
+ stampa(sql);
@@ -0,0 +1,9 @@
1
+ importa { data, ora, pausa, timestamp } da "@std/Tempo";
2
+
3
+ stampa("Oggi è il: " + data());
4
+ stampa("Ora attuale: " + ora());
5
+ stampa("Timestamp: " + timestamp());
6
+
7
+ stampa("\nAttendo 1 secondo...");
8
+ attendi pausa(1000);
9
+ stampa("Fine attesa.");
@@ -0,0 +1,10 @@
1
+ importa { maiuscolo, minuscolo, capitalizza, slugify, pulisci } da "@std/Testo";
2
+
3
+ variabile testo = " vladx è fantastico ";
4
+
5
+ stampa("Originale: '" + testo + "'");
6
+ stampa("Pulisci: '" + pulisci(testo) + "'");
7
+ stampa("Maiuscolo: " + maiuscolo(testo));
8
+ stampa("Minuscolo: " + minuscolo(testo));
9
+ stampa("Capitalizza: " + capitalizza("vlad"));
10
+ stampa("Slugify: " + slugify("Benvenuti su VladX 2.0!"));
@@ -0,0 +1,11 @@
1
+ importa { analizza, componi } da "@std/Url";
2
+
3
+ variabile link = "https://vladx.org/search?q=syntax&lang=it#top";
4
+ variabile info = analizza(link);
5
+
6
+ stampa("Host: " + info.host);
7
+ stampa("Protocollo: " + info.protocollo);
8
+ stampa("Query: " + info.query.q);
9
+
10
+ variabile nuovoLink = componi("https://api.test.com", { chiave: "123", utente: "vlad" });
11
+ stampa("Nuovo URL: " + nuovoLink);
@@ -0,0 +1,5 @@
1
+ importa { email, url, ip } da "@std/Validazione";
2
+
3
+ stampa("Email valida? " + email("test@vladx.it")); // vero
4
+ stampa("URL valido? " + url("httttp://errore")); // falso
5
+ stampa("IP valido? " + ip("192.168.1.1")); // vero
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vladx",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "description": "VladX - Linguaggio di programmazione con sintassi italiana",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -1,4 +1,16 @@
1
- stampa("Ciao dal programma 1!");
2
- costante saluti = { matteo: "Ciao!", vlad: "Ehilà!" };
3
- stampa("Matteo dice: " + saluti.matteo);
4
- stampa("Vlad dice: " + saluti.vlad);
1
+ classe Persona {
2
+ funzione costruttore(nome, eta) {
3
+ questo.nome = nome;
4
+ questo.eta = eta;
5
+ }
6
+
7
+ funzione saluta() {
8
+ stampa("Ciao, sono " + questo.nome + " e ho " + questo.eta + " anni");
9
+ }
10
+ }
11
+
12
+ variabile p1 = nuovo Persona("Mario", 25);
13
+ p1.saluta();
14
+
15
+ variabile p2 = nuovo Persona("Luigi", 30);
16
+ p2.saluta();
@@ -1,11 +1,15 @@
1
1
  variabile passport = {}
2
2
  passport.nome = chiedi("Come ti chiami? ")
3
- passport.eta = chiedi("Quanti anni hai? ")
4
-
5
- se(passport.eta >= 18) {
6
- stampa("Sei maggiorenne")
3
+ se(Archivio.esiste(`passports/${passport.nome}.json`)) {
4
+ stampa("GIA ESISTE, VATTENE!!")
7
5
  } altrimenti {
8
- stampa("Sei minorenne")
9
- }
6
+ passport.eta = chiedi("Quanti anni hai? ")
7
+
8
+ se(passport.eta >= 18) {
9
+ stampa("Sei maggiorenne")
10
+ } altrimenti {
11
+ stampa("Sei minorenne")
12
+ }
10
13
 
11
- Archivio.scrivi(`passports/${passport.nome}.json`, JSON.stringify(passport))
14
+ Archivio.scrivi(`passports/${passport.nome}.json`, JSON.stringify(passport))
15
+ }
@@ -0,0 +1 @@
1
+ {"nome":"Tanya","eta":"70"}
@@ -1 +1 @@
1
- {"nome":"Vlad","eta":"25"}
1
+ {"nome":"Vlad","eta":"12"}
@@ -0,0 +1 @@
1
+ {"nome":"vlad","eta":"13"}
@@ -0,0 +1,53 @@
1
+ // Test Nuove Funzionalità VladX
2
+
3
+ stampa("--- TEST SCEGLI-CASO ---");
4
+ variabile colore = "verde";
5
+ scegli (colore) {
6
+ caso "rosso":
7
+ stampa("Alt!");
8
+ interrompi;
9
+ caso "verde":
10
+ stampa("Vai!");
11
+ interrompi;
12
+ predefinito:
13
+ stampa("Colore ignoto");
14
+ }
15
+
16
+ stampa("\n--- TEST DESTRUTTURAZIONE ---");
17
+ variabile [a, b] = [10, 20];
18
+ stampa("a: " + a + ", b: " + b); // 10, 20
19
+
20
+ variabile { nome, eta } = { nome: "Vlad", eta: 25, citta: "Roma" };
21
+ stampa("Nome: " + nome + ", Eta: " + eta); // Vlad, 25
22
+
23
+ stampa("\n--- TEST OPERATORE TERNARIO ---");
24
+ variabile x = 10;
25
+ variabile risultato = x > 5 ? "Maggiore" : "Minore";
26
+ stampa("Risultato: " + risultato); // Maggiore
27
+
28
+ stampa("\n--- TEST NULLISH COALESCING ---");
29
+ variabile n = nullo;
30
+ variabile val = n ?? "Default";
31
+ stampa("Valore: " + val); // Default
32
+
33
+ stampa("\n--- TEST OPTIONAL CHAINING ---");
34
+ variabile utente = { info: { nome: "Luca" } };
35
+ stampa("Nome utente: " + utente?.info?.nome); // Luca
36
+ stampa("Cognome utente: " + (utente?.info?.cognome ?? "N/A")); // N/A
37
+
38
+ stampa("\n--- TEST METODI ARRAY ---");
39
+ variabile numeri = [1, 2, 3, 4, 5];
40
+ variabile raddoppiati = attendi numeri.mappa((n) => n * 2);
41
+ stampa("Raddoppiati: " + raddoppiati); // [2, 4, 6, 8, 10]
42
+
43
+ variabile pari = attendi numeri.filtra((n) => n % 2 == 0);
44
+ stampa("Pari: " + pari); // [2, 4]
45
+
46
+ stampa("\n--- TEST MATEMATICA ---");
47
+ stampa("PI: " + Matematica.piGreco);
48
+ stampa("Radice di 16: " + Matematica.radice(16));
49
+ stampa("Casuale (0-1): " + Matematica.casuale());
50
+
51
+ stampa("\n--- TEST TEMPO ---");
52
+ stampa("Data oggi: " + Tempo.data());
53
+ stampa("Ora attuale: " + Tempo.ora());
@@ -0,0 +1,26 @@
1
+ // Test Integrazione Standard Library VladX
2
+
3
+ importa { maiuscolo, slugify } da "@std/Testo";
4
+ importa { sha256, uuid } da "@std/Cripto";
5
+ importa { log, successo, avviso } da "@std/Console";
6
+ importa { data, ora } da "@std/Tempo"; // Modulo integrato precendente (ora anche in @std/Cronometro)
7
+ importa { media, somma } da "@std/Calcolo";
8
+
9
+ stampa("--- TEST @std/Testo ---");
10
+ stampa("Maiuscolo: " + maiuscolo("vladx"));
11
+ stampa("Slug: " + slugify("Questo è un test!"));
12
+
13
+ stampa("\n--- TEST @std/Cripto ---");
14
+ stampa("SHA256: " + sha256("segreto"));
15
+ stampa("UUID: " + uuid());
16
+
17
+ stampa("\n--- TEST @std/Calcolo ---");
18
+ variabile voti = [10, 8, 9, 7];
19
+ stampa("Media: " + media(voti));
20
+ stampa("Somma: " + somma(voti));
21
+
22
+ stampa("\n--- TEST @std/Console ---");
23
+ successo("Questo è un messaggio di successo!");
24
+ avviso("Attenzione: questo è un avviso.");
25
+
26
+ stampa("\n--- FINE TEST ---");
@@ -0,0 +1,8 @@
1
+ // Test Template Strings
2
+ variabile nome = "Vlad";
3
+ variabile eta = 25;
4
+ variabile messaggio = `Ciao, mi chiamo ${nome} e ho ${eta} anni.`;
5
+ stampa(messaggio);
6
+
7
+ variabile calc = `Il doppio di ${eta} è ${eta * 2}.`;
8
+ stampa(calc);
@@ -204,6 +204,7 @@ const sqlite3 = require('better-sqlite3');
204
204
  const mysql = require('mysql2/promise');
205
205
  const { Lexer } = require('../lexer/lexer.js');
206
206
  const { Parser } = require('../parser/parser.js');
207
+ const stdRegistry = require('../stdlib/registry.js');
207
208
 
208
209
  class Interpreter {
209
210
  constructor() {
@@ -470,6 +471,27 @@ class Interpreter {
470
471
  }
471
472
  };
472
473
  this.globals.define('Rete', Rete);
474
+
475
+ // --- MATEMATICA ---
476
+ const Matematica = {
477
+ casuale: { call: () => Math.random() },
478
+ rotonda: { call: (_, args) => Math.round(args[0]) },
479
+ radice: { call: (_, args) => Math.sqrt(args[0]) },
480
+ min: { call: (_, args) => Math.min(...args) },
481
+ max: { call: (_, args) => Math.max(...args) },
482
+ piGreco: Math.PI,
483
+ E: Math.E
484
+ };
485
+ this.globals.define('Matematica', Matematica);
486
+
487
+ // --- TEMPO ---
488
+ const Tempo = {
489
+ ora: { call: () => new Date().toLocaleTimeString() },
490
+ data: { call: () => new Date().toLocaleDateString() },
491
+ timestamp: { call: () => Date.now() },
492
+ pausa: { call: async (_, args) => new Promise(res => setTimeout(res, args[0])) }
493
+ };
494
+ this.globals.define('Tempo', Tempo);
473
495
  }
474
496
 
475
497
  async interpret(program) {
@@ -503,6 +525,8 @@ class Interpreter {
503
525
  return await this.executeWhileStatement(node, env);
504
526
  case 'ForStatement':
505
527
  return await this.executeForStatement(node, env);
528
+ case 'SwitchStatement':
529
+ return await this.executeSwitchStatement(node, env);
506
530
  case 'ReturnStatement':
507
531
  throw new ReturnValue(node.argument ? await this.evaluate(node.argument, env) : null);
508
532
  case 'BreakStatement':
@@ -528,7 +552,48 @@ class Interpreter {
528
552
 
529
553
  async executeVariableDeclaration(node, env) {
530
554
  const value = node.value ? await this.evaluate(node.value, env) : null;
531
- env.define(node.name, value, node.isConstant);
555
+
556
+ if (typeof node.name === 'string') {
557
+ env.define(node.name, value, node.isConstant);
558
+ } else if (node.name.type === 'ArrayPattern') {
559
+ if (!Array.isArray(value)) throw new Error('Destrutturazione array fallita: il valore non è un array');
560
+ for (let i = 0; i < node.name.elements.length; i++) {
561
+ env.define(node.name.elements[i], value[i] !== undefined ? value[i] : null, node.isConstant);
562
+ }
563
+ } else if (node.name.type === 'ObjectPattern') {
564
+ if (typeof value !== 'object' || value === null) throw new Error('Destrutturazione oggetto fallita: il valore non è un oggetto');
565
+ for (const prop of node.name.properties) {
566
+ env.define(prop, value[prop] !== undefined ? value[prop] : null, node.isConstant);
567
+ }
568
+ }
569
+ }
570
+
571
+ async executeSwitchStatement(node, env) {
572
+ const discriminant = await this.evaluate(node.discriminant, env);
573
+ let matched = false;
574
+ let defaultCase = null;
575
+
576
+ for (const switchCase of node.cases) {
577
+ if (switchCase.test === null) {
578
+ defaultCase = switchCase;
579
+ continue;
580
+ }
581
+
582
+ const testValue = await this.evaluate(switchCase.test, env);
583
+ if (matched || discriminant === testValue) {
584
+ matched = true;
585
+ try {
586
+ await this.executeBlock(switchCase.consequent, env);
587
+ } catch (e) {
588
+ if (e instanceof BreakSignal) return;
589
+ throw e;
590
+ }
591
+ }
592
+ }
593
+
594
+ if (!matched && defaultCase) {
595
+ await this.executeBlock(defaultCase.consequent, env);
596
+ }
532
597
  }
533
598
 
534
599
  executeFunctionDeclaration(node, env) {
@@ -649,44 +714,54 @@ class Interpreter {
649
714
 
650
715
  async executeImportDeclaration(node, env) {
651
716
  const sourcePath = node.source;
652
- let fullPath;
717
+ let moduleExports;
653
718
 
654
- if (sourcePath.startsWith('./') || sourcePath.startsWith('../')) {
655
- fullPath = path.resolve(path.dirname(this.currentPath), sourcePath);
656
- } else {
657
- // Check vladx_modules
658
- fullPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
659
- if (!fullPath.endsWith('.vx')) fullPath += '.vx';
660
- if (!fs.existsSync(fullPath)) {
661
- // Try index.vx inside the module folder
662
- const folderPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
663
- fullPath = path.join(folderPath, 'index.vx');
719
+ // Check if it's a standard library module
720
+ if (sourcePath.startsWith('@std/')) {
721
+ if (stdRegistry[sourcePath]) {
722
+ moduleExports = stdRegistry[sourcePath];
723
+ } else {
724
+ throw new Error(`Modulo standard non trovato: ${sourcePath}`);
664
725
  }
665
- }
666
-
667
- if (!fs.existsSync(fullPath)) {
668
- throw new Error(`Modulo non trovato: ${sourcePath}`);
669
- }
670
-
671
- let moduleExports;
672
- if (this.modules.has(fullPath)) {
673
- moduleExports = this.modules.get(fullPath);
674
726
  } else {
675
- // Load and execute module
676
- const content = fs.readFileSync(fullPath, 'utf8');
677
- const lexer = new Lexer(content);
678
- const tokens = lexer.tokenize();
679
- const parser = new Parser(tokens);
680
- const program = parser.parse();
727
+ let fullPath;
681
728
 
682
- const subInterpreter = new Interpreter();
683
- // Sharing the same modules cache
684
- subInterpreter.modules = this.modules;
685
- subInterpreter.currentPath = fullPath;
729
+ if (sourcePath.startsWith('./') || sourcePath.startsWith('../')) {
730
+ fullPath = path.resolve(path.dirname(this.currentPath), sourcePath);
731
+ } else {
732
+ // Check vladx_modules
733
+ fullPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
734
+ if (!fullPath.endsWith('.vx')) fullPath += '.vx';
735
+ if (!fs.existsSync(fullPath)) {
736
+ // Try index.vx inside the module folder
737
+ const folderPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
738
+ fullPath = path.join(folderPath, 'index.vx');
739
+ }
740
+ }
686
741
 
687
- await subInterpreter.interpret(program);
688
- moduleExports = subInterpreter.exportedSymbols;
689
- this.modules.set(fullPath, moduleExports);
742
+ if (!fs.existsSync(fullPath)) {
743
+ throw new Error(`Modulo non trovato: ${sourcePath}`);
744
+ }
745
+
746
+ if (this.modules.has(fullPath)) {
747
+ moduleExports = this.modules.get(fullPath);
748
+ } else {
749
+ // Load and execute module
750
+ const content = fs.readFileSync(fullPath, 'utf8');
751
+ const lexer = new Lexer(content);
752
+ const tokens = lexer.tokenize();
753
+ const parser = new Parser(tokens);
754
+ const program = parser.parse();
755
+
756
+ const subInterpreter = new Interpreter();
757
+ // Sharing the same modules cache
758
+ subInterpreter.modules = this.modules;
759
+ subInterpreter.currentPath = fullPath;
760
+
761
+ await subInterpreter.interpret(program);
762
+ moduleExports = subInterpreter.exportedSymbols;
763
+ this.modules.set(fullPath, moduleExports);
764
+ }
690
765
  }
691
766
 
692
767
  // Import specified symbols
@@ -746,6 +821,8 @@ class Interpreter {
746
821
  return await this.evaluateMemberExpression(node, env);
747
822
  case 'ArrowFunction':
748
823
  return new ArrowFunc(node.params, node.body, env, node.isAsync);
824
+ case 'ConditionalExpression':
825
+ return await this.evaluateConditionalExpression(node, env);
749
826
  case 'TemplateLiteral':
750
827
  return await this.evaluateTemplateLiteral(node, env);
751
828
  case 'AwaitExpression':
@@ -806,8 +883,18 @@ class Interpreter {
806
883
  const left = await this.evaluate(node.left, env);
807
884
  if (node.operator === '||') {
808
885
  return this.isTruthy(left) ? left : await this.evaluate(node.right, env);
809
- } else {
886
+ } else if (node.operator === '&&') {
810
887
  return !this.isTruthy(left) ? left : await this.evaluate(node.right, env);
888
+ } else if (node.operator === '??') {
889
+ return (left !== null && left !== undefined) ? left : await this.evaluate(node.right, env);
890
+ }
891
+ }
892
+
893
+ async evaluateConditionalExpression(node, env) {
894
+ if (this.isTruthy(await this.evaluate(node.test, env))) {
895
+ return await this.evaluate(node.consequent, env);
896
+ } else {
897
+ return await this.evaluate(node.alternate, env);
811
898
  }
812
899
  }
813
900
 
@@ -897,6 +984,11 @@ class Interpreter {
897
984
 
898
985
  async evaluateMemberExpression(node, env) {
899
986
  const obj = await this.evaluate(node.object, env);
987
+
988
+ if (node.optional && (obj === undefined || obj === null)) {
989
+ return null;
990
+ }
991
+
900
992
  const prop = node.computed
901
993
  ? await this.evaluate(node.property, env)
902
994
  : node.property.name;
@@ -916,6 +1008,34 @@ class Interpreter {
916
1008
  if (prop === 'lunghezza' || prop === 'length') {
917
1009
  return obj.length;
918
1010
  }
1011
+ if (prop === 'mappa' || prop === 'map') {
1012
+ return {
1013
+ isAsync: true,
1014
+ call: async (interpreter, args) => {
1015
+ const callback = args[0];
1016
+ const results = [];
1017
+ for (let i = 0; i < obj.length; i++) {
1018
+ results.push(await callback.call(interpreter, [obj[i], i, obj]));
1019
+ }
1020
+ return results;
1021
+ }
1022
+ };
1023
+ }
1024
+ if (prop === 'filtra' || prop === 'filter') {
1025
+ return {
1026
+ isAsync: true,
1027
+ call: async (interpreter, args) => {
1028
+ const callback = args[0];
1029
+ const results = [];
1030
+ for (let i = 0; i < obj.length; i++) {
1031
+ if (interpreter.isTruthy(await callback.call(interpreter, [obj[i], i, obj]))) {
1032
+ results.push(obj[i]);
1033
+ }
1034
+ }
1035
+ return results;
1036
+ }
1037
+ };
1038
+ }
919
1039
  }
920
1040
 
921
1041
  if (obj instanceof VladXInstance) {
@@ -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 '?': this.addToken(TokenType.PUNTO_INTERROGATIVO, '?'); break;
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, '++');
@@ -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,6 +27,20 @@ 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
45
  constructor(name, params, body, isAsync = false) {
32
46
  super('FunctionDeclaration');
@@ -76,6 +90,22 @@ class BlockStatement extends ASTNode {
76
90
  }
77
91
  }
78
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
+
79
109
  class IfStatement extends ASTNode {
80
110
  constructor(condition, consequent, alternate = null) {
81
111
  super('IfStatement');
@@ -84,8 +114,6 @@ class IfStatement extends ASTNode {
84
114
  this.alternate = alternate;
85
115
  }
86
116
  }
87
- // ... skip ...
88
-
89
117
 
90
118
  class WhileStatement extends ASTNode {
91
119
  constructor(condition, body) {
@@ -264,11 +292,12 @@ class CallExpression extends ASTNode {
264
292
  }
265
293
 
266
294
  class MemberExpression extends ASTNode {
267
- constructor(object, property, computed = false) {
295
+ constructor(object, property, computed = false, optional = false) {
268
296
  super('MemberExpression');
269
297
  this.object = object;
270
298
  this.property = property;
271
299
  this.computed = computed;
300
+ this.optional = optional;
272
301
  }
273
302
  }
274
303
 
@@ -290,6 +319,15 @@ class UpdateExpression extends ASTNode {
290
319
  }
291
320
  }
292
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
+
293
331
  class LogicalExpression extends ASTNode {
294
332
  constructor(operator, left, right) {
295
333
  super('LogicalExpression');
@@ -362,5 +400,10 @@ module.exports = {
362
400
  ThisExpression,
363
401
  NewExpression,
364
402
  TemplateLiteral,
365
- AwaitExpression
403
+ AwaitExpression,
404
+ SwitchStatement,
405
+ SwitchCase,
406
+ ConditionalExpression,
407
+ ArrayPattern,
408
+ ObjectPattern
366
409
  };
@@ -89,6 +89,34 @@ class Parser {
89
89
  }
90
90
 
91
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
+
92
120
  const name = this.consume(TokenType.IDENTIFICATORE, 'Nome variabile atteso').value;
93
121
  let value = null;
94
122
  if (this.match(TokenType.ASSEGNA)) {
@@ -98,7 +126,20 @@ class Parser {
98
126
  return new AST.VariableDeclaration(name, value, isConstant);
99
127
  }
100
128
 
101
- // ... imports ...
129
+ importDeclaration() {
130
+ this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "importa"');
131
+ const specifiers = [];
132
+ if (!this.check(TokenType.GRAFFA_CHIUSA)) {
133
+ do {
134
+ specifiers.push(this.consume(TokenType.IDENTIFICATORE, 'Nome simbolo atteso').value);
135
+ } while (this.match(TokenType.VIRGOLA));
136
+ }
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;
140
+ this.match(TokenType.PUNTO_VIRGOLA);
141
+ return new AST.ImportDeclaration(specifiers, source);
142
+ }
102
143
 
103
144
  exportDeclaration() {
104
145
  if (this.match(TokenType.ASINCRONO)) {
@@ -197,6 +238,7 @@ class Parser {
197
238
 
198
239
  statement() {
199
240
  if (this.match(TokenType.SE)) return this.ifStatement();
241
+ if (this.match(TokenType.SCEGLI)) return this.switchStatement();
200
242
  if (this.match(TokenType.MENTRE)) return this.whileStatement();
201
243
  if (this.match(TokenType.PER)) return this.forStatement();
202
244
  if (this.match(TokenType.RITORNA)) return this.returnStatement();
@@ -209,6 +251,38 @@ class Parser {
209
251
  return this.expressionStatement();
210
252
  }
211
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
+
212
286
  blockStatement() {
213
287
  const statements = [];
214
288
  while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
@@ -346,7 +420,7 @@ class Parser {
346
420
  }
347
421
 
348
422
  assignment() {
349
- const expr = this.logicalOr();
423
+ const expr = this.conditional();
350
424
 
351
425
  if (this.match(TokenType.ASSEGNA, TokenType.PIU_ASSEGNA, TokenType.MENO_ASSEGNA)) {
352
426
  const operator = this.previous().value;
@@ -357,18 +431,42 @@ class Parser {
357
431
  return expr;
358
432
  }
359
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
+
360
447
  logicalOr() {
361
- let expr = this.logicalAnd();
448
+ let expr = this.nullishCoalescing();
362
449
 
363
450
  while (this.match(TokenType.O, TokenType.OR)) {
364
451
  const operator = '||';
365
- const right = this.logicalAnd();
452
+ const right = this.nullishCoalescing();
366
453
  expr = new AST.LogicalExpression(operator, expr, right);
367
454
  }
368
455
 
369
456
  return expr;
370
457
  }
371
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
+
372
470
  logicalAnd() {
373
471
  let expr = this.equality();
374
472
 
@@ -463,11 +561,14 @@ class Parser {
463
561
  expr = this.finishCall(expr);
464
562
  } else if (this.match(TokenType.PUNTO)) {
465
563
  const property = this.consume(TokenType.IDENTIFICATORE, 'Nome proprietà atteso dopo "."');
466
- 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);
467
568
  } else if (this.match(TokenType.QUADRA_APERTA)) {
468
569
  const property = this.expression();
469
570
  this.consume(TokenType.QUADRA_CHIUSA, 'Atteso "]" dopo indice');
470
- expr = new AST.MemberExpression(expr, property, true);
571
+ expr = new AST.MemberExpression(expr, property, true, false);
471
572
  } else {
472
573
  break;
473
574
  }
@@ -0,0 +1,301 @@
1
+ /**
2
+ * VladX Standard Library Registry
3
+ * Contiene le definizioni di tutti i moduli integrati.
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const crypto = require('crypto');
9
+ const zlib = require('zlib');
10
+ const { execSync } = require('child_process');
11
+
12
+ const registry = {};
13
+
14
+ // --- 1. TESTO ---
15
+ registry['@std/Testo'] = new Map([
16
+ ['maiuscolo', { call: (_, args) => String(args[0]).toUpperCase() }],
17
+ ['minuscolo', { call: (_, args) => String(args[0]).toLowerCase() }],
18
+ ['capitalizza', {
19
+ call: (_, args) => {
20
+ const s = String(args[0]);
21
+ return s.charAt(0).toUpperCase() + s.slice(1);
22
+ }
23
+ }],
24
+ ['pulisci', { call: (_, args) => String(args[0]).trim() }],
25
+ ['sostituisci', { call: (_, args) => String(args[0]).replace(new RegExp(args[1], 'g'), args[2]) }],
26
+ ['dividi', { call: (_, args) => String(args[0]).split(args[1]) }],
27
+ ['contiene', { call: (_, args) => String(args[0]).includes(args[1]) }],
28
+ ['slugify', { call: (_, args) => String(args[0]).toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]+/g, '') }]
29
+ ]);
30
+
31
+ // --- 2. COLLEZIONI ---
32
+ registry['@std/Collezioni'] = new Map([
33
+ ['CreaMappa', { call: () => new Map() }],
34
+ ['CreaInsieme', { call: () => new Set() }],
35
+ ['unisci', { call: (_, args) => Object.assign({}, ...args) }],
36
+ ['chiavi', { call: (_, args) => Object.keys(args[0]) }],
37
+ ['valori', { call: (_, args) => Object.values(args[0]) }]
38
+ ]);
39
+
40
+ // --- 3. PERCORSO ---
41
+ registry['@std/Percorso'] = new Map([
42
+ ['unisci', { call: (_, args) => path.join(...args) }],
43
+ ['base', { call: (_, args) => path.basename(args[0]) }],
44
+ ['cartella', { call: (_, args) => path.dirname(args[0]) }],
45
+ ['estensione', { call: (_, args) => path.extname(args[0]) }],
46
+ ['assoluto', { call: (_, args) => path.resolve(args[0]) }]
47
+ ]);
48
+
49
+ // --- 4. CONSOLE ---
50
+ registry['@std/Console'] = new Map([
51
+ ['log', { call: (_, args) => console.log(...args) }],
52
+ ['errore', { call: (_, args) => console.error('\x1b[31m%s\x1b[0m', ...args) }],
53
+ ['avviso', { call: (_, args) => console.warn('\x1b[33m%s\x1b[0m', ...args) }],
54
+ ['successo', { call: (_, args) => console.log('\x1b[32m%s\x1b[0m', ...args) }],
55
+ ['pulisci', { call: () => console.clear() }],
56
+ ['tabella', { call: (_, args) => console.table(args[0]) }]
57
+ ]);
58
+
59
+ // --- 5. CRIPTO ---
60
+ registry['@std/Cripto'] = new Map([
61
+ ['sha256', { call: (_, args) => crypto.createHash('sha256').update(String(args[0])).digest('hex') }],
62
+ ['md5', { call: (_, args) => crypto.createHash('md5').update(String(args[0])).digest('hex') }],
63
+ ['uuid', { call: () => crypto.randomUUID() }],
64
+ ['casuale', { call: (_, args) => crypto.randomBytes(args[0] || 16).toString('hex') }]
65
+ ]);
66
+
67
+ // --- 6. COMPRESSIONE ---
68
+ registry['@std/Compressione'] = new Map([
69
+ ['comprimi', { call: (_, args) => zlib.gzipSync(args[0]).toString('base64') }],
70
+ ['decomprimi', { call: (_, args) => zlib.gunzipSync(Buffer.from(args[0], 'base64')).toString() }]
71
+ ]);
72
+
73
+ // --- 7. URL ---
74
+ registry['@std/Url'] = new Map([
75
+ ['analizza', {
76
+ call: (_, args) => {
77
+ const u = new URL(args[0]);
78
+ return {
79
+ protocollo: u.protocol,
80
+ host: u.host,
81
+ percorso: u.pathname,
82
+ query: Object.fromEntries(u.searchParams),
83
+ frammento: u.hash
84
+ };
85
+ }
86
+ }],
87
+ ['componi', {
88
+ call: (_, args) => {
89
+ const u = new URL(args[0]);
90
+ Object.entries(args[1] || {}).forEach(([k, v]) => u.searchParams.set(k, v));
91
+ return u.toString();
92
+ }
93
+ }]
94
+ ]);
95
+
96
+ // --- 8. EVENTI ---
97
+ registry['@std/Eventi'] = new Map([
98
+ ['CreaEmitter', {
99
+ call: () => {
100
+ const listeners = {};
101
+ return {
102
+ su: {
103
+ call: (_, args) => {
104
+ const [ev, cb] = args;
105
+ if (!listeners[ev]) listeners[ev] = [];
106
+ listeners[ev].push(cb);
107
+ }
108
+ },
109
+ lancia: {
110
+ call: async (int, args) => {
111
+ const [ev, data] = args;
112
+ if (listeners[ev]) {
113
+ for (const cb of listeners[ev]) await cb.call(int, [data]);
114
+ }
115
+ }
116
+ }
117
+ };
118
+ }
119
+ }]
120
+ ]);
121
+
122
+ // --- 9. PROCESSO ---
123
+ registry['@std/Processo'] = new Map([
124
+ ['id', process.pid],
125
+ ['memoria', { call: () => process.memoryUsage() }],
126
+ ['tempoAttivita', { call: () => process.uptime() }],
127
+ ['versioneNode', process.version],
128
+ ['piattaforma', process.platform]
129
+ ]);
130
+
131
+ // --- 10. AMBIENTE ---
132
+ registry['@std/Ambiente'] = new Map([
133
+ ['prendi', { call: (_, args) => process.env[args[0]] }],
134
+ ['tutti', { call: () => process.env }]
135
+ ]);
136
+
137
+ // --- 11. DATI ---
138
+ registry['@std/Dati'] = new Map([
139
+ ['clona', { call: (_, args) => JSON.parse(JSON.stringify(args[0])) }],
140
+ ['fondi', {
141
+ call: (_, args) => {
142
+ const merge = (target, source) => {
143
+ for (const key of Object.keys(source)) {
144
+ if (source[key] instanceof Object) Object.assign(source[key], merge(target[key] || {}, source[key]));
145
+ }
146
+ Object.assign(target || {}, source);
147
+ return target;
148
+ };
149
+ return merge(args[0], args[1]);
150
+ }
151
+ }],
152
+ ['raggruppa', {
153
+ call: (_, args) => {
154
+ const [arr, key] = args;
155
+ return arr.reduce((rv, x) => {
156
+ (rv[x[key]] = rv[x[key]] || []).push(x);
157
+ return rv;
158
+ }, {});
159
+ }
160
+ }]
161
+ ]);
162
+
163
+ // --- 12. VALIDAZIONE ---
164
+ registry['@std/Validazione'] = new Map([
165
+ ['email', { call: (_, args) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(args[0]) }],
166
+ ['url', { call: (_, args) => /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/.test(args[0]) }],
167
+ ['numero', { call: (_, args) => !isNaN(args[0]) }],
168
+ ['ip', { call: (_, args) => /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(args[0]) }]
169
+ ]);
170
+
171
+ // --- 13. CALCOLO ---
172
+ registry['@std/Calcolo'] = new Map([
173
+ ['media', { call: (_, args) => args[0].reduce((a, b) => a + b, 0) / args[0].length }],
174
+ ['somma', { call: (_, args) => args[0].reduce((a, b) => a + b, 0) }],
175
+ ['fattoriale', {
176
+ call: (_, args) => {
177
+ let f = 1;
178
+ for (let i = 1; i <= args[0]; i++) f *= i;
179
+ return f;
180
+ }
181
+ }]
182
+ ]);
183
+
184
+ // --- 14. CRONOMETRO ---
185
+ registry['@std/Cronometro'] = new Map([
186
+ ['oraPrecisa', { call: () => performance.now() }],
187
+ ['formattaDurata', {
188
+ call: (_, args) => {
189
+ const ms = args[0];
190
+ const s = Math.floor(ms / 1000);
191
+ const m = Math.floor(s / 60);
192
+ return `${m}:${(s % 60).toString().padStart(2, '0')}.${(Math.floor(ms % 1000)).toString().padStart(3, '0')}`;
193
+ }
194
+ }]
195
+ ]);
196
+
197
+ // --- 15. CODIFICA ---
198
+ registry['@std/Codifica'] = new Map([
199
+ ['base64', { call: (_, args) => Buffer.from(String(args[0])).toString('base64') }],
200
+ ['daBase64', { call: (_, args) => Buffer.from(String(args[0]), 'base64').toString() }],
201
+ ['uri', { call: (_, args) => encodeURIComponent(args[0]) }],
202
+ ['daUri', { call: (_, args) => decodeURIComponent(args[0]) }],
203
+ ['hex', { call: (_, args) => Buffer.from(String(args[0])).toString('hex') }]
204
+ ]);
205
+
206
+ // --- 16. ARCHIVIO_EXTRA ---
207
+ registry['@std/ArchivioExtra'] = new Map([
208
+ ['elencaTutto', {
209
+ call: (_, args) => {
210
+ const walk = (dir) => {
211
+ let results = [];
212
+ const list = fs.readdirSync(dir);
213
+ list.forEach(file => {
214
+ file = path.resolve(dir, file);
215
+ const stat = fs.statSync(file);
216
+ if (stat && stat.isDirectory()) results = results.concat(walk(file));
217
+ else results.push(file);
218
+ });
219
+ return results;
220
+ };
221
+ return walk(args[0]);
222
+ }
223
+ }],
224
+ ['copia', { call: (_, args) => fs.copyFileSync(args[0], args[1]) }]
225
+ ]);
226
+
227
+ // --- 17. SERVER_WEB ---
228
+ registry['@std/ServerWeb'] = new Map([
229
+ ['rispondiJson', {
230
+ call: (_, args) => {
231
+ return { stato: 200, intestazioni: { 'Content-Type': 'application/json' }, corpo: args[0] };
232
+ }
233
+ }],
234
+ ['errore', {
235
+ call: (_, args) => {
236
+ return { stato: args[0] || 500, corpo: args[1] || "Errore Interno" };
237
+ }
238
+ }]
239
+ ]);
240
+
241
+ // --- 18. CLIENT_WEB ---
242
+ registry['@std/ClientWeb'] = new Map([
243
+ ['get', { call: (_, args) => execSync(`curl -sL "${args[0]}"`, { encoding: 'utf8' }) }],
244
+ ['post', {
245
+ call: (_, args) => {
246
+ const [url, data] = args;
247
+ return execSync(`curl -sLX POST "${url}" -d '${JSON.stringify(data)}' -H "Content-Type: application/json"`, { encoding: 'utf8' });
248
+ }
249
+ }]
250
+ ]);
251
+
252
+ // --- 19. SQL_HELPER ---
253
+ registry['@std/SqlHelper'] = new Map([
254
+ ['generaInsert', {
255
+ call: (_, args) => {
256
+ const [tabella, dati] = args;
257
+ const keys = Object.keys(dati);
258
+ const vals = Object.values(dati).map(v => typeof v === 'string' ? `'${v}'` : v);
259
+ return `INSERT INTO ${tabella} (${keys.join(', ')}) VALUES (${vals.join(', ')})`;
260
+ }
261
+ }]
262
+ ]);
263
+
264
+ // --- 20. JSON_EXTRA ---
265
+ registry['@std/JsonExtra'] = new Map([
266
+ ['codifica', { call: (_, args) => JSON.stringify(args[0], null, 2) }],
267
+ ['analizzaSicuro', {
268
+ call: (_, args) => {
269
+ try { return JSON.parse(args[0]); }
270
+ catch (e) { return null; }
271
+ }
272
+ }]
273
+ ]);
274
+
275
+ // --- 21. ASSERZIONE ---
276
+ registry['@std/Asserzione'] = new Map([
277
+ ['uguale', {
278
+ call: (_, args) => {
279
+ if (args[0] !== args[1]) throw new Error(`Asserzione fallita: ${args[0]} !== ${args[1]}`);
280
+ }
281
+ }],
282
+ ['vero', {
283
+ call: (_, args) => {
284
+ if (!args[0]) throw new Error(`Asserzione fallita: atteso vero`);
285
+ }
286
+ }],
287
+ ['falso', {
288
+ call: (_, args) => {
289
+ if (args[0]) throw new Error(`Asserzione fallita: atteso falso`);
290
+ }
291
+ }]
292
+ ]);
293
+
294
+ // --- ALIAS ---
295
+ registry['@std/Matematica'] = registry['@std/Calcolo'];
296
+ registry['@std/Tempo'] = registry['@std/Cronometro'];
297
+ // Aggiungo anche metodi data/ora a @std/Tempo se mancanti
298
+ registry['@std/Tempo'].set('data', { call: () => new Date().toLocaleDateString() });
299
+ registry['@std/Tempo'].set('ora', { call: () => new Date().toLocaleTimeString() });
300
+
301
+ module.exports = registry;