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.
- package/examples/stdlib/ambiente.vx +8 -0
- package/examples/stdlib/archivio_extra.vx +11 -0
- package/examples/stdlib/asserzione.vx +11 -0
- package/examples/stdlib/client_web.vx +7 -0
- package/examples/stdlib/codifica.vx +8 -0
- package/examples/stdlib/collezioni.vx +11 -0
- package/examples/stdlib/compressione.vx +10 -0
- package/examples/stdlib/console.vx +14 -0
- package/examples/stdlib/cripto.vx +8 -0
- package/examples/stdlib/dati.vx +21 -0
- package/examples/stdlib/eventi.vx +10 -0
- package/examples/stdlib/json_extra.vx +7 -0
- package/examples/stdlib/matematica.vx +9 -0
- package/examples/stdlib/percorso.vx +9 -0
- package/examples/stdlib/processo.vx +9 -0
- package/examples/stdlib/server_web.vx +10 -0
- package/examples/stdlib/sql_helper.vx +7 -0
- package/examples/stdlib/tempo.vx +9 -0
- package/examples/stdlib/testo.vx +10 -0
- package/examples/stdlib/url.vx +11 -0
- package/examples/stdlib/validazione.vx +5 -0
- package/package.json +1 -1
- package/programs/1/index.vx +16 -4
- package/programs/main/index.vx +11 -7
- package/programs/main/passports/Tanya.json +1 -0
- package/programs/main/passports/Vlad.json +1 -1
- package/programs/main/passports/vlad.json +1 -0
- package/programs/tests/test_new_features.vx +53 -0
- package/programs/tests/test_stdlib.vx +26 -0
- package/programs/tests/test_templates.vx +8 -0
- package/src/interpreter/interpreter.js +155 -35
- package/src/lexer/lexer.js +5 -1
- package/src/lexer/tokens.js +9 -1
- package/src/parser/ast.js +47 -4
- package/src/parser/parser.js +107 -6
- package/src/stdlib/registry.js +301 -0
|
@@ -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 { 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,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,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);
|
package/package.json
CHANGED
package/programs/1/index.vx
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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();
|
package/programs/main/index.vx
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
variabile passport = {}
|
|
2
2
|
passport.nome = chiedi("Come ti chiami? ")
|
|
3
|
-
passport.
|
|
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
|
-
|
|
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":"
|
|
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 ---");
|
|
@@ -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
|
-
|
|
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
|
|
717
|
+
let moduleExports;
|
|
653
718
|
|
|
654
|
-
if
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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
|
-
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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) {
|
package/src/lexer/lexer.js
CHANGED
|
@@ -270,7 +270,11 @@ class Lexer {
|
|
|
270
270
|
case ';': this.addToken(TokenType.PUNTO_VIRGOLA, ';'); break;
|
|
271
271
|
case '.': this.addToken(TokenType.PUNTO, '.'); break;
|
|
272
272
|
case ':': this.addToken(TokenType.DUE_PUNTI, ':'); break;
|
|
273
|
-
case '?':
|
|
273
|
+
case '?':
|
|
274
|
+
if (this.match('?')) this.addToken(TokenType.NULLISH, '??');
|
|
275
|
+
else if (this.match('.')) this.addToken(TokenType.OPTIONAL_CHAINING, '?.');
|
|
276
|
+
else this.addToken(TokenType.PUNTO_INTERROGATIVO, '?');
|
|
277
|
+
break;
|
|
274
278
|
|
|
275
279
|
case '+':
|
|
276
280
|
if (this.match('+')) this.addToken(TokenType.INCREMENTA, '++');
|
package/src/lexer/tokens.js
CHANGED
|
@@ -39,6 +39,9 @@ const TokenType = {
|
|
|
39
39
|
ATTENDI: 'ATTENDI',
|
|
40
40
|
INTERROMPI: 'INTERROMPI',
|
|
41
41
|
CONTINUA: 'CONTINUA',
|
|
42
|
+
SCEGLI: 'SCEGLI',
|
|
43
|
+
CASO: 'CASO',
|
|
44
|
+
PREDEFINITO: 'PREDEFINITO',
|
|
42
45
|
|
|
43
46
|
// Operators
|
|
44
47
|
PIU: 'PIU',
|
|
@@ -62,6 +65,8 @@ const TokenType = {
|
|
|
62
65
|
FRECCIA: 'FRECCIA',
|
|
63
66
|
AND: 'AND',
|
|
64
67
|
OR: 'OR',
|
|
68
|
+
NULLISH: 'NULLISH',
|
|
69
|
+
OPTIONAL_CHAINING: 'OPTIONAL_CHAINING',
|
|
65
70
|
|
|
66
71
|
// Delimiters
|
|
67
72
|
PARENTESI_APERTA: 'PARENTESI_APERTA',
|
|
@@ -116,7 +121,10 @@ const KEYWORDS = {
|
|
|
116
121
|
'asincrono': TokenType.ASINCRONO,
|
|
117
122
|
'attendi': TokenType.ATTENDI,
|
|
118
123
|
'interrompi': TokenType.INTERROMPI,
|
|
119
|
-
'continua': TokenType.CONTINUA
|
|
124
|
+
'continua': TokenType.CONTINUA,
|
|
125
|
+
'scegli': TokenType.SCEGLI,
|
|
126
|
+
'caso': TokenType.CASO,
|
|
127
|
+
'predefinito': TokenType.PREDEFINITO
|
|
120
128
|
};
|
|
121
129
|
|
|
122
130
|
module.exports = { TokenType, KEYWORDS };
|
package/src/parser/ast.js
CHANGED
|
@@ -27,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
|
};
|
package/src/parser/parser.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
448
|
+
let expr = this.nullishCoalescing();
|
|
362
449
|
|
|
363
450
|
while (this.match(TokenType.O, TokenType.OR)) {
|
|
364
451
|
const operator = '||';
|
|
365
|
-
const right = this.
|
|
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;
|