vladx 1.2.3 → 1.2.5
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 +55 -0
- package/README.md +17 -0
- package/examples/test_classi.vx +42 -0
- package/examples/test_classi_semplice.vx +15 -0
- package/modules/logger/index.vx +68 -0
- package/modules/logger/vladx-lock.json +5 -0
- package/modules/logger/vladx.json +18 -0
- package/modules/stringhe/index.vx +248 -0
- package/modules/stringhe/vladx-lock.json +5 -0
- package/modules/stringhe/vladx.json +19 -0
- package/package.json +1 -1
- package/src/interpreter/interpreter.js +140 -2
- package/src/lexer/lexer.js +88 -2
- package/src/lexer/tokens.js +7 -1
- package/src/parser/ast.js +37 -1
- package/src/parser/parser.js +96 -2
package/DOCUMENTAZIONE.md
CHANGED
|
@@ -174,6 +174,61 @@ costante quadrato = (n) => n * n;
|
|
|
174
174
|
stampa(quadrato(4)); // 16
|
|
175
175
|
```
|
|
176
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
|
+
|
|
177
232
|
---
|
|
178
233
|
|
|
179
234
|
## 🧩 Moduli
|
package/README.md
CHANGED
|
@@ -63,6 +63,23 @@ funzione saluta(nome) {
|
|
|
63
63
|
costante somma = (a, b) => a + b;
|
|
64
64
|
```
|
|
65
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
|
+
|
|
66
83
|
### Gestione Errori (Try/Catch)
|
|
67
84
|
```javascript
|
|
68
85
|
prova {
|
|
@@ -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,68 @@
|
|
|
1
|
+
// vladx-logger - Logger per VladX
|
|
2
|
+
//
|
|
3
|
+
// API:
|
|
4
|
+
// - esporta funzione info(messaggio)
|
|
5
|
+
// - esporta funzione avviso(messaggio)
|
|
6
|
+
// - esporta funzione errore(messaggio)
|
|
7
|
+
// - esporta funzione debug(messaggio)
|
|
8
|
+
// - esporta funzione creaLogger(nome) -> oggetto con i 4 metodi sopra
|
|
9
|
+
//
|
|
10
|
+
// Ogni messaggio viene stampato con timestamp e livello.
|
|
11
|
+
|
|
12
|
+
funzione _oraIso() {
|
|
13
|
+
variabile ms = Sistema.esegui("date -u +\"%Y-%m-%dT%H:%M:%SZ\"");
|
|
14
|
+
ritorna ms;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
funzione _formattaMessaggio(livello, nome, messaggio) {
|
|
18
|
+
variabile prefissoNome = "";
|
|
19
|
+
|
|
20
|
+
se (nome && tipo(nome) === "string" && nome !== "") {
|
|
21
|
+
prefissoNome = "[" + nome + "] ";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ritorna "[" + _oraIso().trim() + "]" + "[" + livello + "] " + prefissoNome + messaggio;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
funzione _logGenerico(livello, nome, messaggio) {
|
|
28
|
+
// Se il messaggio non è stringa, proviamo a convertirlo
|
|
29
|
+
se (tipo(messaggio) !== "string") {
|
|
30
|
+
messaggio = stringa(messaggio);
|
|
31
|
+
}
|
|
32
|
+
stampa(_formattaMessaggio(livello, nome, messaggio));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
esporta funzione info(messaggio) {
|
|
36
|
+
_logGenerico("INFO", "", messaggio);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
esporta funzione avviso(messaggio) {
|
|
40
|
+
_logGenerico("AVVISO", "", messaggio);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
esporta funzione errore(messaggio) {
|
|
44
|
+
_logGenerico("ERRORE", "", messaggio);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
esporta funzione debug(messaggio) {
|
|
48
|
+
_logGenerico("DEBUG", "", messaggio);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Logger con nome (es. modulo, servizio, contesto)
|
|
52
|
+
esporta funzione creaLogger(nome) {
|
|
53
|
+
ritorna {
|
|
54
|
+
info: (messaggio) => _logGenerico("INFO", nome, messaggio),
|
|
55
|
+
avviso: (messaggio) => _logGenerico("AVVISO", nome, messaggio),
|
|
56
|
+
errore: (messaggio) => _logGenerico("ERRORE", nome, messaggio),
|
|
57
|
+
debug: (messaggio) => _logGenerico("DEBUG", nome, messaggio)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Esempio rapido se eseguito direttamente
|
|
62
|
+
se (Sistema.argomenti && Sistema.argomenti.length > 0) {
|
|
63
|
+
variabile log = creaLogger("esempio-logger");
|
|
64
|
+
info("Logger globale attivo");
|
|
65
|
+
log.debug("Messaggio di debug");
|
|
66
|
+
log.info("Informazione di esempio");
|
|
67
|
+
log.errore("Errore di esempio");
|
|
68
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vladx-logger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Logger semplice per VladX con livelli e prefissi",
|
|
5
|
+
"main": "index.vx",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "vladx index.vx",
|
|
8
|
+
"test": "vladx test.vx"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"logger",
|
|
12
|
+
"logging",
|
|
13
|
+
"vladx"
|
|
14
|
+
],
|
|
15
|
+
"author": "VladX Team",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"dependencies": {}
|
|
18
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// vladx-stringhe - Utility per stringhe in VladX
|
|
2
|
+
//
|
|
3
|
+
// API principale (tutte esportate):
|
|
4
|
+
// - trim(testo)
|
|
5
|
+
// - minuscolo(testo)
|
|
6
|
+
// - maiuscolo(testo)
|
|
7
|
+
// - iniziaCon(testo, prefisso)
|
|
8
|
+
// - finisceCon(testo, suffisso)
|
|
9
|
+
// - contiene(testo, parte)
|
|
10
|
+
// - sostituisciTutto(testo, cerca, sostituisci)
|
|
11
|
+
// - slug(testo)
|
|
12
|
+
// - ripeti(testo, volte)
|
|
13
|
+
// - unisci(separatore, elementiArray)
|
|
14
|
+
|
|
15
|
+
funzione _assicuratiStringa(valore) {
|
|
16
|
+
se (tipo(valore) === "string") {
|
|
17
|
+
ritorna valore;
|
|
18
|
+
}
|
|
19
|
+
ritorna stringa(valore);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
esporta funzione trim(testo) {
|
|
23
|
+
testo = _assicuratiStringa(testo);
|
|
24
|
+
|
|
25
|
+
// rimuove spazi iniziali
|
|
26
|
+
mentre (lunghezza(testo) > 0 && (
|
|
27
|
+
testo[0] === " " ||
|
|
28
|
+
testo[0] === "\n" ||
|
|
29
|
+
testo[0] === "\t" ||
|
|
30
|
+
testo[0] === "\r"
|
|
31
|
+
)) {
|
|
32
|
+
testo = testo.substring(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// rimuove spazi finali
|
|
36
|
+
mentre (lunghezza(testo) > 0) {
|
|
37
|
+
variabile ultimo = testo[lunghezza(testo) - 1];
|
|
38
|
+
se (ultimo === " " || ultimo === "\n" || ultimo === "\t" || ultimo === "\r") {
|
|
39
|
+
testo = testo.substring(0, lunghezza(testo) - 1);
|
|
40
|
+
} altrimenti {
|
|
41
|
+
interrompi;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ritorna testo;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
esporta funzione minuscolo(testo) {
|
|
49
|
+
testo = _assicuratiStringa(testo);
|
|
50
|
+
ritorna testo.toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
esporta funzione maiuscolo(testo) {
|
|
54
|
+
testo = _assicuratiStringa(testo);
|
|
55
|
+
ritorna testo.toUpperCase();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
esporta funzione iniziaCon(testo, prefisso) {
|
|
59
|
+
testo = _assicuratiStringa(testo);
|
|
60
|
+
prefisso = _assicuratiStringa(prefisso);
|
|
61
|
+
|
|
62
|
+
se (lunghezza(prefisso) > lunghezza(testo)) {
|
|
63
|
+
ritorna falso;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
variabile i = 0;
|
|
67
|
+
mentre (i < lunghezza(prefisso)) {
|
|
68
|
+
se (testo[i] !== prefisso[i]) {
|
|
69
|
+
ritorna falso;
|
|
70
|
+
}
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
ritorna vero;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
esporta funzione finisceCon(testo, suffisso) {
|
|
78
|
+
testo = _assicuratiStringa(testo);
|
|
79
|
+
suffisso = _assicuratiStringa(suffisso);
|
|
80
|
+
|
|
81
|
+
se (lunghezza(suffisso) > lunghezza(testo)) {
|
|
82
|
+
ritorna falso;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
variabile offset = lunghezza(testo) - lunghezza(suffisso);
|
|
86
|
+
variabile i = 0;
|
|
87
|
+
mentre (i < lunghezza(suffisso)) {
|
|
88
|
+
se (testo[offset + i] !== suffisso[i]) {
|
|
89
|
+
ritorna falso;
|
|
90
|
+
}
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ritorna vero;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
esporta funzione contiene(testo, parte) {
|
|
98
|
+
testo = _assicuratiStringa(testo);
|
|
99
|
+
parte = _assicuratiStringa(parte);
|
|
100
|
+
|
|
101
|
+
se (lunghezza(parte) === 0) {
|
|
102
|
+
ritorna vero;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
variabile i = 0;
|
|
106
|
+
mentre (i <= lunghezza(testo) - lunghezza(parte)) {
|
|
107
|
+
variabile j = 0;
|
|
108
|
+
variabile match = vero;
|
|
109
|
+
|
|
110
|
+
mentre (j < lunghezza(parte)) {
|
|
111
|
+
se (testo[i + j] !== parte[j]) {
|
|
112
|
+
match = falso;
|
|
113
|
+
interrompi;
|
|
114
|
+
}
|
|
115
|
+
j++;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
se (match) {
|
|
119
|
+
ritorna vero;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
ritorna falso;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
esporta funzione sostituisciTutto(testo, cerca, sostituisci) {
|
|
129
|
+
testo = _assicuratiStringa(testo);
|
|
130
|
+
cerca = _assicuratiStringa(cerca);
|
|
131
|
+
sostituisci = _assicuratiStringa(sostituisci);
|
|
132
|
+
|
|
133
|
+
se (lunghezza(cerca) === 0) {
|
|
134
|
+
ritorna testo;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
variabile risultato = "";
|
|
138
|
+
variabile i = 0;
|
|
139
|
+
|
|
140
|
+
mentre (i < lunghezza(testo)) {
|
|
141
|
+
variabile j = 0;
|
|
142
|
+
variabile match = vero;
|
|
143
|
+
|
|
144
|
+
mentre (j < lunghezza(cerca) && i + j < lunghezza(testo)) {
|
|
145
|
+
se (testo[i + j] !== cerca[j]) {
|
|
146
|
+
match = falso;
|
|
147
|
+
interrompi;
|
|
148
|
+
}
|
|
149
|
+
j++;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
se (match && j === lunghezza(cerca)) {
|
|
153
|
+
risultato = risultato + sostituisci;
|
|
154
|
+
i = i + lunghezza(cerca);
|
|
155
|
+
} altrimenti {
|
|
156
|
+
risultato = risultato + testo[i];
|
|
157
|
+
i = i + 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
ritorna risultato;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
esporta funzione slug(testo) {
|
|
165
|
+
testo = _assicuratiStringa(testo);
|
|
166
|
+
|
|
167
|
+
// minuscolo
|
|
168
|
+
testo = testo.toLowerCase();
|
|
169
|
+
|
|
170
|
+
// sostituisci spazi con -
|
|
171
|
+
testo = sostituisciTutto(testo, " ", "-");
|
|
172
|
+
|
|
173
|
+
// rimuovi caratteri non alfanumerici (mantieni -)
|
|
174
|
+
variabile risultato = "";
|
|
175
|
+
variabile i = 0;
|
|
176
|
+
mentre (i < lunghezza(testo)) {
|
|
177
|
+
variabile c = testo[i];
|
|
178
|
+
|
|
179
|
+
se (
|
|
180
|
+
(c >= "a" && c <= "z") ||
|
|
181
|
+
(c >= "0" && c <= "9") ||
|
|
182
|
+
c === "-"
|
|
183
|
+
) {
|
|
184
|
+
risultato = risultato + c;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
i++;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// rimuovi eventuali -- doppi
|
|
191
|
+
mentre (contiene(risultato, "--")) {
|
|
192
|
+
risultato = sostituisciTutto(risultato, "--", "-");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// togli - iniziali e finali
|
|
196
|
+
risultato = trim(risultato);
|
|
197
|
+
mentre (lunghezza(risultato) > 0 && risultato[0] === "-") {
|
|
198
|
+
risultato = risultato.substring(1);
|
|
199
|
+
}
|
|
200
|
+
mentre (lunghezza(risultato) > 0 && risultato[lunghezza(risultato) - 1] === "-") {
|
|
201
|
+
risultato = risultato.substring(0, lunghezza(risultato) - 1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
ritorna risultato;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
esporta funzione ripeti(testo, volte) {
|
|
208
|
+
testo = _assicuratiStringa(testo);
|
|
209
|
+
se (tipo(volte) !== "number" || volte <= 0) {
|
|
210
|
+
ritorna "";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
variabile risultato = "";
|
|
214
|
+
variabile i = 0;
|
|
215
|
+
mentre (i < volte) {
|
|
216
|
+
risultato = risultato + testo;
|
|
217
|
+
i++;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
ritorna risultato;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
esporta funzione unisci(separatore, elementi) {
|
|
224
|
+
separatore = _assicuratiStringa(separatore);
|
|
225
|
+
|
|
226
|
+
se (!array(elementi)) {
|
|
227
|
+
ritorna "";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
se (lunghezza(elementi) === 0) {
|
|
231
|
+
ritorna "";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
variabile risultato = _assicuratiStringa(elementi[0]);
|
|
235
|
+
variabile i = 1;
|
|
236
|
+
|
|
237
|
+
mentre (i < lunghezza(elementi)) {
|
|
238
|
+
risultato = risultato + separatore + _assicuratiStringa(elementi[i]);
|
|
239
|
+
i++;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
ritorna risultato;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Esempio rapido se eseguito direttamente
|
|
246
|
+
se (Sistema.argomenti && Sistema.argomenti.length > 0) {
|
|
247
|
+
stampa(slug("Ciao Mondo da VladX Stringhe!"));
|
|
248
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vladx-stringhe",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Utility per la manipolazione di stringhe in VladX",
|
|
5
|
+
"main": "index.vx",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "vladx index.vx",
|
|
8
|
+
"test": "vladx test.vx"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"string",
|
|
12
|
+
"stringhe",
|
|
13
|
+
"vladx",
|
|
14
|
+
"utility"
|
|
15
|
+
],
|
|
16
|
+
"author": "VladX Team",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {}
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -116,6 +116,72 @@ 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');
|
|
121
187
|
const http = require('http');
|
|
@@ -288,6 +354,8 @@ class Interpreter {
|
|
|
288
354
|
return this.executeVariableDeclaration(node);
|
|
289
355
|
case 'FunctionDeclaration':
|
|
290
356
|
return this.executeFunctionDeclaration(node);
|
|
357
|
+
case 'ClassDeclaration':
|
|
358
|
+
return this.executeClassDeclaration(node);
|
|
291
359
|
case 'BlockStatement':
|
|
292
360
|
return this.executeBlock(node.body, new Environment(this.environment));
|
|
293
361
|
case 'IfStatement':
|
|
@@ -329,6 +397,28 @@ class Interpreter {
|
|
|
329
397
|
this.environment.define(node.name, func);
|
|
330
398
|
}
|
|
331
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
|
+
|
|
332
422
|
executeBlock(statements, env) {
|
|
333
423
|
const previousEnv = this.environment;
|
|
334
424
|
this.environment = env;
|
|
@@ -502,6 +592,8 @@ class Interpreter {
|
|
|
502
592
|
return null;
|
|
503
593
|
case 'Identifier':
|
|
504
594
|
return this.environment.get(node.name);
|
|
595
|
+
case 'ThisExpression':
|
|
596
|
+
return this.environment.get('questo');
|
|
505
597
|
case 'ArrayLiteral':
|
|
506
598
|
return node.elements.map(el => this.evaluate(el));
|
|
507
599
|
case 'ObjectLiteral':
|
|
@@ -522,15 +614,31 @@ class Interpreter {
|
|
|
522
614
|
return this.evaluateUpdateExpression(node);
|
|
523
615
|
case 'CallExpression':
|
|
524
616
|
return this.evaluateCallExpression(node);
|
|
617
|
+
case 'NewExpression':
|
|
618
|
+
return this.evaluateNewExpression(node);
|
|
525
619
|
case 'MemberExpression':
|
|
526
620
|
return this.evaluateMemberExpression(node);
|
|
527
621
|
case 'ArrowFunction':
|
|
528
622
|
return new ArrowFunc(node.params, node.body, this.environment);
|
|
623
|
+
case 'TemplateLiteral':
|
|
624
|
+
return this.evaluateTemplateLiteral(node);
|
|
529
625
|
default:
|
|
530
626
|
throw new Error(`Tipo espressione sconosciuto: ${node.type}`);
|
|
531
627
|
}
|
|
532
628
|
}
|
|
533
629
|
|
|
630
|
+
evaluateTemplateLiteral(node) {
|
|
631
|
+
let result = node.quasis[0];
|
|
632
|
+
|
|
633
|
+
for (let i = 0; i < node.expressions.length; i++) {
|
|
634
|
+
const value = this.evaluate(node.expressions[i]);
|
|
635
|
+
result += this.stringify(value);
|
|
636
|
+
result += node.quasis[i + 1];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
|
|
534
642
|
evaluateBinaryExpression(node) {
|
|
535
643
|
const left = this.evaluate(node.left);
|
|
536
644
|
const right = this.evaluate(node.right);
|
|
@@ -592,7 +700,13 @@ class Interpreter {
|
|
|
592
700
|
const prop = node.left.computed
|
|
593
701
|
? this.evaluate(node.left.property)
|
|
594
702
|
: node.left.property.name;
|
|
595
|
-
|
|
703
|
+
|
|
704
|
+
// Handle class instances
|
|
705
|
+
if (obj instanceof VladXInstance) {
|
|
706
|
+
obj.set(prop, value);
|
|
707
|
+
} else {
|
|
708
|
+
obj[prop] = value;
|
|
709
|
+
}
|
|
596
710
|
}
|
|
597
711
|
|
|
598
712
|
return value;
|
|
@@ -620,6 +734,25 @@ class Interpreter {
|
|
|
620
734
|
throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
|
|
621
735
|
}
|
|
622
736
|
|
|
737
|
+
evaluateNewExpression(node) {
|
|
738
|
+
const klass = this.evaluate(node.callee);
|
|
739
|
+
|
|
740
|
+
if (!(klass instanceof VladXClass)) {
|
|
741
|
+
throw new Error(`"${node.callee.name || 'valore'}" non è una classe`);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const instance = new VladXInstance(klass);
|
|
745
|
+
const constructor = klass.findMethod('costruttore');
|
|
746
|
+
|
|
747
|
+
if (constructor) {
|
|
748
|
+
const boundConstructor = new BoundMethod(constructor, instance);
|
|
749
|
+
const args = node.arguments.map(arg => this.evaluate(arg));
|
|
750
|
+
boundConstructor.call(this, args);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return instance;
|
|
754
|
+
}
|
|
755
|
+
|
|
623
756
|
evaluateMemberExpression(node) {
|
|
624
757
|
const obj = this.evaluate(node.object);
|
|
625
758
|
if (obj === null || obj === undefined) {
|
|
@@ -643,6 +776,11 @@ class Interpreter {
|
|
|
643
776
|
}
|
|
644
777
|
}
|
|
645
778
|
|
|
779
|
+
// Handle class instances
|
|
780
|
+
if (obj instanceof VladXInstance) {
|
|
781
|
+
return obj.get(prop);
|
|
782
|
+
}
|
|
783
|
+
|
|
646
784
|
return obj[prop];
|
|
647
785
|
}
|
|
648
786
|
|
|
@@ -674,4 +812,4 @@ class Interpreter {
|
|
|
674
812
|
}
|
|
675
813
|
}
|
|
676
814
|
|
|
677
|
-
module.exports = { Interpreter, Environment };
|
|
815
|
+
module.exports = { Interpreter, Environment, VladXClass, VladXInstance };
|
package/src/lexer/lexer.js
CHANGED
|
@@ -25,6 +25,8 @@ class Lexer {
|
|
|
25
25
|
this.current = 0;
|
|
26
26
|
this.line = 1;
|
|
27
27
|
this.column = 1;
|
|
28
|
+
this.braceDepth = 0;
|
|
29
|
+
this.templateStack = [];
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
peek() {
|
|
@@ -149,6 +151,70 @@ class Lexer {
|
|
|
149
151
|
this.tokens.push(new Token(TokenType.STRINGA, value, startLine, startColumn));
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
readTemplateString(isRestart) {
|
|
155
|
+
// If not restarting (fresh backtick), consume the backtick
|
|
156
|
+
if (!isRestart) {
|
|
157
|
+
this.advance();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let value = '';
|
|
161
|
+
const startLine = this.line;
|
|
162
|
+
const startColumn = this.column;
|
|
163
|
+
|
|
164
|
+
while (!this.isAtEnd()) {
|
|
165
|
+
// Check for end of template string
|
|
166
|
+
if (this.peek() === '`') {
|
|
167
|
+
this.advance(); // consume backtick
|
|
168
|
+
if (isRestart) {
|
|
169
|
+
this.tokens.push(new Token(TokenType.TEMPLATE_FINE, value, startLine, startColumn));
|
|
170
|
+
} else {
|
|
171
|
+
this.tokens.push(new Token(TokenType.TEMPLATE_COMPLETO, value, startLine, startColumn));
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check for start of interpolation
|
|
177
|
+
if (this.peek() === '$' && this.peekNext() === '{') {
|
|
178
|
+
this.advance(); // consume $
|
|
179
|
+
this.advance(); // consume {
|
|
180
|
+
|
|
181
|
+
if (isRestart) {
|
|
182
|
+
this.tokens.push(new Token(TokenType.TEMPLATE_CENTRO, value, startLine, startColumn));
|
|
183
|
+
} else {
|
|
184
|
+
this.tokens.push(new Token(TokenType.TEMPLATE_INIZIO, value, startLine, startColumn));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Track brace depth for this interpolation
|
|
188
|
+
this.templateStack.push(this.braceDepth);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Handle escapes
|
|
193
|
+
if (this.peek() === '\\') {
|
|
194
|
+
this.advance();
|
|
195
|
+
if (this.isAtEnd()) break;
|
|
196
|
+
|
|
197
|
+
const escaped = this.peek();
|
|
198
|
+
switch (escaped) {
|
|
199
|
+
case 'n': value += '\n'; break;
|
|
200
|
+
case 't': value += '\t'; break;
|
|
201
|
+
case 'r': value += '\r'; break;
|
|
202
|
+
case '\\': value += '\\'; break;
|
|
203
|
+
case '"': value += '"'; break;
|
|
204
|
+
case "'": value += "'"; break;
|
|
205
|
+
case '`': value += '`'; break;
|
|
206
|
+
case '$': value += '$'; break;
|
|
207
|
+
default: value += escaped;
|
|
208
|
+
}
|
|
209
|
+
this.advance();
|
|
210
|
+
} else {
|
|
211
|
+
value += this.advance();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
throw new Error(`Template string non terminata alla riga ${startLine}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
152
218
|
readIdentifier() {
|
|
153
219
|
const startColumn = this.column;
|
|
154
220
|
let value = '';
|
|
@@ -182,8 +248,22 @@ class Lexer {
|
|
|
182
248
|
switch (char) {
|
|
183
249
|
case '(': this.addToken(TokenType.PARENTESI_APERTA, '('); break;
|
|
184
250
|
case ')': this.addToken(TokenType.PARENTESI_CHIUSA, ')'); break;
|
|
185
|
-
|
|
186
|
-
case '
|
|
251
|
+
|
|
252
|
+
case '{':
|
|
253
|
+
this.braceDepth++;
|
|
254
|
+
this.addToken(TokenType.GRAFFA_APERTA, '{');
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case '}':
|
|
258
|
+
if (this.templateStack.length > 0 && this.braceDepth === this.templateStack[this.templateStack.length - 1]) {
|
|
259
|
+
this.templateStack.pop();
|
|
260
|
+
this.readTemplateString(true);
|
|
261
|
+
} else {
|
|
262
|
+
this.braceDepth--;
|
|
263
|
+
this.addToken(TokenType.GRAFFA_CHIUSA, '}');
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
|
|
187
267
|
case '[': this.addToken(TokenType.QUADRA_APERTA, '['); break;
|
|
188
268
|
case ']': this.addToken(TokenType.QUADRA_CHIUSA, ']'); break;
|
|
189
269
|
case ',': this.addToken(TokenType.VIRGOLA, ','); break;
|
|
@@ -253,6 +333,12 @@ class Lexer {
|
|
|
253
333
|
this.readString(char);
|
|
254
334
|
break;
|
|
255
335
|
|
|
336
|
+
case '`':
|
|
337
|
+
this.current--;
|
|
338
|
+
this.column--;
|
|
339
|
+
this.readTemplateString(false);
|
|
340
|
+
break;
|
|
341
|
+
|
|
256
342
|
default:
|
|
257
343
|
if (this.isDigit(char)) {
|
|
258
344
|
this.current--;
|
package/src/lexer/tokens.js
CHANGED
|
@@ -78,7 +78,13 @@ const TokenType = {
|
|
|
78
78
|
|
|
79
79
|
// Special
|
|
80
80
|
FINE: 'FINE',
|
|
81
|
-
SCONOSCIUTO: 'SCONOSCIUTO'
|
|
81
|
+
SCONOSCIUTO: 'SCONOSCIUTO',
|
|
82
|
+
|
|
83
|
+
// Template Strings
|
|
84
|
+
TEMPLATE_COMPLETO: 'TEMPLATE_COMPLETO', // `stringa`
|
|
85
|
+
TEMPLATE_INIZIO: 'TEMPLATE_INIZIO', // `str${
|
|
86
|
+
TEMPLATE_CENTRO: 'TEMPLATE_CENTRO', // }str${
|
|
87
|
+
TEMPLATE_FINE: 'TEMPLATE_FINE' // }str`
|
|
82
88
|
};
|
|
83
89
|
|
|
84
90
|
const KEYWORDS = {
|
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) {
|
|
@@ -150,6 +174,14 @@ class StringLiteral extends ASTNode {
|
|
|
150
174
|
}
|
|
151
175
|
}
|
|
152
176
|
|
|
177
|
+
class TemplateLiteral extends ASTNode {
|
|
178
|
+
constructor(quasis, expressions) {
|
|
179
|
+
super('TemplateLiteral');
|
|
180
|
+
this.quasis = quasis;
|
|
181
|
+
this.expressions = expressions;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
153
185
|
class BooleanLiteral extends ASTNode {
|
|
154
186
|
constructor(value) {
|
|
155
187
|
super('BooleanLiteral');
|
|
@@ -284,6 +316,7 @@ module.exports = {
|
|
|
284
316
|
Program,
|
|
285
317
|
VariableDeclaration,
|
|
286
318
|
FunctionDeclaration,
|
|
319
|
+
ClassDeclaration,
|
|
287
320
|
BlockStatement,
|
|
288
321
|
IfStatement,
|
|
289
322
|
WhileStatement,
|
|
@@ -314,5 +347,8 @@ module.exports = {
|
|
|
314
347
|
MemberExpression,
|
|
315
348
|
ArrowFunction,
|
|
316
349
|
UpdateExpression,
|
|
317
|
-
LogicalExpression
|
|
350
|
+
LogicalExpression,
|
|
351
|
+
ThisExpression,
|
|
352
|
+
NewExpression,
|
|
353
|
+
TemplateLiteral
|
|
318
354
|
};
|
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();
|
|
@@ -414,6 +467,19 @@ class Parser {
|
|
|
414
467
|
return expr;
|
|
415
468
|
}
|
|
416
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
|
+
|
|
417
483
|
finishCall(callee) {
|
|
418
484
|
const args = [];
|
|
419
485
|
if (!this.check(TokenType.PARENTESI_CHIUSA)) {
|
|
@@ -438,12 +504,40 @@ class Parser {
|
|
|
438
504
|
return new AST.StringLiteral(this.previous().value);
|
|
439
505
|
}
|
|
440
506
|
|
|
507
|
+
if (this.match(TokenType.TEMPLATE_COMPLETO)) {
|
|
508
|
+
return new AST.TemplateLiteral([this.previous().value], []);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (this.match(TokenType.TEMPLATE_INIZIO)) {
|
|
512
|
+
const quasis = [this.previous().value];
|
|
513
|
+
const expressions = [];
|
|
514
|
+
|
|
515
|
+
while (true) {
|
|
516
|
+
expressions.push(this.expression());
|
|
517
|
+
|
|
518
|
+
if (this.match(TokenType.TEMPLATE_CENTRO)) {
|
|
519
|
+
quasis.push(this.previous().value);
|
|
520
|
+
} else if (this.match(TokenType.TEMPLATE_FINE)) {
|
|
521
|
+
quasis.push(this.previous().value);
|
|
522
|
+
break;
|
|
523
|
+
} else {
|
|
524
|
+
throw new ParserError('Atteso parte centrale o finale della template string', this.peek());
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return new AST.TemplateLiteral(quasis, expressions);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (this.match(TokenType.NUOVO)) {
|
|
532
|
+
return this.newExpression();
|
|
533
|
+
}
|
|
534
|
+
|
|
441
535
|
if (this.match(TokenType.IDENTIFICATORE)) {
|
|
442
536
|
return new AST.Identifier(this.previous().value);
|
|
443
537
|
}
|
|
444
538
|
|
|
445
539
|
if (this.match(TokenType.QUESTO)) {
|
|
446
|
-
return new AST.
|
|
540
|
+
return new AST.ThisExpression();
|
|
447
541
|
}
|
|
448
542
|
|
|
449
543
|
if (this.match(TokenType.QUADRA_APERTA)) {
|