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 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,5 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "requires": true,
4
+ "dependencies": {}
5
+ }
@@ -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,5 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "requires": true,
4
+ "dependencies": {}
5
+ }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vladx",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "VladX - Linguaggio di programmazione con sintassi italiana",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -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
- obj[prop] = value;
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 };
@@ -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
- case '{': this.addToken(TokenType.GRAFFA_APERTA, '{'); break;
186
- case '}': this.addToken(TokenType.GRAFFA_CHIUSA, '}'); break;
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--;
@@ -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
  };
@@ -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.Identifier('questo');
540
+ return new AST.ThisExpression();
447
541
  }
448
542
 
449
543
  if (this.match(TokenType.QUADRA_APERTA)) {