vladx 1.2.3 → 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 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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vladx",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
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,6 +614,8 @@ 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':
@@ -592,7 +686,13 @@ class Interpreter {
592
686
  const prop = node.left.computed
593
687
  ? this.evaluate(node.left.property)
594
688
  : node.left.property.name;
595
- obj[prop] = value;
689
+
690
+ // Handle class instances
691
+ if (obj instanceof VladXInstance) {
692
+ obj.set(prop, value);
693
+ } else {
694
+ obj[prop] = value;
695
+ }
596
696
  }
597
697
 
598
698
  return value;
@@ -620,6 +720,25 @@ class Interpreter {
620
720
  throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
621
721
  }
622
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
+
623
742
  evaluateMemberExpression(node) {
624
743
  const obj = this.evaluate(node.object);
625
744
  if (obj === null || obj === undefined) {
@@ -643,6 +762,11 @@ class Interpreter {
643
762
  }
644
763
  }
645
764
 
765
+ // Handle class instances
766
+ if (obj instanceof VladXInstance) {
767
+ return obj.get(prop);
768
+ }
769
+
646
770
  return obj[prop];
647
771
  }
648
772
 
@@ -674,4 +798,4 @@ class Interpreter {
674
798
  }
675
799
  }
676
800
 
677
- 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) {
@@ -284,6 +308,7 @@ module.exports = {
284
308
  Program,
285
309
  VariableDeclaration,
286
310
  FunctionDeclaration,
311
+ ClassDeclaration,
287
312
  BlockStatement,
288
313
  IfStatement,
289
314
  WhileStatement,
@@ -314,5 +339,7 @@ module.exports = {
314
339
  MemberExpression,
315
340
  ArrowFunction,
316
341
  UpdateExpression,
317
- LogicalExpression
342
+ LogicalExpression,
343
+ ThisExpression,
344
+ NewExpression
318
345
  };
@@ -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,16 @@ class Parser {
438
504
  return new AST.StringLiteral(this.previous().value);
439
505
  }
440
506
 
507
+ if (this.match(TokenType.NUOVO)) {
508
+ return this.newExpression();
509
+ }
510
+
441
511
  if (this.match(TokenType.IDENTIFICATORE)) {
442
512
  return new AST.Identifier(this.previous().value);
443
513
  }
444
514
 
445
515
  if (this.match(TokenType.QUESTO)) {
446
- return new AST.Identifier('questo');
516
+ return new AST.ThisExpression();
447
517
  }
448
518
 
449
519
  if (this.match(TokenType.QUADRA_APERTA)) {