vladx 1.2.1 → 1.2.4
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/DOCUMENTAZIONE.md +375 -0
- package/README.md +143 -0
- package/examples/test_classi.vx +42 -0
- package/examples/test_classi_semplice.vx +15 -0
- package/examples/test_eccezioni.vx +33 -0
- package/examples/test_stdlib_fs.vx +43 -0
- package/examples/test_stdlib_rete.vx +46 -0
- package/install.sh +42 -0
- package/package.json +1 -1
- package/programs/1/index.vx +1 -0
- package/src/interpreter/interpreter.js +277 -2
- package/src/parser/ast.js +55 -1
- package/src/parser/parser.js +113 -3
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# 🇮🇹 Documentazione Ufficiale VladX
|
|
2
|
+
|
|
3
|
+
Benvenuti nella documentazione ufficiale di **VladX**, un linguaggio di programmazione moderno con **sintassi italiana**. VladX è progettato per essere leggibile, intuitivo e completo, ideale per chi vuole programmare in modo naturale senza rinunciare alla potenza di un linguaggio moderno.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📑 Indice
|
|
8
|
+
|
|
9
|
+
1. [Installazione](#-installazione)
|
|
10
|
+
2. [Guida Rapida CLI](#-guida-rapida-cli)
|
|
11
|
+
3. [Sintassi di Base](#-sintassi-di-base)
|
|
12
|
+
4. [Tipi di Dati](#-tipi-di-dati)
|
|
13
|
+
5. [Operatori](#-operatori)
|
|
14
|
+
6. [Controllo del Flusso](#-controllo-del-flusso)
|
|
15
|
+
7. [Funzioni](#-funzioni)
|
|
16
|
+
8. [Moduli](#-moduli)
|
|
17
|
+
9. [Libreria Standard (StdLib)](#-libreria-standard-stdlib)
|
|
18
|
+
10. [Funzioni Globali](#-funzioni-globali)
|
|
19
|
+
11. [Metodi di Array e Oggetti](#-metodi-di-array-e-oggetti)
|
|
20
|
+
12. [VladPM (Package Manager)](#-vladpm-package-manager)
|
|
21
|
+
13. [Esempi Completi](#-esempi-completi)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Installazione
|
|
26
|
+
|
|
27
|
+
Puoi installare VladX sul tuo sistema in due modi:
|
|
28
|
+
|
|
29
|
+
### Tramite NPM (Consigliato)
|
|
30
|
+
Se hai Node.js installato, puoi installare VladX globalmente con un semplice comando:
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g vladx
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Tramite Script Bash
|
|
36
|
+
Puoi utilizzare lo script di installazione incluso nel repository:
|
|
37
|
+
```bash
|
|
38
|
+
chmod +x install.sh
|
|
39
|
+
./install.sh
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🛠 Guida Rapida CLI
|
|
45
|
+
|
|
46
|
+
Il comando principale è `vladx`.
|
|
47
|
+
|
|
48
|
+
- **REPL Interattivo**: Digita semplicemente `vladx` per entrare nella console interattiva.
|
|
49
|
+
- **Esecuzione Script**: `vladx mio_programma.vx`
|
|
50
|
+
- **Analisi AST**: `vladx ast mio_programma.vx` (mostra la struttura interna del codice).
|
|
51
|
+
- **Lexing**: `vladx lex mio_programma.vx` (mostra i token analizzati).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 📝 Sintassi di Base
|
|
56
|
+
|
|
57
|
+
### Commenti
|
|
58
|
+
```javascript
|
|
59
|
+
// Questo è un commento su riga singola
|
|
60
|
+
/* Questo è un commento
|
|
61
|
+
multilinea */
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Variabili e Costanti
|
|
65
|
+
In VladX, le variabili vengono dichiarate con la parola chiave `variabile` e le costanti con `costante`.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
variabile nome = "Vlad";
|
|
69
|
+
nome = "VladX"; // Ok, è una variabile
|
|
70
|
+
|
|
71
|
+
costante VERSIONE = 1.2;
|
|
72
|
+
// VERSIONE = 1.3; // Errore! Non puoi riassegnare una costante
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 💎 Tipi di Dati
|
|
78
|
+
|
|
79
|
+
VladX supporta i seguenti tipi di dati fondamentali:
|
|
80
|
+
|
|
81
|
+
| Tipo | Esempio |
|
|
82
|
+
| :--- | :--- |
|
|
83
|
+
| **Numero** | `10`, `3.14`, `-5` |
|
|
84
|
+
| **Stringa** | `"Ciao Mondo"`, `'Esempio'` |
|
|
85
|
+
| **Booleano** | `vero`, `falso` |
|
|
86
|
+
| **Nullo** | `nullo` |
|
|
87
|
+
| **Array** | `[1, 2, 3, "test"]` |
|
|
88
|
+
| **Oggetto** | `{ nome: "Vlad", eta: 20 }` |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## ⚡ Operatori
|
|
93
|
+
|
|
94
|
+
### Aritmetici
|
|
95
|
+
Supporto completo per le operazioni matematiche:
|
|
96
|
+
- `+` (Addizione / Concatenazione stringhe)
|
|
97
|
+
- `-` (Sottrazione)
|
|
98
|
+
- `*` (Moltiplicazione)
|
|
99
|
+
- `/` (Divisione)
|
|
100
|
+
- `%` (Resto della divisione)
|
|
101
|
+
- `++` / `--` (Incremento e decremento)
|
|
102
|
+
|
|
103
|
+
### Confronto
|
|
104
|
+
- `==` / `===` (Uguaglianza)
|
|
105
|
+
- `!=` / `!==` (Diversità)
|
|
106
|
+
- `<` / `>` (Minore / Maggiore)
|
|
107
|
+
- `<=` / `>=` (Minore o uguale / Maggiore o uguale)
|
|
108
|
+
|
|
109
|
+
### Logici
|
|
110
|
+
- `e` (AND logico)
|
|
111
|
+
- `o` (OR logico)
|
|
112
|
+
- `non` (NOT logico)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🔄 Controllo del Flusso
|
|
117
|
+
|
|
118
|
+
### Condizionali (se / altrimenti)
|
|
119
|
+
```javascript
|
|
120
|
+
variabile voto = 8;
|
|
121
|
+
|
|
122
|
+
se (voto >= 6) {
|
|
123
|
+
stampa("Promosso!");
|
|
124
|
+
} altrimenti {
|
|
125
|
+
stampa("Bocciato!");
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Ciclo Mentre (mentre)
|
|
130
|
+
```javascript
|
|
131
|
+
variabile contatore = 0;
|
|
132
|
+
mentre (contatore < 5) {
|
|
133
|
+
stampa("Conteggio: " + contatore);
|
|
134
|
+
contatore++;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Ciclo Per (per)
|
|
139
|
+
```javascript
|
|
140
|
+
per (variabile i = 0; i < 3; i++) {
|
|
141
|
+
stampa("Giro numero " + i);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Gestione Errori (prova / cattura / finalmente)
|
|
146
|
+
VladX include un sistema robusto per gestire le eccezioni:
|
|
147
|
+
```javascript
|
|
148
|
+
prova {
|
|
149
|
+
lancia "Qualcosa è andato storto!";
|
|
150
|
+
} cattura (errore) {
|
|
151
|
+
stampa("Errore catturato: " + errore);
|
|
152
|
+
} finalmente {
|
|
153
|
+
stampa("Esecuzione terminata.");
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 📦 Funzioni
|
|
160
|
+
|
|
161
|
+
### Dichiarazione Funzione
|
|
162
|
+
```javascript
|
|
163
|
+
funzione saluta(nome) {
|
|
164
|
+
ritorna "Ciao, " + nome + "!";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
stampa(saluta("Amico"));
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Arrow Functions
|
|
171
|
+
Sintassi compatta per funzioni rapide:
|
|
172
|
+
```javascript
|
|
173
|
+
costante quadrato = (n) => n * n;
|
|
174
|
+
stampa(quadrato(4)); // 16
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 🏛️ Classi
|
|
178
|
+
|
|
179
|
+
VladX supporta la programmazione orientata agli oggetti con classi.
|
|
180
|
+
|
|
181
|
+
### Dichiarazione Classe
|
|
182
|
+
```javascript
|
|
183
|
+
classe Persona {
|
|
184
|
+
funzione costruttore(nome, eta) {
|
|
185
|
+
questo.nome = nome;
|
|
186
|
+
questo.eta = eta;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
funzione saluta() {
|
|
190
|
+
stampa("Ciao, sono " + questo.nome);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Creazione Istanze
|
|
196
|
+
Usa la parola chiave `nuovo` per creare un'istanza di una classe:
|
|
197
|
+
```javascript
|
|
198
|
+
variabile p = nuovo Persona("Mario", 25);
|
|
199
|
+
p.saluta(); // "Ciao, sono Mario"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Ereditarietà
|
|
203
|
+
Le classi possono estendere altre classi usando `da`:
|
|
204
|
+
```javascript
|
|
205
|
+
classe Studente da Persona {
|
|
206
|
+
funzione costruttore(nome, eta, scuola) {
|
|
207
|
+
questo.nome = nome;
|
|
208
|
+
questo.eta = eta;
|
|
209
|
+
questo.scuola = scuola;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
funzione studia() {
|
|
213
|
+
stampa(questo.nome + " studia alla " + questo.scuola);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Parola Chiave `questo`
|
|
219
|
+
Usa `questo` per riferirti all'istanza corrente della classe:
|
|
220
|
+
```javascript
|
|
221
|
+
classe Contatore {
|
|
222
|
+
funzione costruttore() {
|
|
223
|
+
questo.valore = 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
funzione incrementa() {
|
|
227
|
+
questo.valore++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 🧩 Moduli
|
|
235
|
+
|
|
236
|
+
VladX permette di organizzare il codice in più file.
|
|
237
|
+
|
|
238
|
+
**file: `matematica.vx`**
|
|
239
|
+
```javascript
|
|
240
|
+
esporta costante PI = 3.14;
|
|
241
|
+
esporta funzione doppia(n) { ritorna n * 2; }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**file: `app.vx`**
|
|
245
|
+
```javascript
|
|
246
|
+
importa { PI, doppia } da "./matematica.vx";
|
|
247
|
+
|
|
248
|
+
stampa("Il doppio di PI è: " + doppia(PI));
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 📚 Libreria Standard (StdLib)
|
|
254
|
+
|
|
255
|
+
La libreria standard offre strumenti potenti pronti all'uso.
|
|
256
|
+
|
|
257
|
+
### 📁 Archivio (File System)
|
|
258
|
+
Gestisci i file in modo semplice.
|
|
259
|
+
- `Archivio.leggi(percorso)`: Legge il contenuto di un file.
|
|
260
|
+
- `Archivio.scrivi(percorso, dati)`: Scrive dati in un file.
|
|
261
|
+
- `Archivio.esiste(percorso)`: Verifica se un file esiste.
|
|
262
|
+
- `Archivio.elimina(percorso)`: Rimuove un file.
|
|
263
|
+
|
|
264
|
+
### 💻 Sistema (OS)
|
|
265
|
+
Interagisci con il sistema operativo.
|
|
266
|
+
- `Sistema.esegui(comando)`: Esegue un comando shell e restituisce l'output.
|
|
267
|
+
- `Sistema.argomenti`: Array degli argomenti passati da riga di comando.
|
|
268
|
+
- `Sistema.piattaforma`: Stringa che indica l'OS (es. "linux", "darwin").
|
|
269
|
+
- `Sistema.cartellaCorrente()`: Percorso della directory attuale.
|
|
270
|
+
- `Sistema.esci(codice)`: Termina il programma.
|
|
271
|
+
|
|
272
|
+
### 🌐 Rete (HTTP)
|
|
273
|
+
Includi capacità di rete nei tuoi script.
|
|
274
|
+
- `Rete.chiama(url)`: Esegue una richiesta HTTP GET (utilizza curl internamente).
|
|
275
|
+
- `Rete.server(porta, gestore)`: Avvia un server web sulla porta specificata.
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
funzione mioGestore(richiesta) {
|
|
279
|
+
ritorna {
|
|
280
|
+
stato: 200,
|
|
281
|
+
corpo: "Ciao dal server VladX!",
|
|
282
|
+
intestazioni: { "Content-Type": "text/plain" }
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
Rete.server(8080, mioGestore);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 🌍 Funzioni Globali
|
|
292
|
+
|
|
293
|
+
Funzioni sempre disponibili senza importazioni:
|
|
294
|
+
- `stampa(valore)`: Mostra un valore nella console.
|
|
295
|
+
- `aspetta(ms)`: Sospende l'esecuzione per il numero specificato di millisecondi.
|
|
296
|
+
- `lunghezza(valore)`: Restituisce la lunghezza di una stringa o array.
|
|
297
|
+
- `tipo(valore)`: Restituisce il tipo del valore (es. "number", "string").
|
|
298
|
+
- `numero(valore)`: Converte in numero.
|
|
299
|
+
- `stringa(valore)`: Converte in stringa.
|
|
300
|
+
- `array(valore)`: Verifica se il valore è un array.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 📊 Metodi di Array e Oggetti
|
|
305
|
+
|
|
306
|
+
### Array
|
|
307
|
+
Gli array supportano metodi dinamici:
|
|
308
|
+
```javascript
|
|
309
|
+
variabile lista = [1, 2];
|
|
310
|
+
lista.aggiungi(3); // lista è [1, 2, 3]
|
|
311
|
+
variabile rimosso = lista.rimuovi(); // rimosso = 3, lista è [1, 2]
|
|
312
|
+
stampa(lista.lunghezza); // 2
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Oggetti
|
|
316
|
+
Accesso alle proprietà tramite punto o parentesi quadre:
|
|
317
|
+
```javascript
|
|
318
|
+
variabile auto = { marca: "Fiat", modello: "500" };
|
|
319
|
+
stampa(auto.marca); // "Fiat"
|
|
320
|
+
auto.anno = 2022;
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 📦 VladPM (Package Manager)
|
|
326
|
+
|
|
327
|
+
VladPM è lo strumento per gestire le dipendenze e i progetti VladX.
|
|
328
|
+
|
|
329
|
+
- `vladpm init`: Crea un nuovo file `vladx.json` nel progetto.
|
|
330
|
+
- `vladpm install <pacchetto>`: Installa un pacchetto dal registry.
|
|
331
|
+
- `vladpm publish`: Pubblica il progetto nel registry VladX.
|
|
332
|
+
- `vladpm start`: Esegue lo script `start` definito in `vladx.json`.
|
|
333
|
+
- `vladpm run <nome>`: Esegue uno script personalizzato.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 🌟 Esempi Completi
|
|
338
|
+
|
|
339
|
+
#### Fibonacci
|
|
340
|
+
```javascript
|
|
341
|
+
funzione fibonacci(n) {
|
|
342
|
+
se (n <= 1) {
|
|
343
|
+
ritorna n;
|
|
344
|
+
}
|
|
345
|
+
ritorna fibonacci(n - 1) + fibonacci(n - 2);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
per (variabile i = 0; i < 10; i++) {
|
|
349
|
+
stampa(fibonacci(i));
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Client HTTP API
|
|
354
|
+
```javascript
|
|
355
|
+
variabile risposta = Rete.chiama("https://api.github.com/users/octocat");
|
|
356
|
+
stampa("Nome utente: " + risposta.name);
|
|
357
|
+
stampa("Bio: " + risposta.bio);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Generatore di File
|
|
361
|
+
```javascript
|
|
362
|
+
costante NOME_FILE = "test.txt";
|
|
363
|
+
|
|
364
|
+
se (non Archivio.esiste(NOME_FILE)) {
|
|
365
|
+
Archivio.scrivi(NOME_FILE, "Contenuto generato automaticamente da VladX");
|
|
366
|
+
stampa("File creato con successo!");
|
|
367
|
+
} altrimenti {
|
|
368
|
+
stampa("Il file esiste già. Contenuto:");
|
|
369
|
+
stampa(Archivio.leggi(NOME_FILE));
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
Creato con ❤️ dal team di **VladX**.
|
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# 🇮🇹 VladX
|
|
2
|
+
|
|
3
|
+
**VladX** è un linguaggio di programmazione moderno con **sintassi italiana**, progettato per essere potente, leggibile e completo. Include un interprete robusto, una libreria standard (StdLib) estesa e un package manager dedicato (**VladPM**).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Installazione
|
|
8
|
+
|
|
9
|
+
Puoi installare VladX in due modi:
|
|
10
|
+
|
|
11
|
+
### Metodo 1: NPM (Consigliato)
|
|
12
|
+
Se hai Node.js installato:
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g vladx
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Metodo 2: Script di installazione
|
|
18
|
+
```bash
|
|
19
|
+
chmod +x install.sh
|
|
20
|
+
./install.sh
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🛠️ Utilizzo CLI
|
|
26
|
+
|
|
27
|
+
- `vladx` - Avvia il REPL interattivo.
|
|
28
|
+
- `vladx programma.vx` - Esegue un file VladX.
|
|
29
|
+
- `vladx ast programma.vx` - Mostra l'Abstract Syntax Tree.
|
|
30
|
+
- `vladx lex programma.vx` - Mostra i token analizzati.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📝 Sintassi in breve
|
|
35
|
+
|
|
36
|
+
### Variabili e Costanti
|
|
37
|
+
```javascript
|
|
38
|
+
variabile nome = "Vlad";
|
|
39
|
+
costante PI = 3.14;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Controllo del Flusso
|
|
43
|
+
```javascript
|
|
44
|
+
se (x > 10) {
|
|
45
|
+
stampa("Maggiore di 10");
|
|
46
|
+
} altrimenti {
|
|
47
|
+
stampa("Minore o uguale a 10");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
per (variabile i = 0; i < 5; i++) {
|
|
51
|
+
stampa(i);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
mentre (condizione) { ... }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Funzioni e Arrow Functions
|
|
58
|
+
```javascript
|
|
59
|
+
funzione saluta(nome) {
|
|
60
|
+
ritorna "Ciao, " + nome;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
costante somma = (a, b) => a + b;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Classi
|
|
67
|
+
```javascript
|
|
68
|
+
classe Persona {
|
|
69
|
+
funzione costruttore(nome, eta) {
|
|
70
|
+
questo.nome = nome;
|
|
71
|
+
questo.eta = eta;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
funzione saluta() {
|
|
75
|
+
stampa("Ciao, sono " + questo.nome);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
variabile p = nuovo Persona("Mario", 25);
|
|
80
|
+
p.saluta();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Gestione Errori (Try/Catch)
|
|
84
|
+
```javascript
|
|
85
|
+
prova {
|
|
86
|
+
lancia "Ops!";
|
|
87
|
+
} cattura (err) {
|
|
88
|
+
stampa("Errore: " + err);
|
|
89
|
+
} finalmente {
|
|
90
|
+
stampa("Fine.");
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 📦 Sistema Moduli
|
|
97
|
+
|
|
98
|
+
Dividi il tuo codice in più file:
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
// math.vx
|
|
102
|
+
esporta costante PI = 3.14;
|
|
103
|
+
esporta funzione doppia(n) { ritorna n * 2; }
|
|
104
|
+
|
|
105
|
+
// app.vx
|
|
106
|
+
importa { PI, doppia } da "./math.vx";
|
|
107
|
+
stampa(doppia(PI));
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 📚 Libreria Standard (StdLib)
|
|
113
|
+
|
|
114
|
+
VladX include moduli nativi potenti:
|
|
115
|
+
|
|
116
|
+
- **`Archivio`**: `leggi`, `scrivi`, `esiste`, `elimina`.
|
|
117
|
+
- **`Sistema`**: `esegui` (shell), `argomenti`, `piattaforma`, `esci`.
|
|
118
|
+
- **`Rete`**: `chiama` (HTTP GET), `server` (Crea server web).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 📦 VladPM (Package Manager)
|
|
123
|
+
|
|
124
|
+
VladPM è il gestore di pacchetti ufficiale per VladX.
|
|
125
|
+
|
|
126
|
+
- `vladpm init` - Inizializza un nuovo progetto (`vladx.json`).
|
|
127
|
+
- `vladpm install <nome>` - Installa un pacchetto dal registry.
|
|
128
|
+
- `vladpm publish` - Pubblica il tuo pacchetto.
|
|
129
|
+
- `vladpm start` / `vladpm run <script>` - Esegue gli script definiti nel progetto.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🌟 Esempi
|
|
134
|
+
|
|
135
|
+
Trovi molti esempi nella cartella `examples/`:
|
|
136
|
+
- `test_eccezioni.vx` - Test try/catch.
|
|
137
|
+
- `test_stdlib_fs.vx` - Test File System.
|
|
138
|
+
- `test_stdlib_rete.vx` - Test HTTP e Server.
|
|
139
|
+
- `moduli/` - Esempio di sistema a moduli.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
Creato con ❤️ per la comunità italiana.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Test delle classi in VladX
|
|
2
|
+
|
|
3
|
+
classe Persona {
|
|
4
|
+
funzione costruttore(nome, eta) {
|
|
5
|
+
questo.nome = nome;
|
|
6
|
+
questo.eta = eta;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
funzione saluta() {
|
|
10
|
+
stampa("Ciao, sono " + questo.nome + " e ho " + questo.eta + " anni");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
funzione compieAnni() {
|
|
14
|
+
questo.eta++;
|
|
15
|
+
stampa(questo.nome + " ora ha " + questo.eta + " anni");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
variabile p1 = nuovo Persona("Mario", 25);
|
|
20
|
+
p1.saluta();
|
|
21
|
+
p1.compieAnni();
|
|
22
|
+
|
|
23
|
+
variabile p2 = nuovo Persona("Luigi", 30);
|
|
24
|
+
p2.saluta();
|
|
25
|
+
|
|
26
|
+
// Test con classe derivata (ereditarietà)
|
|
27
|
+
classe Studente da Persona {
|
|
28
|
+
funzione costruttore(nome, eta, scuola) {
|
|
29
|
+
// Chiamata al costruttore padre (se supportato)
|
|
30
|
+
questo.nome = nome;
|
|
31
|
+
questo.eta = eta;
|
|
32
|
+
questo.scuola = scuola;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
funzione studia() {
|
|
36
|
+
stampa(questo.nome + " studia alla " + questo.scuola);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
variabile s1 = nuovo Studente("Anna", 20, "Università");
|
|
41
|
+
s1.saluta();
|
|
42
|
+
s1.studia();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Test semplice delle classi
|
|
2
|
+
|
|
3
|
+
classe Test {
|
|
4
|
+
funzione costruttore(nome) {
|
|
5
|
+
questo.nome = nome;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
funzione saluta() {
|
|
9
|
+
stampa("Ciao " + questo.nome);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
variabile t = nuovo Test("Mario");
|
|
14
|
+
stampa("Istanza creata");
|
|
15
|
+
t.saluta();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Eccezioni VladX
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
stampa("--- Inizio Test Eccezioni ---");
|
|
6
|
+
|
|
7
|
+
prova {
|
|
8
|
+
stampa("1. Provo a fare qualcosa...");
|
|
9
|
+
lancia "Ops! Qualcosa è andato storto.";
|
|
10
|
+
stampa("Questo non dovrebbe apparire.");
|
|
11
|
+
} cattura (errore) {
|
|
12
|
+
stampa("2. Catturato errore: " + errore);
|
|
13
|
+
} finalmente {
|
|
14
|
+
stampa("3. Questo viene eseguito sempre (finalmente).");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
stampa("");
|
|
18
|
+
|
|
19
|
+
prova {
|
|
20
|
+
stampa("4. Provo divisione per zero (errore runtime)...");
|
|
21
|
+
variabile x = 10 / 0; // In JS è Infinity, non lancia errore. Proviamo qualcosa che lancia errore.
|
|
22
|
+
stampa("Risultato: " + x);
|
|
23
|
+
|
|
24
|
+
// Forza errore runtime reale (chiamata a non funzione)
|
|
25
|
+
variabile nonFunzione = 10;
|
|
26
|
+
nonFunzione();
|
|
27
|
+
} cattura (err) {
|
|
28
|
+
stampa("5. Catturato errore runtime: " + err);
|
|
29
|
+
} finalmente {
|
|
30
|
+
stampa("6. Fine secondo blocco prova.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
stampa("--- Fine Test Eccezioni ---");
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Archivio e Sistema
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
stampa("--- Test Sistema ---");
|
|
6
|
+
stampa("Piattaforma: " + Sistema.piattaforma);
|
|
7
|
+
stampa("Cartella Corrente: " + Sistema.cartellaCorrente());
|
|
8
|
+
|
|
9
|
+
stampa("\n--- Test Archivio ---");
|
|
10
|
+
costante fileTest = "test_stdlib.txt";
|
|
11
|
+
costante contenuto = "Questo è un test della StdLib di VladX!";
|
|
12
|
+
|
|
13
|
+
stampa("Scrittura file...");
|
|
14
|
+
Archivio.scrivi(fileTest, contenuto);
|
|
15
|
+
|
|
16
|
+
se (Archivio.esiste(fileTest)) {
|
|
17
|
+
stampa("File creato con successo.");
|
|
18
|
+
costante letto = Archivio.leggi(fileTest);
|
|
19
|
+
stampa("Contenuto letto: " + letto);
|
|
20
|
+
|
|
21
|
+
se (letto == contenuto) {
|
|
22
|
+
stampa("VERIFICA: I contenuti corrispondono.");
|
|
23
|
+
} altrimenti {
|
|
24
|
+
stampa("ERRORE: I contenuti NON corrispondono!");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
stampa("Eliminazione file...");
|
|
28
|
+
Archivio.elimina(fileTest);
|
|
29
|
+
|
|
30
|
+
se (!Archivio.esiste(fileTest)) {
|
|
31
|
+
stampa("File eliminato con successo.");
|
|
32
|
+
}
|
|
33
|
+
} altrimenti {
|
|
34
|
+
stampa("ERRORE: Il file non è stato creato!");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
stampa("\n--- Esecuzione comando shell ---");
|
|
38
|
+
prova {
|
|
39
|
+
costante output = Sistema.esegui("ls -l bin/vladx");
|
|
40
|
+
stampa("Output ls: " + output);
|
|
41
|
+
} cattura (err) {
|
|
42
|
+
stampa("Errore esecuzione: " + err);
|
|
43
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Rete VladX
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
stampa("--- Test Rete.chiama ---");
|
|
6
|
+
prova {
|
|
7
|
+
// Chiamata a JSONPlaceholder per test
|
|
8
|
+
stampa("Eseguo chiamata a JSONPlaceholder...");
|
|
9
|
+
costante post = Rete.chiama("https://jsonplaceholder.typicode.com/posts/1");
|
|
10
|
+
|
|
11
|
+
stampa("Risposta ricevuta!");
|
|
12
|
+
stampa("Titolo: " + post.title);
|
|
13
|
+
stampa("ID: " + post.id);
|
|
14
|
+
} cattura (err) {
|
|
15
|
+
stampa("Errore chiamata: " + err);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
stampa("\n--- Test Rete.server ---");
|
|
19
|
+
// Creiamo un server che risponde con JSON
|
|
20
|
+
funzione mioGestore(req) {
|
|
21
|
+
stampa("Richiesta ricevuta: " + req.metodo + " " + req.url);
|
|
22
|
+
|
|
23
|
+
ritorna {
|
|
24
|
+
stato: 200,
|
|
25
|
+
intestazioni: { "Content-Type": "application/json" },
|
|
26
|
+
corpo: {
|
|
27
|
+
messaggio: "Ciao dal Server VladX!",
|
|
28
|
+
percorso: req.url,
|
|
29
|
+
metodo: req.metodo
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Avviamo il server sulla porta 8080
|
|
35
|
+
// Nota: In questo ambiente il server rimarrà attivo finché non terminiamo lo script.
|
|
36
|
+
// Per il test automatico, lo avviamo e poi usiamo Sistema.esegui per testarlo se possibile,
|
|
37
|
+
// ma Sistema.esegui è sincrono, quindi dovremmo usare un altro approccio se volessimo testarlo automatizzato qui.
|
|
38
|
+
// Per ora lo avviamo semplicemente per conferma visiva.
|
|
39
|
+
|
|
40
|
+
stampa("Avvio server su porta 8080...");
|
|
41
|
+
Rete.server(8080, mioGestore);
|
|
42
|
+
|
|
43
|
+
// Mock test: visto che non possiamo fare chiamate asincrone facilmente nel test suite qui,
|
|
44
|
+
// il test si ferma qui o continua se il server non blocca (http.listen non blocca in Node).
|
|
45
|
+
stampa("Server avviato. Test completato.");
|
|
46
|
+
Sistema.esci(0);
|
package/install.sh
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# VladX & VladPM Global Installer
|
|
4
|
+
# 🇮🇹 Script di installazione completa per VladX
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "🚀 Inizio installazione VladX..."
|
|
9
|
+
|
|
10
|
+
# 1. Installazione NVM if not present
|
|
11
|
+
if [ -z "$NVM_DIR" ]; then
|
|
12
|
+
echo "📦 Installazione NVM (Node Version Manager)..."
|
|
13
|
+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
|
14
|
+
|
|
15
|
+
# Load nvm for current session
|
|
16
|
+
export NVM_DIR="$HOME/.nvm"
|
|
17
|
+
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
18
|
+
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
|
19
|
+
else
|
|
20
|
+
echo "✅ NVM già installato."
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# 2. Installazione Node.js (Ultima versione)
|
|
24
|
+
echo "🌐 Installazione dell'ultima versione di Node.js..."
|
|
25
|
+
nvm install node
|
|
26
|
+
nvm use node
|
|
27
|
+
|
|
28
|
+
# 3. Installazione VladX
|
|
29
|
+
echo "🛠️ Installazione globale di VladX e VladPM dal registry NPM..."
|
|
30
|
+
|
|
31
|
+
npm install -g vladx
|
|
32
|
+
|
|
33
|
+
echo ""
|
|
34
|
+
echo "✅ Installazione completata con successo!"
|
|
35
|
+
echo "Node.js: $(node -v)"
|
|
36
|
+
echo "NPM: $(npm -v)"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Puoi ora usare i comandi:"
|
|
39
|
+
echo " vladx"
|
|
40
|
+
echo " vladpm"
|
|
41
|
+
echo ""
|
|
42
|
+
echo "🇮🇹 Buona programmazione con VladX!"
|
package/package.json
CHANGED
package/programs/1/index.vx
CHANGED
|
@@ -116,8 +116,77 @@ class ArrowFunc {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
class VladXClass {
|
|
120
|
+
constructor(name, superclass, constructor, methods) {
|
|
121
|
+
this.name = name;
|
|
122
|
+
this.superclass = superclass;
|
|
123
|
+
this.constructor = constructor;
|
|
124
|
+
this.methods = methods;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
findMethod(name) {
|
|
128
|
+
if (this.methods.has(name)) {
|
|
129
|
+
return this.methods.get(name);
|
|
130
|
+
}
|
|
131
|
+
if (this.superclass) {
|
|
132
|
+
return this.superclass.findMethod(name);
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class VladXInstance {
|
|
139
|
+
constructor(klass) {
|
|
140
|
+
this.klass = klass;
|
|
141
|
+
this.fields = new Map();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get(name) {
|
|
145
|
+
if (this.fields.has(name)) {
|
|
146
|
+
return this.fields.get(name);
|
|
147
|
+
}
|
|
148
|
+
const method = this.klass.findMethod(name);
|
|
149
|
+
if (method) {
|
|
150
|
+
return new BoundMethod(method, this);
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`Proprietà "${name}" non definita nella classe "${this.klass.name}"`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
set(name, value) {
|
|
156
|
+
this.fields.set(name, value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class BoundMethod {
|
|
161
|
+
constructor(method, instance) {
|
|
162
|
+
this.method = method;
|
|
163
|
+
this.instance = instance;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
call(interpreter, args) {
|
|
167
|
+
const env = new Environment(this.method.closure);
|
|
168
|
+
env.define('questo', this.instance);
|
|
169
|
+
for (let i = 0; i < this.method.declaration.params.length; i++) {
|
|
170
|
+
env.define(this.method.declaration.params[i], args[i] !== undefined ? args[i] : null);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
interpreter.executeBlock(this.method.declaration.body.body, env);
|
|
175
|
+
} catch (e) {
|
|
176
|
+
if (e instanceof ReturnValue) {
|
|
177
|
+
return e.value;
|
|
178
|
+
}
|
|
179
|
+
throw e;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
119
185
|
const fs = require('fs');
|
|
120
186
|
const path = require('path');
|
|
187
|
+
const http = require('http');
|
|
188
|
+
const https = require('https');
|
|
189
|
+
const { execSync } = require('child_process');
|
|
121
190
|
const { Lexer } = require('../lexer/lexer.js');
|
|
122
191
|
const { Parser } = require('../parser/parser.js');
|
|
123
192
|
|
|
@@ -155,6 +224,113 @@ class Interpreter {
|
|
|
155
224
|
this.globals.define('array', {
|
|
156
225
|
call: (_, args) => Array.isArray(args[0])
|
|
157
226
|
});
|
|
227
|
+
|
|
228
|
+
this.globals.define('aspetta', {
|
|
229
|
+
call: (_, args) => {
|
|
230
|
+
const ms = args[0];
|
|
231
|
+
if (typeof ms === 'number') {
|
|
232
|
+
const start = Date.now();
|
|
233
|
+
while (Date.now() - start < ms);
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
this._registerStdLib();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
_registerStdLib() {
|
|
243
|
+
// --- ARCHIVIO (File System) ---
|
|
244
|
+
const Archivio = {
|
|
245
|
+
leggi: {
|
|
246
|
+
call: (_, args) => fs.readFileSync(path.resolve(this.currentPath, '..', args[0]), 'utf8')
|
|
247
|
+
},
|
|
248
|
+
scrivi: {
|
|
249
|
+
call: (_, args) => {
|
|
250
|
+
fs.writeFileSync(path.resolve(this.currentPath, '..', args[0]), args[1]);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
esiste: {
|
|
255
|
+
call: (_, args) => fs.existsSync(path.resolve(this.currentPath, '..', args[0]))
|
|
256
|
+
},
|
|
257
|
+
elimina: {
|
|
258
|
+
call: (_, args) => {
|
|
259
|
+
fs.unlinkSync(path.resolve(this.currentPath, '..', args[0]));
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
this.globals.define('Archivio', Archivio);
|
|
265
|
+
|
|
266
|
+
// --- SISTEMA (Process/OS) ---
|
|
267
|
+
const Sistema = {
|
|
268
|
+
esegui: {
|
|
269
|
+
call: (_, args) => execSync(args[0], { encoding: 'utf8' })
|
|
270
|
+
},
|
|
271
|
+
argomenti: process.argv.slice(2),
|
|
272
|
+
esci: {
|
|
273
|
+
call: (_, args) => process.exit(args[0] || 0)
|
|
274
|
+
},
|
|
275
|
+
piattaforma: process.platform,
|
|
276
|
+
cartellaCorrente: {
|
|
277
|
+
call: () => process.cwd()
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
this.globals.define('Sistema', Sistema);
|
|
281
|
+
|
|
282
|
+
// --- RETE (HTTP) ---
|
|
283
|
+
const Rete = {
|
|
284
|
+
chiama: {
|
|
285
|
+
call: (_, args) => {
|
|
286
|
+
const url = args[0];
|
|
287
|
+
try {
|
|
288
|
+
const output = execSync(`curl -sL "${url}"`, { encoding: 'utf8' });
|
|
289
|
+
try {
|
|
290
|
+
return JSON.parse(output);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
return output;
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
throw new Error(`Errore chiamata HTTP: ${err.message}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
server: {
|
|
300
|
+
call: (_, args) => {
|
|
301
|
+
const porta = args[0] || 3000;
|
|
302
|
+
const gestore = args[1]; // Una funzione VladX
|
|
303
|
+
|
|
304
|
+
const server = http.createServer(async (req, res) => {
|
|
305
|
+
if (gestore && gestore.call) {
|
|
306
|
+
// Creiamo un oggetto richiesta per VladX
|
|
307
|
+
const richiesta = {
|
|
308
|
+
metodo: req.method,
|
|
309
|
+
url: req.url,
|
|
310
|
+
intestazioni: req.headers
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
const risposta = await gestore.call(this, [richiesta]);
|
|
315
|
+
res.writeHead(risposta.stato || 200, risposta.intestazioni || { 'Content-Type': 'text/plain' });
|
|
316
|
+
res.end(typeof risposta.corpo === 'object' ? JSON.stringify(risposta.corpo) : String(risposta.corpo));
|
|
317
|
+
} catch (e) {
|
|
318
|
+
res.writeHead(500);
|
|
319
|
+
res.end("Errore Server VladX: " + e.message);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
res.writeHead(200);
|
|
323
|
+
res.end("Server VladX attivo sulla porta " + porta);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
server.listen(porta);
|
|
328
|
+
console.log(`Server VladX in ascolto sulla porta ${porta}`);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
this.globals.define('Rete', Rete);
|
|
158
334
|
}
|
|
159
335
|
|
|
160
336
|
interpret(program) {
|
|
@@ -178,6 +354,8 @@ class Interpreter {
|
|
|
178
354
|
return this.executeVariableDeclaration(node);
|
|
179
355
|
case 'FunctionDeclaration':
|
|
180
356
|
return this.executeFunctionDeclaration(node);
|
|
357
|
+
case 'ClassDeclaration':
|
|
358
|
+
return this.executeClassDeclaration(node);
|
|
181
359
|
case 'BlockStatement':
|
|
182
360
|
return this.executeBlock(node.body, new Environment(this.environment));
|
|
183
361
|
case 'IfStatement':
|
|
@@ -192,6 +370,10 @@ class Interpreter {
|
|
|
192
370
|
throw new BreakSignal();
|
|
193
371
|
case 'ContinueStatement':
|
|
194
372
|
throw new ContinueSignal();
|
|
373
|
+
case 'TryStatement':
|
|
374
|
+
return this.executeTryStatement(node);
|
|
375
|
+
case 'ThrowStatement':
|
|
376
|
+
return this.executeThrowStatement(node);
|
|
195
377
|
case 'ExportNamedDeclaration':
|
|
196
378
|
return this.executeExportNamedDeclaration(node);
|
|
197
379
|
case 'ImportDeclaration':
|
|
@@ -215,6 +397,28 @@ class Interpreter {
|
|
|
215
397
|
this.environment.define(node.name, func);
|
|
216
398
|
}
|
|
217
399
|
|
|
400
|
+
executeClassDeclaration(node) {
|
|
401
|
+
let superclass = null;
|
|
402
|
+
if (node.superclass) {
|
|
403
|
+
const superclassValue = this.environment.get(node.superclass);
|
|
404
|
+
if (!(superclassValue instanceof VladXClass)) {
|
|
405
|
+
throw new Error(`"${node.superclass}" non è una classe`);
|
|
406
|
+
}
|
|
407
|
+
superclass = superclassValue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const methods = new Map();
|
|
411
|
+
if (node.constructor) {
|
|
412
|
+
methods.set('costruttore', new VladXFunction(node.constructor, this.environment));
|
|
413
|
+
}
|
|
414
|
+
for (const method of node.methods) {
|
|
415
|
+
methods.set(method.name, new VladXFunction(method, this.environment));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const klass = new VladXClass(node.name, superclass, node.constructor, methods);
|
|
419
|
+
this.environment.define(node.name, klass);
|
|
420
|
+
}
|
|
421
|
+
|
|
218
422
|
executeBlock(statements, env) {
|
|
219
423
|
const previousEnv = this.environment;
|
|
220
424
|
this.environment = env;
|
|
@@ -273,6 +477,43 @@ class Interpreter {
|
|
|
273
477
|
}
|
|
274
478
|
}
|
|
275
479
|
|
|
480
|
+
executeTryStatement(node) {
|
|
481
|
+
try {
|
|
482
|
+
this.execute(node.block);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
// Se è un ReturnValue o Break/Continue Signal, non catturarlo qui
|
|
485
|
+
if (error instanceof ReturnValue || error instanceof BreakSignal || error instanceof ContinueSignal) {
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (node.handler) {
|
|
490
|
+
const catchEnv = new Environment(this.environment);
|
|
491
|
+
// Il valore errore può essere quello lanciato da LANCIA o un errore JS
|
|
492
|
+
const errorValue = error instanceof Error ? error.message : error;
|
|
493
|
+
catchEnv.define(node.handler.param, errorValue);
|
|
494
|
+
|
|
495
|
+
const previousEnv = this.environment;
|
|
496
|
+
this.environment = catchEnv;
|
|
497
|
+
try {
|
|
498
|
+
this.execute(node.handler.body);
|
|
499
|
+
} finally {
|
|
500
|
+
this.environment = previousEnv;
|
|
501
|
+
}
|
|
502
|
+
} else if (!node.finalizer) {
|
|
503
|
+
throw error;
|
|
504
|
+
}
|
|
505
|
+
} finally {
|
|
506
|
+
if (node.finalizer) {
|
|
507
|
+
this.execute(node.finalizer);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
executeThrowStatement(node) {
|
|
513
|
+
const value = this.evaluate(node.argument);
|
|
514
|
+
throw value;
|
|
515
|
+
}
|
|
516
|
+
|
|
276
517
|
executeExportNamedDeclaration(node) {
|
|
277
518
|
this.execute(node.declaration);
|
|
278
519
|
const name = node.declaration.name;
|
|
@@ -351,6 +592,8 @@ class Interpreter {
|
|
|
351
592
|
return null;
|
|
352
593
|
case 'Identifier':
|
|
353
594
|
return this.environment.get(node.name);
|
|
595
|
+
case 'ThisExpression':
|
|
596
|
+
return this.environment.get('questo');
|
|
354
597
|
case 'ArrayLiteral':
|
|
355
598
|
return node.elements.map(el => this.evaluate(el));
|
|
356
599
|
case 'ObjectLiteral':
|
|
@@ -371,6 +614,8 @@ class Interpreter {
|
|
|
371
614
|
return this.evaluateUpdateExpression(node);
|
|
372
615
|
case 'CallExpression':
|
|
373
616
|
return this.evaluateCallExpression(node);
|
|
617
|
+
case 'NewExpression':
|
|
618
|
+
return this.evaluateNewExpression(node);
|
|
374
619
|
case 'MemberExpression':
|
|
375
620
|
return this.evaluateMemberExpression(node);
|
|
376
621
|
case 'ArrowFunction':
|
|
@@ -441,7 +686,13 @@ class Interpreter {
|
|
|
441
686
|
const prop = node.left.computed
|
|
442
687
|
? this.evaluate(node.left.property)
|
|
443
688
|
: node.left.property.name;
|
|
444
|
-
|
|
689
|
+
|
|
690
|
+
// Handle class instances
|
|
691
|
+
if (obj instanceof VladXInstance) {
|
|
692
|
+
obj.set(prop, value);
|
|
693
|
+
} else {
|
|
694
|
+
obj[prop] = value;
|
|
695
|
+
}
|
|
445
696
|
}
|
|
446
697
|
|
|
447
698
|
return value;
|
|
@@ -469,6 +720,25 @@ class Interpreter {
|
|
|
469
720
|
throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
|
|
470
721
|
}
|
|
471
722
|
|
|
723
|
+
evaluateNewExpression(node) {
|
|
724
|
+
const klass = this.evaluate(node.callee);
|
|
725
|
+
|
|
726
|
+
if (!(klass instanceof VladXClass)) {
|
|
727
|
+
throw new Error(`"${node.callee.name || 'valore'}" non è una classe`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const instance = new VladXInstance(klass);
|
|
731
|
+
const constructor = klass.findMethod('costruttore');
|
|
732
|
+
|
|
733
|
+
if (constructor) {
|
|
734
|
+
const boundConstructor = new BoundMethod(constructor, instance);
|
|
735
|
+
const args = node.arguments.map(arg => this.evaluate(arg));
|
|
736
|
+
boundConstructor.call(this, args);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return instance;
|
|
740
|
+
}
|
|
741
|
+
|
|
472
742
|
evaluateMemberExpression(node) {
|
|
473
743
|
const obj = this.evaluate(node.object);
|
|
474
744
|
if (obj === null || obj === undefined) {
|
|
@@ -492,6 +762,11 @@ class Interpreter {
|
|
|
492
762
|
}
|
|
493
763
|
}
|
|
494
764
|
|
|
765
|
+
// Handle class instances
|
|
766
|
+
if (obj instanceof VladXInstance) {
|
|
767
|
+
return obj.get(prop);
|
|
768
|
+
}
|
|
769
|
+
|
|
495
770
|
return obj[prop];
|
|
496
771
|
}
|
|
497
772
|
|
|
@@ -523,4 +798,4 @@ class Interpreter {
|
|
|
523
798
|
}
|
|
524
799
|
}
|
|
525
800
|
|
|
526
|
-
module.exports = { Interpreter, Environment };
|
|
801
|
+
module.exports = { Interpreter, Environment, VladXClass, VladXInstance };
|
package/src/parser/ast.js
CHANGED
|
@@ -36,6 +36,30 @@ class FunctionDeclaration extends ASTNode {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
class ClassDeclaration extends ASTNode {
|
|
40
|
+
constructor(name, superclass, constructor, methods) {
|
|
41
|
+
super('ClassDeclaration');
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.superclass = superclass;
|
|
44
|
+
this.constructor = constructor;
|
|
45
|
+
this.methods = methods;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class ThisExpression extends ASTNode {
|
|
50
|
+
constructor() {
|
|
51
|
+
super('ThisExpression');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class NewExpression extends ASTNode {
|
|
56
|
+
constructor(callee, args) {
|
|
57
|
+
super('NewExpression');
|
|
58
|
+
this.callee = callee;
|
|
59
|
+
this.arguments = args;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
39
63
|
// === Statements ===
|
|
40
64
|
class BlockStatement extends ASTNode {
|
|
41
65
|
constructor(body) {
|
|
@@ -104,6 +128,30 @@ class PrintStatement extends ASTNode {
|
|
|
104
128
|
}
|
|
105
129
|
}
|
|
106
130
|
|
|
131
|
+
class TryStatement extends ASTNode {
|
|
132
|
+
constructor(block, handler, finalizer = null) {
|
|
133
|
+
super('TryStatement');
|
|
134
|
+
this.block = block;
|
|
135
|
+
this.handler = handler;
|
|
136
|
+
this.finalizer = finalizer;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class CatchClause extends ASTNode {
|
|
141
|
+
constructor(param, body) {
|
|
142
|
+
super('CatchClause');
|
|
143
|
+
this.param = param;
|
|
144
|
+
this.body = body;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class ThrowStatement extends ASTNode {
|
|
149
|
+
constructor(argument) {
|
|
150
|
+
super('ThrowStatement');
|
|
151
|
+
this.argument = argument;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
107
155
|
// === Espressioni ===
|
|
108
156
|
class Identifier extends ASTNode {
|
|
109
157
|
constructor(name) {
|
|
@@ -260,6 +308,7 @@ module.exports = {
|
|
|
260
308
|
Program,
|
|
261
309
|
VariableDeclaration,
|
|
262
310
|
FunctionDeclaration,
|
|
311
|
+
ClassDeclaration,
|
|
263
312
|
BlockStatement,
|
|
264
313
|
IfStatement,
|
|
265
314
|
WhileStatement,
|
|
@@ -269,6 +318,9 @@ module.exports = {
|
|
|
269
318
|
ContinueStatement,
|
|
270
319
|
ExpressionStatement,
|
|
271
320
|
PrintStatement,
|
|
321
|
+
TryStatement,
|
|
322
|
+
CatchClause,
|
|
323
|
+
ThrowStatement,
|
|
272
324
|
ExportNamedDeclaration,
|
|
273
325
|
ImportDeclaration,
|
|
274
326
|
ImportSpecifier,
|
|
@@ -287,5 +339,7 @@ module.exports = {
|
|
|
287
339
|
MemberExpression,
|
|
288
340
|
ArrowFunction,
|
|
289
341
|
UpdateExpression,
|
|
290
|
-
LogicalExpression
|
|
342
|
+
LogicalExpression,
|
|
343
|
+
ThisExpression,
|
|
344
|
+
NewExpression
|
|
291
345
|
};
|
package/src/parser/parser.js
CHANGED
|
@@ -74,6 +74,7 @@ class Parser {
|
|
|
74
74
|
if (this.match(TokenType.VARIABILE)) return this.variableDeclaration(false);
|
|
75
75
|
if (this.match(TokenType.COSTANTE)) return this.variableDeclaration(true);
|
|
76
76
|
if (this.match(TokenType.FUNZIONE)) return this.functionDeclaration();
|
|
77
|
+
if (this.match(TokenType.CLASSE)) return this.classDeclaration();
|
|
77
78
|
return this.statement();
|
|
78
79
|
} catch (error) {
|
|
79
80
|
this.synchronize();
|
|
@@ -112,6 +113,10 @@ class Parser {
|
|
|
112
113
|
const decl = this.functionDeclaration();
|
|
113
114
|
return new AST.ExportNamedDeclaration(decl);
|
|
114
115
|
}
|
|
116
|
+
if (this.match(TokenType.CLASSE)) {
|
|
117
|
+
const decl = this.classDeclaration();
|
|
118
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
119
|
+
}
|
|
115
120
|
if (this.match(TokenType.VARIABILE)) {
|
|
116
121
|
const decl = this.variableDeclaration(false);
|
|
117
122
|
return new AST.ExportNamedDeclaration(decl);
|
|
@@ -120,7 +125,7 @@ class Parser {
|
|
|
120
125
|
const decl = this.variableDeclaration(true);
|
|
121
126
|
return new AST.ExportNamedDeclaration(decl);
|
|
122
127
|
}
|
|
123
|
-
throw new ParserError('Atteso funzione o variabile dopo "esporta"', this.peek());
|
|
128
|
+
throw new ParserError('Atteso funzione, classe o variabile dopo "esporta"', this.peek());
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
functionDeclaration() {
|
|
@@ -138,6 +143,54 @@ class Parser {
|
|
|
138
143
|
return new AST.FunctionDeclaration(name, params, body);
|
|
139
144
|
}
|
|
140
145
|
|
|
146
|
+
classDeclaration() {
|
|
147
|
+
// Il token CLASSE è già stato consumato se chiamato da exportDeclaration
|
|
148
|
+
// Ma se chiamato direttamente, dobbiamo consumarlo
|
|
149
|
+
if (!this.previous() || this.previous().type !== TokenType.CLASSE) {
|
|
150
|
+
if (this.check(TokenType.CLASSE)) {
|
|
151
|
+
this.advance();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome classe atteso').value;
|
|
155
|
+
|
|
156
|
+
let superclass = null;
|
|
157
|
+
if (this.match(TokenType.DA)) {
|
|
158
|
+
superclass = this.consume(TokenType.IDENTIFICATORE, 'Nome classe padre atteso').value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo nome classe');
|
|
162
|
+
|
|
163
|
+
let constructor = null;
|
|
164
|
+
const methods = [];
|
|
165
|
+
|
|
166
|
+
while (!this.check(TokenType.GRAFFA_CHIUSA) && !this.isAtEnd()) {
|
|
167
|
+
if (this.match(TokenType.FUNZIONE)) {
|
|
168
|
+
const funcName = this.consume(TokenType.IDENTIFICATORE, 'Nome metodo atteso').value;
|
|
169
|
+
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome metodo');
|
|
170
|
+
const params = [];
|
|
171
|
+
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
172
|
+
do {
|
|
173
|
+
params.push(this.consume(TokenType.IDENTIFICATORE, 'Nome parametro atteso').value);
|
|
174
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
175
|
+
}
|
|
176
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo parametri');
|
|
177
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" prima del corpo metodo');
|
|
178
|
+
const body = this.blockStatement();
|
|
179
|
+
|
|
180
|
+
if (funcName === 'costruttore') {
|
|
181
|
+
constructor = new AST.FunctionDeclaration('costruttore', params, body);
|
|
182
|
+
} else {
|
|
183
|
+
methods.push(new AST.FunctionDeclaration(funcName, params, body));
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
throw new ParserError('Atteso metodo nella classe', this.peek());
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" alla fine della classe');
|
|
191
|
+
return new AST.ClassDeclaration(name, superclass, constructor, methods);
|
|
192
|
+
}
|
|
193
|
+
|
|
141
194
|
statement() {
|
|
142
195
|
if (this.match(TokenType.SE)) return this.ifStatement();
|
|
143
196
|
if (this.match(TokenType.MENTRE)) return this.whileStatement();
|
|
@@ -146,6 +199,8 @@ class Parser {
|
|
|
146
199
|
if (this.match(TokenType.INTERROMPI)) return this.breakStatement();
|
|
147
200
|
if (this.match(TokenType.CONTINUA)) return this.continueStatement();
|
|
148
201
|
if (this.match(TokenType.STAMPA)) return this.printStatement();
|
|
202
|
+
if (this.match(TokenType.PROVA)) return this.tryStatement();
|
|
203
|
+
if (this.match(TokenType.LANCIA)) return this.throwStatement();
|
|
149
204
|
if (this.match(TokenType.GRAFFA_APERTA)) return this.blockStatement();
|
|
150
205
|
return this.expressionStatement();
|
|
151
206
|
}
|
|
@@ -242,6 +297,39 @@ class Parser {
|
|
|
242
297
|
return new AST.PrintStatement(argument);
|
|
243
298
|
}
|
|
244
299
|
|
|
300
|
+
tryStatement() {
|
|
301
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "prova"');
|
|
302
|
+
const block = this.blockStatement();
|
|
303
|
+
|
|
304
|
+
let handler = null;
|
|
305
|
+
if (this.match(TokenType.CATTURA)) {
|
|
306
|
+
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo "cattura"');
|
|
307
|
+
const param = this.consume(TokenType.IDENTIFICATORE, 'Nome variabile errore atteso').value;
|
|
308
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo nome variabile');
|
|
309
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo clausola "cattura"');
|
|
310
|
+
const body = this.blockStatement();
|
|
311
|
+
handler = new AST.CatchClause(param, body);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
let finalizer = null;
|
|
315
|
+
if (this.match(TokenType.FINALMENTE)) {
|
|
316
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "finalmente"');
|
|
317
|
+
finalizer = this.blockStatement();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!handler && !finalizer) {
|
|
321
|
+
throw new ParserError('Atteso "cattura" o "finalmente" dopo blocco "prova"', this.peek());
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return new AST.TryStatement(block, handler, finalizer);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
throwStatement() {
|
|
328
|
+
const argument = this.expression();
|
|
329
|
+
this.match(TokenType.PUNTO_VIRGOLA);
|
|
330
|
+
return new AST.ThrowStatement(argument);
|
|
331
|
+
}
|
|
332
|
+
|
|
245
333
|
expressionStatement() {
|
|
246
334
|
const expr = this.expression();
|
|
247
335
|
this.match(TokenType.PUNTO_VIRGOLA);
|
|
@@ -379,6 +467,19 @@ class Parser {
|
|
|
379
467
|
return expr;
|
|
380
468
|
}
|
|
381
469
|
|
|
470
|
+
newExpression() {
|
|
471
|
+
const callee = this.consume(TokenType.IDENTIFICATORE, 'Nome classe atteso dopo "nuovo"').value;
|
|
472
|
+
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome classe');
|
|
473
|
+
const args = [];
|
|
474
|
+
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
475
|
+
do {
|
|
476
|
+
args.push(this.expression());
|
|
477
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
478
|
+
}
|
|
479
|
+
this.consume(TokenType.PARENTESI_CHIUSA, 'Atteso ")" dopo argomenti');
|
|
480
|
+
return new AST.NewExpression(new AST.Identifier(callee), args);
|
|
481
|
+
}
|
|
482
|
+
|
|
382
483
|
finishCall(callee) {
|
|
383
484
|
const args = [];
|
|
384
485
|
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
@@ -403,12 +504,16 @@ class Parser {
|
|
|
403
504
|
return new AST.StringLiteral(this.previous().value);
|
|
404
505
|
}
|
|
405
506
|
|
|
507
|
+
if (this.match(TokenType.NUOVO)) {
|
|
508
|
+
return this.newExpression();
|
|
509
|
+
}
|
|
510
|
+
|
|
406
511
|
if (this.match(TokenType.IDENTIFICATORE)) {
|
|
407
512
|
return new AST.Identifier(this.previous().value);
|
|
408
513
|
}
|
|
409
514
|
|
|
410
515
|
if (this.match(TokenType.QUESTO)) {
|
|
411
|
-
return new AST.
|
|
516
|
+
return new AST.ThisExpression();
|
|
412
517
|
}
|
|
413
518
|
|
|
414
519
|
if (this.match(TokenType.QUADRA_APERTA)) {
|
|
@@ -478,7 +583,12 @@ class Parser {
|
|
|
478
583
|
const properties = [];
|
|
479
584
|
if (!this.check(TokenType.GRAFFA_CHIUSA)) {
|
|
480
585
|
do {
|
|
481
|
-
|
|
586
|
+
let key;
|
|
587
|
+
if (this.match(TokenType.IDENTIFICATORE, TokenType.STRINGA)) {
|
|
588
|
+
key = this.previous().value;
|
|
589
|
+
} else {
|
|
590
|
+
throw new ParserError('Nome proprietà o stringa atteso', this.peek());
|
|
591
|
+
}
|
|
482
592
|
this.consume(TokenType.DUE_PUNTI, 'Atteso ":" dopo nome proprietà');
|
|
483
593
|
const value = this.expression();
|
|
484
594
|
properties.push(new AST.Property(key, value));
|