vladx 1.2.4 → 1.2.6
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/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/programs/main/index.vx +21 -0
- package/programs/main/vladx-lock.json +5 -0
- package/programs/main/vladx.json +14 -0
- package/src/interpreter/interpreter.js +43 -3
- package/src/lexer/lexer.js +88 -2
- package/src/lexer/tokens.js +7 -1
- package/src/parser/ast.js +10 -1
- package/src/parser/parser.js +25 -1
|
@@ -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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
variabile nome = chiedi("Come ti chiami? ");
|
|
2
|
+
stampa(`Ciao ${nome}`);
|
|
3
|
+
|
|
4
|
+
variabile eta = 20
|
|
5
|
+
stampa(`Ho ${eta} anni`)
|
|
6
|
+
|
|
7
|
+
variabile persona = {nome: "Vlad", eta: 20}
|
|
8
|
+
stampa(`Persona: ${persona}`)
|
|
9
|
+
|
|
10
|
+
variabile lista = [1, 2, 3, 4, 5]
|
|
11
|
+
stampa(`Lista: ${lista}`)
|
|
12
|
+
|
|
13
|
+
variabile lista_di_liste = [[1, 2], [3, 4], [5, 6]]
|
|
14
|
+
stampa(`Lista di liste: ${lista_di_liste}`)
|
|
15
|
+
|
|
16
|
+
variabile lista_di_liste_di_liste = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
17
|
+
stampa(`Lista di liste di liste: ${lista_di_liste_di_liste}`)
|
|
18
|
+
|
|
19
|
+
variabile lista_di_liste_di_liste_di_liste = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
20
|
+
stampa(`Lista di liste di liste di liste: ${lista_di_liste_di_liste_di_liste}`)
|
|
21
|
+
|
|
@@ -236,6 +236,32 @@ class Interpreter {
|
|
|
236
236
|
}
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
this.globals.define('chiedi', {
|
|
240
|
+
call: (_, args) => {
|
|
241
|
+
if (args[0] !== undefined) {
|
|
242
|
+
process.stdout.write(String(args[0]));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const buffer = Buffer.alloc(1);
|
|
246
|
+
let input = '';
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
while (true) {
|
|
250
|
+
const bytesRead = fs.readSync(0, buffer, 0, 1, null);
|
|
251
|
+
if (bytesRead === 0) break; // EOF
|
|
252
|
+
const char = buffer.toString('utf8');
|
|
253
|
+
if (char === '\n') break;
|
|
254
|
+
if (char === '\r') continue;
|
|
255
|
+
input += char;
|
|
256
|
+
}
|
|
257
|
+
} catch (e) {
|
|
258
|
+
// Ignore errors on read (e.g. pipe closed)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return input;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
239
265
|
this._registerStdLib();
|
|
240
266
|
}
|
|
241
267
|
|
|
@@ -620,11 +646,25 @@ class Interpreter {
|
|
|
620
646
|
return this.evaluateMemberExpression(node);
|
|
621
647
|
case 'ArrowFunction':
|
|
622
648
|
return new ArrowFunc(node.params, node.body, this.environment);
|
|
649
|
+
case 'TemplateLiteral':
|
|
650
|
+
return this.evaluateTemplateLiteral(node);
|
|
623
651
|
default:
|
|
624
652
|
throw new Error(`Tipo espressione sconosciuto: ${node.type}`);
|
|
625
653
|
}
|
|
626
654
|
}
|
|
627
655
|
|
|
656
|
+
evaluateTemplateLiteral(node) {
|
|
657
|
+
let result = node.quasis[0];
|
|
658
|
+
|
|
659
|
+
for (let i = 0; i < node.expressions.length; i++) {
|
|
660
|
+
const value = this.evaluate(node.expressions[i]);
|
|
661
|
+
result += this.stringify(value);
|
|
662
|
+
result += node.quasis[i + 1];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return result;
|
|
666
|
+
}
|
|
667
|
+
|
|
628
668
|
evaluateBinaryExpression(node) {
|
|
629
669
|
const left = this.evaluate(node.left);
|
|
630
670
|
const right = this.evaluate(node.right);
|
|
@@ -686,7 +726,7 @@ class Interpreter {
|
|
|
686
726
|
const prop = node.left.computed
|
|
687
727
|
? this.evaluate(node.left.property)
|
|
688
728
|
: node.left.property.name;
|
|
689
|
-
|
|
729
|
+
|
|
690
730
|
// Handle class instances
|
|
691
731
|
if (obj instanceof VladXInstance) {
|
|
692
732
|
obj.set(prop, value);
|
|
@@ -722,14 +762,14 @@ class Interpreter {
|
|
|
722
762
|
|
|
723
763
|
evaluateNewExpression(node) {
|
|
724
764
|
const klass = this.evaluate(node.callee);
|
|
725
|
-
|
|
765
|
+
|
|
726
766
|
if (!(klass instanceof VladXClass)) {
|
|
727
767
|
throw new Error(`"${node.callee.name || 'valore'}" non è una classe`);
|
|
728
768
|
}
|
|
729
769
|
|
|
730
770
|
const instance = new VladXInstance(klass);
|
|
731
771
|
const constructor = klass.findMethod('costruttore');
|
|
732
|
-
|
|
772
|
+
|
|
733
773
|
if (constructor) {
|
|
734
774
|
const boundConstructor = new BoundMethod(constructor, instance);
|
|
735
775
|
const args = node.arguments.map(arg => this.evaluate(arg));
|
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
|
@@ -174,6 +174,14 @@ class StringLiteral extends ASTNode {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
class TemplateLiteral extends ASTNode {
|
|
178
|
+
constructor(quasis, expressions) {
|
|
179
|
+
super('TemplateLiteral');
|
|
180
|
+
this.quasis = quasis;
|
|
181
|
+
this.expressions = expressions;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
177
185
|
class BooleanLiteral extends ASTNode {
|
|
178
186
|
constructor(value) {
|
|
179
187
|
super('BooleanLiteral');
|
|
@@ -341,5 +349,6 @@ module.exports = {
|
|
|
341
349
|
UpdateExpression,
|
|
342
350
|
LogicalExpression,
|
|
343
351
|
ThisExpression,
|
|
344
|
-
NewExpression
|
|
352
|
+
NewExpression,
|
|
353
|
+
TemplateLiteral
|
|
345
354
|
};
|
package/src/parser/parser.js
CHANGED
|
@@ -152,7 +152,7 @@ class Parser {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome classe atteso').value;
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
let superclass = null;
|
|
157
157
|
if (this.match(TokenType.DA)) {
|
|
158
158
|
superclass = this.consume(TokenType.IDENTIFICATORE, 'Nome classe padre atteso').value;
|
|
@@ -504,6 +504,30 @@ class Parser {
|
|
|
504
504
|
return new AST.StringLiteral(this.previous().value);
|
|
505
505
|
}
|
|
506
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
|
+
|
|
507
531
|
if (this.match(TokenType.NUOVO)) {
|
|
508
532
|
return this.newExpression();
|
|
509
533
|
}
|