hispano-lang 1.1.7 → 2.1.0

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/dist/parser.js CHANGED
@@ -20,7 +20,7 @@ class Parser {
20
20
  statements.push(this.declaration());
21
21
 
22
22
  // Consume semicolon if present between statements
23
- if (this.match('SEMICOLON')) {
23
+ if (this.match("SEMICOLON")) {
24
24
  // Semicolon consumed
25
25
  }
26
26
  }
@@ -34,47 +34,67 @@ class Parser {
34
34
  */
35
35
  declaration() {
36
36
  try {
37
- if (this.match('VARIABLE')) {
37
+ if (this.match("VARIABLE")) {
38
38
  return this.variableDeclaration();
39
39
  }
40
40
 
41
- if (this.match('FUNCION')) {
41
+ if (this.match("CONSTANTE")) {
42
+ return this.constantDeclaration();
43
+ }
44
+
45
+ if (this.match("FUNCION")) {
42
46
  return this.functionDeclaration();
43
47
  }
44
48
 
45
- if (this.match('MOSTRAR')) {
49
+ if (this.match("CLASE")) {
50
+ return this.classDeclaration();
51
+ }
52
+
53
+ if (this.match("MOSTRAR")) {
46
54
  return this.mostrarStatement();
47
55
  }
48
56
 
49
- if (this.match('LEER')) {
57
+ if (this.match("LEER")) {
50
58
  return this.leerStatement();
51
59
  }
52
60
 
53
- if (this.match('SI')) {
61
+ if (this.match("SI")) {
54
62
  return this.ifStatement();
55
63
  }
56
64
 
57
- if (this.match('MIENTRAS')) {
65
+ if (this.match("MIENTRAS")) {
58
66
  return this.whileStatement();
59
67
  }
60
68
 
61
- if (this.match('PARA')) {
69
+ if (this.match("PARA")) {
70
+ // Check if it's "para cada" (for-each) or regular "para" (for)
71
+ if (this.match("CADA")) {
72
+ return this.forEachStatement();
73
+ }
62
74
  return this.forStatement();
63
75
  }
64
76
 
65
- if (this.match('RETORNAR')) {
77
+ if (this.match("ELEGIR")) {
78
+ return this.elegirStatement();
79
+ }
80
+
81
+ if (this.match("HACER")) {
82
+ return this.hacerMientrasStatement();
83
+ }
84
+
85
+ if (this.match("RETORNAR")) {
66
86
  return this.returnStatement();
67
87
  }
68
88
 
69
- if (this.match('ROMPER')) {
89
+ if (this.match("ROMPER")) {
70
90
  return this.breakStatement();
71
91
  }
72
92
 
73
- if (this.match('CONTINUAR')) {
93
+ if (this.match("CONTINUAR")) {
74
94
  return this.continueStatement();
75
95
  }
76
96
 
77
- if (this.match('INTENTAR')) {
97
+ if (this.match("INTENTAR")) {
78
98
  return this.tryStatement();
79
99
  }
80
100
 
@@ -91,21 +111,46 @@ class Parser {
91
111
  */
92
112
  variableDeclaration() {
93
113
  let name;
94
- if (this.match('IDENTIFIER')) {
114
+ if (this.match("IDENTIFIER")) {
95
115
  name = this.previous();
96
- } else if (this.match('AND')) {
116
+ } else if (this.match("AND")) {
97
117
  name = this.previous();
98
118
  } else {
99
- throw new Error('Se esperaba un nombre de variable');
119
+ throw new Error("Se esperaba un nombre de variable");
100
120
  }
101
121
 
102
122
  let initializer = null;
103
- if (this.match('EQUAL')) {
123
+ if (this.match("EQUAL")) {
104
124
  initializer = this.expression();
105
125
  }
106
126
 
107
127
  return {
108
- type: 'VariableDeclaration',
128
+ type: "VariableDeclaration",
129
+ name: name.lexeme,
130
+ initializer,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Parses a constant declaration
136
+ * @returns {Object} Constant declaration
137
+ */
138
+ constantDeclaration() {
139
+ let name;
140
+ if (this.match("IDENTIFIER")) {
141
+ name = this.previous();
142
+ } else {
143
+ throw new Error("Se esperaba un nombre de constante");
144
+ }
145
+
146
+ if (!this.match("EQUAL")) {
147
+ throw new Error("Las constantes deben ser inicializadas");
148
+ }
149
+
150
+ const initializer = this.expression();
151
+
152
+ return {
153
+ type: "ConstantDeclaration",
109
154
  name: name.lexeme,
110
155
  initializer,
111
156
  };
@@ -116,40 +161,143 @@ class Parser {
116
161
  * @returns {Object} Function declaration
117
162
  */
118
163
  functionDeclaration() {
119
- const name = this.consume('IDENTIFIER', 'Expected function name');
120
- this.consume('LEFT_PAREN', 'Expected ( after function name');
164
+ const name = this.consume("IDENTIFIER", "Expected function name");
165
+ this.consume("LEFT_PAREN", "Expected ( after function name");
121
166
 
122
167
  const parameters = [];
123
- if (!this.check('RIGHT_PAREN')) {
168
+ if (!this.check("RIGHT_PAREN")) {
124
169
  do {
125
170
  if (parameters.length >= 255) {
126
- throw new Error('No se pueden tener más de 255 parámetros');
171
+ throw new Error("No se pueden tener más de 255 parámetros");
127
172
  }
128
173
  let param;
129
- if (this.match('IDENTIFIER')) {
174
+ if (this.match("IDENTIFIER")) {
130
175
  param = this.previous();
131
- } else if (this.match('AND')) {
176
+ } else if (this.match("AND")) {
132
177
  param = this.previous();
133
178
  } else {
134
- throw new Error('Se esperaba un nombre de parámetro');
179
+ throw new Error("Se esperaba un nombre de parámetro");
135
180
  }
136
181
  parameters.push(param.lexeme);
137
- } while (this.match('COMMA'));
182
+ } while (this.match("COMMA"));
138
183
  }
139
184
 
140
- this.consume('RIGHT_PAREN', 'Expected ) after parameters');
141
- this.consume('LEFT_BRACE', 'Expected { before function body');
185
+ this.consume("RIGHT_PAREN", "Expected ) after parameters");
186
+ this.consume("LEFT_BRACE", "Expected { before function body");
142
187
  const body = this.block();
143
- this.consume('RIGHT_BRACE', 'Expected } after function body');
188
+ this.consume("RIGHT_BRACE", "Expected } after function body");
144
189
 
145
190
  return {
146
- type: 'FunctionDeclaration',
191
+ type: "FunctionDeclaration",
147
192
  name: name.lexeme,
148
193
  parameters,
149
194
  body,
150
195
  };
151
196
  }
152
197
 
198
+ /**
199
+ * Parses a class declaration
200
+ * @returns {Object} Class declaration
201
+ */
202
+ classDeclaration() {
203
+ const name = this.consume("IDENTIFIER", "Se esperaba un nombre de clase");
204
+
205
+ // Check for inheritance
206
+ let superclass = null;
207
+ if (this.match("EXTIENDE")) {
208
+ this.consume("IDENTIFIER", "Se esperaba el nombre de la clase padre");
209
+ superclass = this.previous().lexeme;
210
+ }
211
+
212
+ this.consume("LEFT_BRACE", "Se esperaba { después del nombre de clase");
213
+
214
+ // Parse class body (constructor and methods)
215
+ let constructor = null;
216
+ const methods = [];
217
+
218
+ while (!this.check("RIGHT_BRACE") && !this.isAtEnd()) {
219
+ if (this.match("CONSTRUCTOR")) {
220
+ // Parse constructor
221
+ this.consume("LEFT_PAREN", "Se esperaba ( después de constructor");
222
+ const parameters = [];
223
+ if (!this.check("RIGHT_PAREN")) {
224
+ do {
225
+ if (parameters.length >= 255) {
226
+ throw new Error("No se pueden tener más de 255 parámetros");
227
+ }
228
+ const param = this.consume(
229
+ "IDENTIFIER",
230
+ "Se esperaba nombre de parámetro",
231
+ );
232
+ parameters.push(param.lexeme);
233
+ } while (this.match("COMMA"));
234
+ }
235
+ this.consume("RIGHT_PAREN", "Se esperaba ) después de parámetros");
236
+ this.consume(
237
+ "LEFT_BRACE",
238
+ "Se esperaba { antes del cuerpo del constructor",
239
+ );
240
+ const body = this.block();
241
+ this.consume(
242
+ "RIGHT_BRACE",
243
+ "Se esperaba } después del cuerpo del constructor",
244
+ );
245
+
246
+ constructor = {
247
+ parameters,
248
+ body,
249
+ };
250
+ } else if (this.match("IDENTIFIER")) {
251
+ // Parse method
252
+ const methodName = this.previous().lexeme;
253
+ this.consume(
254
+ "LEFT_PAREN",
255
+ "Se esperaba ( después del nombre del método",
256
+ );
257
+ const parameters = [];
258
+ if (!this.check("RIGHT_PAREN")) {
259
+ do {
260
+ if (parameters.length >= 255) {
261
+ throw new Error("No se pueden tener más de 255 parámetros");
262
+ }
263
+ const param = this.consume(
264
+ "IDENTIFIER",
265
+ "Se esperaba nombre de parámetro",
266
+ );
267
+ parameters.push(param.lexeme);
268
+ } while (this.match("COMMA"));
269
+ }
270
+ this.consume("RIGHT_PAREN", "Se esperaba ) después de parámetros");
271
+ this.consume("LEFT_BRACE", "Se esperaba { antes del cuerpo del método");
272
+ const body = this.block();
273
+ this.consume(
274
+ "RIGHT_BRACE",
275
+ "Se esperaba } después del cuerpo del método",
276
+ );
277
+
278
+ methods.push({
279
+ name: methodName,
280
+ parameters,
281
+ body,
282
+ });
283
+ } else {
284
+ throw new Error(
285
+ "Se esperaba constructor o método en el cuerpo de la clase",
286
+ );
287
+ }
288
+ }
289
+
290
+ this.consume("RIGHT_BRACE", "Se esperaba } después del cuerpo de la clase");
291
+
292
+ return {
293
+ type: "ClassDeclaration",
294
+ name: name.lexeme,
295
+ superclass,
296
+ constructor,
297
+ methods,
298
+ };
299
+ }
300
+
153
301
  /**
154
302
  * Parses a show statement
155
303
  * @returns {Object} Show statement
@@ -158,7 +306,7 @@ class Parser {
158
306
  const value = this.expression();
159
307
 
160
308
  return {
161
- type: 'MostrarStatement',
309
+ type: "MostrarStatement",
162
310
  expression: value,
163
311
  };
164
312
  }
@@ -170,18 +318,18 @@ class Parser {
170
318
  leerStatement() {
171
319
  // Parse the variable name to store the input
172
320
  const variableName = this.consume(
173
- 'IDENTIFIER',
174
- 'Expected variable name after leer'
321
+ "IDENTIFIER",
322
+ "Expected variable name after leer",
175
323
  );
176
324
 
177
325
  // Optional prompt message
178
326
  let prompt = null;
179
- if (this.match('STRING')) {
327
+ if (this.match("STRING")) {
180
328
  prompt = this.previous().literal;
181
329
  }
182
330
 
183
331
  return {
184
- type: 'LeerStatement',
332
+ type: "LeerStatement",
185
333
  variable: variableName.lexeme,
186
334
  prompt: prompt,
187
335
  };
@@ -193,19 +341,19 @@ class Parser {
193
341
  */
194
342
  ifStatement() {
195
343
  const condition = this.expression();
196
- this.consume('LEFT_BRACE', 'Expected { after condition');
344
+ this.consume("LEFT_BRACE", "Expected { after condition");
197
345
  const thenBranch = this.block();
198
- this.consume('RIGHT_BRACE', 'Expected } after block');
346
+ this.consume("RIGHT_BRACE", "Expected } after block");
199
347
  let elseBranch = null;
200
348
 
201
- if (this.match('SINO')) {
202
- this.consume('LEFT_BRACE', 'Expected { after else');
349
+ if (this.match("SINO")) {
350
+ this.consume("LEFT_BRACE", "Expected { after else");
203
351
  elseBranch = this.block();
204
- this.consume('RIGHT_BRACE', 'Expected } after else block');
352
+ this.consume("RIGHT_BRACE", "Expected } after else block");
205
353
  }
206
354
 
207
355
  return {
208
- type: 'IfStatement',
356
+ type: "IfStatement",
209
357
  condition,
210
358
  thenBranch,
211
359
  elseBranch,
@@ -218,12 +366,12 @@ class Parser {
218
366
  */
219
367
  whileStatement() {
220
368
  const condition = this.expression();
221
- this.consume('LEFT_BRACE', 'Expected { after condition');
369
+ this.consume("LEFT_BRACE", "Expected { after condition");
222
370
  const body = this.block();
223
- this.consume('RIGHT_BRACE', 'Expected } after block');
371
+ this.consume("RIGHT_BRACE", "Expected } after block");
224
372
 
225
373
  return {
226
- type: 'WhileStatement',
374
+ type: "WhileStatement",
227
375
  condition,
228
376
  body,
229
377
  };
@@ -234,18 +382,18 @@ class Parser {
234
382
  * @returns {Object} For statement
235
383
  */
236
384
  forStatement() {
237
- this.consume('LEFT_PAREN', 'Expected ( after para');
385
+ this.consume("LEFT_PAREN", "Expected ( after para");
238
386
 
239
387
  // Initializer (optional)
240
388
  let initializer = null;
241
- if (!this.check('SEMICOLON')) {
242
- if (this.match('VARIABLE')) {
389
+ if (!this.check("SEMICOLON")) {
390
+ if (this.match("VARIABLE")) {
243
391
  initializer = this.variableDeclaration();
244
392
  } else {
245
393
  initializer = this.expressionStatement();
246
394
  }
247
395
  // Consume the semicolon after initializer
248
- this.consume('SEMICOLON', 'Expected ; after for initializer');
396
+ this.consume("SEMICOLON", "Expected ; after for initializer");
249
397
  } else {
250
398
  // Skip the first semicolon if no initializer
251
399
  this.advance();
@@ -253,24 +401,24 @@ class Parser {
253
401
 
254
402
  // Condition (optional)
255
403
  let condition = null;
256
- if (!this.check('SEMICOLON')) {
404
+ if (!this.check("SEMICOLON")) {
257
405
  condition = this.expression();
258
406
  }
259
- this.consume('SEMICOLON', 'Expected ; after condition');
407
+ this.consume("SEMICOLON", "Expected ; after condition");
260
408
 
261
409
  // Increment (optional)
262
410
  let increment = null;
263
- if (!this.check('RIGHT_PAREN')) {
411
+ if (!this.check("RIGHT_PAREN")) {
264
412
  increment = this.expression();
265
413
  }
266
- this.consume('RIGHT_PAREN', 'Expected ) after for clause');
414
+ this.consume("RIGHT_PAREN", "Expected ) after for clause");
267
415
 
268
- this.consume('LEFT_BRACE', 'Expected { after for clause');
416
+ this.consume("LEFT_BRACE", "Expected { after for clause");
269
417
  const body = this.block();
270
- this.consume('RIGHT_BRACE', 'Expected } after block');
418
+ this.consume("RIGHT_BRACE", "Expected } after block");
271
419
 
272
420
  return {
273
- type: 'ForStatement',
421
+ type: "ForStatement",
274
422
  initializer,
275
423
  condition,
276
424
  increment,
@@ -285,12 +433,12 @@ class Parser {
285
433
  returnStatement() {
286
434
  const keyword = this.previous();
287
435
  let value = null;
288
- if (!this.check('RIGHT_BRACE')) {
436
+ if (!this.check("RIGHT_BRACE")) {
289
437
  value = this.expression();
290
438
  }
291
439
 
292
440
  return {
293
- type: 'ReturnStatement',
441
+ type: "ReturnStatement",
294
442
  keyword,
295
443
  value,
296
444
  };
@@ -303,7 +451,7 @@ class Parser {
303
451
  breakStatement() {
304
452
  const keyword = this.previous();
305
453
  return {
306
- type: 'BreakStatement',
454
+ type: "BreakStatement",
307
455
  keyword,
308
456
  };
309
457
  }
@@ -315,7 +463,7 @@ class Parser {
315
463
  continueStatement() {
316
464
  const keyword = this.previous();
317
465
  return {
318
- type: 'ContinueStatement',
466
+ type: "ContinueStatement",
319
467
  keyword,
320
468
  };
321
469
  }
@@ -327,7 +475,7 @@ class Parser {
327
475
  block() {
328
476
  const statements = [];
329
477
 
330
- while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
478
+ while (!this.check("RIGHT_BRACE") && !this.isAtEnd()) {
331
479
  statements.push(this.declaration());
332
480
  }
333
481
 
@@ -341,7 +489,7 @@ class Parser {
341
489
  expressionStatement() {
342
490
  const expr = this.expression();
343
491
  return {
344
- type: 'ExpressionStatement',
492
+ type: "ExpressionStatement",
345
493
  expression: expr,
346
494
  };
347
495
  }
@@ -361,7 +509,7 @@ class Parser {
361
509
  parseExpression() {
362
510
  let expr = this.equality();
363
511
 
364
- while (this.match('LEFT_BRACKET')) {
512
+ while (this.match("LEFT_BRACKET")) {
365
513
  expr = this.finishArrayAccess(expr);
366
514
  }
367
515
 
@@ -373,68 +521,76 @@ class Parser {
373
521
  * @returns {Object} Parsed assignment
374
522
  */
375
523
  assignment() {
376
- let expr = this.logicalOr();
524
+ const expr = this.logicalOr();
377
525
 
378
- if (this.match('EQUAL')) {
526
+ if (this.match("EQUAL")) {
379
527
  const equals = this.previous();
380
528
  const value = this.logicalOr();
381
529
 
382
- if (expr.type === 'Variable') {
530
+ if (expr.type === "Variable") {
383
531
  const name = expr.name;
384
532
  return {
385
- type: 'Assign',
533
+ type: "Assign",
386
534
  name,
387
535
  value,
388
536
  };
389
537
  }
390
538
 
391
- if (expr.type === 'ArrayAccess') {
539
+ if (expr.type === "ArrayAccess") {
392
540
  return {
393
- type: 'ArrayAssign',
541
+ type: "ArrayAssign",
394
542
  array: expr.array,
395
543
  index: expr.index,
396
544
  value,
397
545
  };
398
546
  }
399
547
 
400
- if (expr.type === 'PropertyAccess') {
548
+ if (expr.type === "PropertyAccess") {
401
549
  return {
402
- type: 'PropertyAssign',
550
+ type: "PropertyAssign",
403
551
  object: expr.object,
404
552
  name: expr.name,
405
553
  value,
406
554
  };
407
555
  }
408
556
 
409
- throw new Error('Objetivo de asignación inválido');
557
+ if (expr.type === "ThisPropertyAccess") {
558
+ return {
559
+ type: "ThisPropertyAssign",
560
+ property: expr.property,
561
+ value,
562
+ };
563
+ }
564
+
565
+ throw new Error("Objetivo de asignación inválido");
410
566
  }
411
567
 
412
568
  // Handle compound assignment operators
413
569
  if (
414
570
  this.match(
415
- 'PLUS_EQUAL',
416
- 'MINUS_EQUAL',
417
- 'STAR_EQUAL',
418
- 'SLASH_EQUAL',
419
- 'PERCENT_EQUAL'
571
+ "PLUS_EQUAL",
572
+ "MINUS_EQUAL",
573
+ "STAR_EQUAL",
574
+ "SLASH_EQUAL",
575
+ "PERCENT_EQUAL",
420
576
  )
421
577
  ) {
422
578
  const operator = this.previous();
423
579
  const value = this.logicalOr();
424
580
 
425
- if (expr.type === 'Variable') {
581
+ if (expr.type === "Variable") {
426
582
  const name = expr.name;
427
583
  return {
428
- type: 'CompoundAssign',
584
+ type: "CompoundAssign",
429
585
  name,
430
586
  operator: operator.type,
431
587
  value,
432
588
  };
433
589
  }
434
590
 
435
- if (expr.type === 'ArrayAccess') {
591
+ if (expr.type === "ArrayAccess") {
436
592
  return {
437
- type: 'CompoundArrayAssign',
593
+ type: "CompoundArrayAssign",
438
594
  array: expr.array,
439
595
  index: expr.index,
440
596
  operator: operator.type,
@@ -442,9 +598,9 @@ class Parser {
442
598
  };
443
599
  }
444
600
 
445
- if (expr.type === 'PropertyAccess') {
601
+ if (expr.type === "PropertyAccess") {
446
602
  return {
447
- type: 'CompoundPropertyAssign',
603
+ type: "CompoundPropertyAssign",
448
604
  object: expr.object,
449
605
  name: expr.name,
450
606
  operator: operator.type,
@@ -452,7 +608,7 @@ class Parser {
452
608
  };
453
609
  }
454
610
 
455
- throw new Error('Objetivo de asignación compuesta inválido');
611
+ throw new Error("Objetivo de asignación compuesta inválido");
456
612
  }
457
613
 
458
614
  return expr;
@@ -465,11 +621,11 @@ class Parser {
465
621
  equality() {
466
622
  let expr = this.comparison();
467
623
 
468
- while (this.match('EQUAL_EQUAL', 'BANG_EQUAL')) {
624
+ while (this.match("EQUAL_EQUAL", "BANG_EQUAL")) {
469
625
  const operator = this.previous();
470
626
  const right = this.comparison();
471
627
  expr = {
472
- type: 'Binary',
628
+ type: "Binary",
473
629
  left: expr,
474
630
  operator: operator.type,
475
631
  right,
@@ -486,11 +642,11 @@ class Parser {
486
642
  logicalAnd() {
487
643
  let expr = this.equality();
488
644
 
489
- while (this.match('AND')) {
645
+ while (this.match("AND")) {
490
646
  const operator = this.previous();
491
647
  const right = this.equality();
492
648
  expr = {
493
- type: 'Logical',
649
+ type: "Logical",
494
650
  left: expr,
495
651
  operator: operator.type,
496
652
  right,
@@ -507,11 +663,11 @@ class Parser {
507
663
  logicalOr() {
508
664
  let expr = this.logicalAnd();
509
665
 
510
- while (this.match('OR')) {
666
+ while (this.match("OR")) {
511
667
  const operator = this.previous();
512
668
  const right = this.logicalAnd();
513
669
  expr = {
514
- type: 'Logical',
670
+ type: "Logical",
515
671
  left: expr,
516
672
  operator: operator.type,
517
673
  right,
@@ -528,11 +684,11 @@ class Parser {
528
684
  comparison() {
529
685
  let expr = this.term();
530
686
 
531
- while (this.match('GREATER', 'GREATER_EQUAL', 'LESS', 'LESS_EQUAL')) {
687
+ while (this.match("GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL")) {
532
688
  const operator = this.previous();
533
689
  const right = this.term();
534
690
  expr = {
535
- type: 'Binary',
691
+ type: "Binary",
536
692
  left: expr,
537
693
  operator: operator.type,
538
694
  right,
@@ -549,11 +705,11 @@ class Parser {
549
705
  term() {
550
706
  let expr = this.factor();
551
707
 
552
- while (this.match('MINUS', 'PLUS')) {
708
+ while (this.match("MINUS", "PLUS")) {
553
709
  const operator = this.previous();
554
710
  const right = this.factor();
555
711
  expr = {
556
- type: 'Binary',
712
+ type: "Binary",
557
713
  left: expr,
558
714
  operator: operator.type,
559
715
  right,
@@ -570,11 +726,11 @@ class Parser {
570
726
  factor() {
571
727
  let expr = this.unary();
572
728
 
573
- while (this.match('SLASH', 'STAR', 'PERCENT')) {
729
+ while (this.match("SLASH", "STAR", "PERCENT")) {
574
730
  const operator = this.previous();
575
731
  const right = this.unary();
576
732
  expr = {
577
- type: 'Binary',
733
+ type: "Binary",
578
734
  left: expr,
579
735
  operator: operator.type,
580
736
  right,
@@ -589,11 +745,11 @@ class Parser {
589
745
  * @returns {Object} Unary expression
590
746
  */
591
747
  unary() {
592
- if (this.match('BANG', 'MINUS')) {
748
+ if (this.match("BANG", "MINUS")) {
593
749
  const operator = this.previous();
594
750
  const right = this.unary();
595
751
  return {
596
- type: 'Unary',
752
+ type: "Unary",
597
753
  operator: operator.type,
598
754
  right,
599
755
  };
@@ -609,10 +765,10 @@ class Parser {
609
765
  postfix() {
610
766
  let expr = this.call();
611
767
 
612
- while (this.match('PLUS_PLUS', 'MINUS_MINUS')) {
768
+ while (this.match("PLUS_PLUS", "MINUS_MINUS")) {
613
769
  const operator = this.previous();
614
770
  expr = {
615
- type: 'Postfix',
771
+ type: "Postfix",
616
772
  operator: operator.type,
617
773
  operand: expr,
618
774
  };
@@ -629,10 +785,44 @@ class Parser {
629
785
  let expr = this.primary();
630
786
 
631
787
  while (true) {
632
- if (this.match('LEFT_BRACKET')) {
788
+ if (this.match("LEFT_BRACKET")) {
633
789
  expr = this.finishArrayAccess(expr);
634
- } else if (this.match('DOT')) {
790
+ } else if (this.match("DOT")) {
635
791
  expr = this.finishPropertyAccess(expr);
792
+ } else if (this.match("LEFT_PAREN")) {
793
+ // Handle method/function calls on PropertyAccess or ThisPropertyAccess
794
+ if (expr.type === "PropertyAccess") {
795
+ const args = [];
796
+ if (!this.check("RIGHT_PAREN")) {
797
+ do {
798
+ args.push(this.expression());
799
+ } while (this.match("COMMA"));
800
+ }
801
+ this.consume("RIGHT_PAREN", "Expected ) after method arguments");
802
+ expr = {
803
+ type: "MethodCall",
804
+ object: expr.object,
805
+ method: expr.name,
806
+ arguments: args,
807
+ };
808
+ } else if (expr.type === "ThisPropertyAccess") {
809
+ const args = [];
810
+ if (!this.check("RIGHT_PAREN")) {
811
+ do {
812
+ args.push(this.expression());
813
+ } while (this.match("COMMA"));
814
+ }
815
+ this.consume("RIGHT_PAREN", "Expected ) after method arguments");
816
+ expr = {
817
+ type: "ThisMethodCall",
818
+ method: expr.property,
819
+ arguments: args,
820
+ };
821
+ } else {
822
+ // Put back the LEFT_PAREN for other handlers
823
+ this.current--;
824
+ break;
825
+ }
636
826
  } else {
637
827
  break;
638
828
  }
@@ -646,60 +836,182 @@ class Parser {
646
836
  * @returns {Object} Primary expression
647
837
  */
648
838
  primary() {
649
- if (this.match('FALSE')) return { type: 'Literal', value: false };
650
- if (this.match('TRUE')) return { type: 'Literal', value: true };
651
- if (this.match('NULL')) return { type: 'Literal', value: null };
652
- if (this.match('UNDEFINED')) return { type: 'Literal', value: undefined };
653
- if (this.match('NUMBER', 'STRING')) {
839
+ if (this.match("FALSE")) return { type: "Literal", value: false };
840
+ if (this.match("TRUE")) return { type: "Literal", value: true };
841
+ if (this.match("NULL")) return { type: "Literal", value: null };
842
+ if (this.match("UNDEFINED")) return { type: "Literal", value: undefined };
843
+ if (this.match("NUMBER", "STRING")) {
654
844
  return {
655
- type: 'Literal',
845
+ type: "Literal",
656
846
  value: this.previous().literal,
657
847
  };
658
848
  }
659
849
 
660
- if (this.match('IDENTIFIER')) {
850
+ if (this.match("TEMPLATE_STRING")) {
851
+ return this.templateStringExpression();
852
+ }
853
+
854
+ if (this.match("IDENTIFIER")) {
661
855
  const identifier = this.previous();
662
- if (this.check('LEFT_PAREN')) {
856
+ // Check for single-param arrow function: x => ...
857
+ if (this.check("ARROW")) {
858
+ this.advance(); // Consume the ARROW
859
+ return this.arrowFunctionBody([identifier.lexeme]);
860
+ }
861
+ if (this.check("LEFT_PAREN")) {
663
862
  this.advance(); // Consume the LEFT_PAREN
664
863
  return this.finishCall(identifier);
665
864
  }
666
865
  return {
667
- type: 'Variable',
866
+ type: "Variable",
668
867
  name: identifier.lexeme,
669
868
  };
670
869
  }
671
870
 
672
- if (this.match('AND')) {
871
+ if (this.match("AND")) {
673
872
  const identifier = this.previous();
674
- if (this.check('LEFT_PAREN')) {
873
+ if (this.check("LEFT_PAREN")) {
675
874
  this.advance(); // Consume the LEFT_PAREN
676
875
  return this.finishCall(identifier);
677
876
  }
678
877
  return {
679
- type: 'Variable',
878
+ type: "Variable",
680
879
  name: identifier.lexeme,
681
880
  };
682
881
  }
683
882
 
684
- if (this.match('LEFT_PAREN')) {
883
+ if (this.match("LEFT_PAREN")) {
884
+ // Check if this could be arrow function params
885
+ if (this.isArrowFunctionParams()) {
886
+ return this.arrowFunctionWithParams();
887
+ }
685
888
  const expr = this.expression();
686
- this.consume('RIGHT_PAREN', 'Expected ) after expression');
889
+ this.consume("RIGHT_PAREN", "Expected ) after expression");
687
890
  return expr;
688
891
  }
689
892
 
690
- if (this.match('LEFT_BRACKET')) {
893
+ if (this.match("LEFT_BRACKET")) {
691
894
  return this.arrayLiteral();
692
895
  }
693
896
 
694
- if (this.match('LEFT_BRACE')) {
897
+ if (this.match("LEFT_BRACE")) {
695
898
  return this.objectLiteral();
696
899
  }
697
900
 
698
- if (this.match('FUNCION')) {
901
+ if (this.match("FUNCION")) {
699
902
  return this.anonymousFunction();
700
903
  }
701
904
 
702
- throw new Error('Se esperaba una expresión');
905
+ if (this.match("NUEVO")) {
906
+ return this.newExpression();
907
+ }
908
+
909
+ if (this.match("ESTE")) {
910
+ return this.thisExpression();
911
+ }
912
+
913
+ if (this.match("SUPER")) {
914
+ return this.superExpression();
915
+ }
916
+
917
+ throw new Error("Se esperaba una expresión");
918
+ }
919
+
920
+ /**
921
+ * Parses a new expression for class instantiation
922
+ * @returns {Object} New expression
923
+ */
924
+ newExpression() {
925
+ const className = this.consume(
926
+ "IDENTIFIER",
927
+ "Se esperaba el nombre de la clase después de 'nuevo'",
928
+ );
929
+ this.consume("LEFT_PAREN", "Se esperaba ( después del nombre de la clase");
930
+
931
+ const args = [];
932
+ if (!this.check("RIGHT_PAREN")) {
933
+ do {
934
+ args.push(this.expression());
935
+ } while (this.match("COMMA"));
936
+ }
937
+
938
+ this.consume("RIGHT_PAREN", "Se esperaba ) después de los argumentos");
939
+
940
+ return {
941
+ type: "NewExpression",
942
+ className: className.lexeme,
943
+ arguments: args,
944
+ };
945
+ }
946
+
947
+ /**
948
+ * Parses 'este' (this) expression
949
+ * @returns {Object} This expression
950
+ */
951
+ thisExpression() {
952
+ // Check if accessing a property
953
+ if (this.match("DOT")) {
954
+ const property = this.consume(
955
+ "IDENTIFIER",
956
+ "Se esperaba el nombre de la propiedad después de 'este.'",
957
+ );
958
+ return {
959
+ type: "ThisPropertyAccess",
960
+ property: property.lexeme,
961
+ };
962
+ }
963
+ return {
964
+ type: "This",
965
+ };
966
+ }
967
+
968
+ /**
969
+ * Parses 'super' expression for parent class calls
970
+ * @returns {Object} Super expression
971
+ */
972
+ superExpression() {
973
+ this.consume("LEFT_PAREN", "Se esperaba ( después de 'super'");
974
+
975
+ const args = [];
976
+ if (!this.check("RIGHT_PAREN")) {
977
+ do {
978
+ args.push(this.expression());
979
+ } while (this.match("COMMA"));
980
+ }
981
+
982
+ this.consume(
983
+ "RIGHT_PAREN",
984
+ "Se esperaba ) después de los argumentos de super",
985
+ );
986
+
987
+ return {
988
+ type: "SuperCall",
989
+ arguments: args,
990
+ };
991
+ }
992
+
993
+ /**
994
+ * Parses a template string expression
995
+ * @returns {Object} Template string expression with parsed expressions
996
+ */
997
+ templateStringExpression() {
998
+ const token = this.previous();
999
+ const { parts, expressions } = token.literal;
1000
+ const Tokenizer = require("./tokenizer.js");
1001
+
1002
+ // Parse each expression string into an AST
1003
+ const parsedExpressions = expressions.map((exprSource) => {
1004
+ const tokenizer = new Tokenizer();
1005
+ const exprTokens = tokenizer.tokenize(exprSource);
1006
+ const exprParser = new Parser(exprTokens);
1007
+ return exprParser.expression();
1008
+ });
1009
+
1010
+ return {
1011
+ type: "TemplateString",
1012
+ parts,
1013
+ expressions: parsedExpressions,
1014
+ };
703
1015
  }
704
1016
 
705
1017
  /**
@@ -707,38 +1019,141 @@ class Parser {
707
1019
  * @returns {Object} Anonymous function expression
708
1020
  */
709
1021
  anonymousFunction() {
710
- this.consume('LEFT_PAREN', 'Expected ( after funcion');
1022
+ this.consume("LEFT_PAREN", "Expected ( after funcion");
711
1023
 
712
1024
  const parameters = [];
713
- if (!this.check('RIGHT_PAREN')) {
1025
+ if (!this.check("RIGHT_PAREN")) {
714
1026
  do {
715
1027
  if (parameters.length >= 255) {
716
- throw new Error('No se pueden tener más de 255 parámetros');
1028
+ throw new Error("No se pueden tener más de 255 parámetros");
717
1029
  }
718
1030
  let param;
719
- if (this.match('IDENTIFIER')) {
1031
+ if (this.match("IDENTIFIER")) {
720
1032
  param = this.previous();
721
- } else if (this.match('AND')) {
1033
+ } else if (this.match("AND")) {
722
1034
  param = this.previous();
723
1035
  } else {
724
- throw new Error('Se esperaba un nombre de parámetro');
1036
+ throw new Error("Se esperaba un nombre de parámetro");
725
1037
  }
726
1038
  parameters.push(param.lexeme);
727
- } while (this.match('COMMA'));
1039
+ } while (this.match("COMMA"));
728
1040
  }
729
1041
 
730
- this.consume('RIGHT_PAREN', 'Expected ) after parameters');
731
- this.consume('LEFT_BRACE', 'Expected { before function body');
1042
+ this.consume("RIGHT_PAREN", "Expected ) after parameters");
1043
+ this.consume("LEFT_BRACE", "Expected { before function body");
732
1044
  const body = this.block();
733
- this.consume('RIGHT_BRACE', 'Expected } after function body');
1045
+ this.consume("RIGHT_BRACE", "Expected } after function body");
734
1046
 
735
1047
  return {
736
- type: 'AnonymousFunction',
1048
+ type: "AnonymousFunction",
737
1049
  parameters,
738
1050
  body,
739
1051
  };
740
1052
  }
741
1053
 
1054
+ /**
1055
+ * Checks if current position looks like arrow function parameters
1056
+ * Looks ahead to find matching ) and checks for =>
1057
+ * @returns {boolean} True if this looks like arrow function params
1058
+ */
1059
+ isArrowFunctionParams() {
1060
+ // Save current position
1061
+ const startPos = this.current;
1062
+
1063
+ // Empty params: () =>
1064
+ if (this.check("RIGHT_PAREN")) {
1065
+ // Check if next is =>
1066
+ if (
1067
+ this.tokens[this.current + 1] &&
1068
+ this.tokens[this.current + 1].type === "ARROW"
1069
+ ) {
1070
+ return true;
1071
+ }
1072
+ return false;
1073
+ }
1074
+
1075
+ // Try to match params pattern: identifier (comma identifier)*
1076
+ let parenDepth = 1;
1077
+ let pos = this.current;
1078
+
1079
+ while (pos < this.tokens.length && parenDepth > 0) {
1080
+ const token = this.tokens[pos];
1081
+ if (token.type === "LEFT_PAREN") {
1082
+ parenDepth++;
1083
+ } else if (token.type === "RIGHT_PAREN") {
1084
+ parenDepth--;
1085
+ }
1086
+ pos++;
1087
+ }
1088
+
1089
+ // pos is now after the matching )
1090
+ // Check if next token is =>
1091
+ if (pos < this.tokens.length && this.tokens[pos].type === "ARROW") {
1092
+ return true;
1093
+ }
1094
+
1095
+ return false;
1096
+ }
1097
+
1098
+ /**
1099
+ * Parses arrow function with parenthesized parameters
1100
+ * Called after LEFT_PAREN has been consumed
1101
+ * @returns {Object} Arrow function expression
1102
+ */
1103
+ arrowFunctionWithParams() {
1104
+ const parameters = [];
1105
+
1106
+ // Parse parameters
1107
+ if (!this.check("RIGHT_PAREN")) {
1108
+ do {
1109
+ if (parameters.length >= 255) {
1110
+ throw new Error("No se pueden tener más de 255 parámetros");
1111
+ }
1112
+ const param = this.consume(
1113
+ "IDENTIFIER",
1114
+ "Se esperaba un nombre de parámetro",
1115
+ );
1116
+ parameters.push(param.lexeme);
1117
+ } while (this.match("COMMA"));
1118
+ }
1119
+
1120
+ this.consume("RIGHT_PAREN", "Se esperaba ) después de los parámetros");
1121
+ this.consume("ARROW", "Se esperaba => después de los parámetros");
1122
+
1123
+ return this.arrowFunctionBody(parameters);
1124
+ }
1125
+
1126
+ /**
1127
+ * Parses arrow function body (expression or block)
1128
+ * @param {Array} parameters - Function parameters
1129
+ * @returns {Object} Arrow function expression
1130
+ */
1131
+ arrowFunctionBody(parameters) {
1132
+ // Check if body is a block
1133
+ if (this.match("LEFT_BRACE")) {
1134
+ const body = this.block();
1135
+ this.consume(
1136
+ "RIGHT_BRACE",
1137
+ "Se esperaba } después del cuerpo de la función",
1138
+ );
1139
+ return {
1140
+ type: "ArrowFunction",
1141
+ parameters,
1142
+ body,
1143
+ isExpression: false,
1144
+ };
1145
+ }
1146
+
1147
+ // Body is a single expression (implicit return)
1148
+ const expression = this.expression();
1149
+ return {
1150
+ type: "ArrowFunction",
1151
+ parameters,
1152
+ body: expression,
1153
+ isExpression: true,
1154
+ };
1155
+ }
1156
+
742
1157
  /**
743
1158
  * Finishes parsing an array access
744
1159
  * @param {Object} array - The array being accessed
@@ -746,10 +1161,10 @@ class Parser {
746
1161
  */
747
1162
  finishArrayAccess(array) {
748
1163
  const index = this.expression();
749
- this.consume('RIGHT_BRACKET', 'Expected ] after array index');
1164
+ this.consume("RIGHT_BRACKET", "Expected ] after array index");
750
1165
 
751
1166
  return {
752
- type: 'ArrayAccess',
1167
+ type: "ArrayAccess",
753
1168
  array,
754
1169
  index,
755
1170
  };
@@ -761,63 +1176,101 @@ class Parser {
761
1176
  * @returns {Object} Property access expression
762
1177
  */
763
1178
  finishPropertyAccess(object) {
764
- this.consume('IDENTIFIER', 'Expected property name after .');
1179
+ this.consume("IDENTIFIER", "Expected property name after .");
765
1180
  const name = this.previous();
766
1181
 
767
- // Check if this is a method call (array or string)
1182
+ // Check if this is a method call (array, string, or number)
768
1183
  if (
769
- name.lexeme === 'longitud' ||
770
- name.lexeme === 'primero' ||
771
- name.lexeme === 'ultimo' ||
772
- name.lexeme === 'agregar' ||
773
- name.lexeme === 'remover' ||
774
- name.lexeme === 'contiene' ||
775
- name.lexeme === 'recorrer' ||
776
- name.lexeme === 'mayusculas' ||
777
- name.lexeme === 'minusculas'
1184
+ name.lexeme === "longitud" ||
1185
+ name.lexeme === "primero" ||
1186
+ name.lexeme === "ultimo" ||
1187
+ name.lexeme === "agregar" ||
1188
+ name.lexeme === "remover" ||
1189
+ name.lexeme === "contiene" ||
1190
+ name.lexeme === "recorrer" ||
1191
+ name.lexeme === "mayusculas" ||
1192
+ name.lexeme === "minusculas" ||
1193
+ name.lexeme === "dividir" ||
1194
+ name.lexeme === "reemplazar" ||
1195
+ name.lexeme === "recortar" ||
1196
+ name.lexeme === "incluye" ||
1197
+ name.lexeme === "empiezaCon" ||
1198
+ name.lexeme === "terminaCon" ||
1199
+ name.lexeme === "caracter" ||
1200
+ name.lexeme === "subcadena" ||
1201
+ name.lexeme === "invertir" ||
1202
+ name.lexeme === "filtrar" ||
1203
+ name.lexeme === "mapear" ||
1204
+ name.lexeme === "reducir" ||
1205
+ name.lexeme === "ordenar" ||
1206
+ name.lexeme === "buscar" ||
1207
+ name.lexeme === "algunos" ||
1208
+ name.lexeme === "todos" ||
1209
+ name.lexeme === "unir" ||
1210
+ name.lexeme === "cortar" ||
1211
+ name.lexeme === "insertar" ||
1212
+ name.lexeme === "esPar" ||
1213
+ name.lexeme === "esImpar" ||
1214
+ name.lexeme === "esPositivo" ||
1215
+ name.lexeme === "esNegativo" ||
1216
+ name.lexeme === "aTexto"
778
1217
  ) {
779
1218
  // Check if there are parentheses (method call syntax)
780
- if (this.match('LEFT_PAREN')) {
1219
+ if (this.match("LEFT_PAREN")) {
781
1220
  // Consume the opening parenthesis
782
1221
  // Check if there are arguments
783
- if (!this.check('RIGHT_PAREN')) {
784
- // Handle methods that accept arguments (like agregar, contiene, recorrer)
1222
+ if (!this.check("RIGHT_PAREN")) {
1223
+ // Handle methods that accept arguments
785
1224
  if (
786
- name.lexeme === 'agregar' ||
787
- name.lexeme === 'contiene' ||
788
- name.lexeme === 'recorrer'
1225
+ name.lexeme === "agregar" ||
1226
+ name.lexeme === "contiene" ||
1227
+ name.lexeme === "recorrer" ||
1228
+ name.lexeme === "dividir" ||
1229
+ name.lexeme === "reemplazar" ||
1230
+ name.lexeme === "incluye" ||
1231
+ name.lexeme === "empiezaCon" ||
1232
+ name.lexeme === "terminaCon" ||
1233
+ name.lexeme === "caracter" ||
1234
+ name.lexeme === "subcadena" ||
1235
+ name.lexeme === "filtrar" ||
1236
+ name.lexeme === "mapear" ||
1237
+ name.lexeme === "reducir" ||
1238
+ name.lexeme === "buscar" ||
1239
+ name.lexeme === "algunos" ||
1240
+ name.lexeme === "todos" ||
1241
+ name.lexeme === "unir" ||
1242
+ name.lexeme === "cortar" ||
1243
+ name.lexeme === "insertar"
789
1244
  ) {
790
1245
  const args = [];
791
1246
  do {
792
1247
  args.push(this.expression());
793
- } while (this.match('COMMA'));
794
- this.consume('RIGHT_PAREN', 'Expected ) after method call');
1248
+ } while (this.match("COMMA"));
1249
+ this.consume("RIGHT_PAREN", "Expected ) after method call");
795
1250
 
796
1251
  return {
797
- type: 'MethodCall',
1252
+ type: "MethodCall",
798
1253
  object,
799
1254
  method: name.lexeme,
800
1255
  arguments: args,
801
1256
  };
802
1257
  } else {
803
- throw new Error(
804
- `El método ${name.lexeme}() no acepta argumentos`
805
- );
1258
+ throw new Error(`El método ${name.lexeme}() no acepta argumentos`);
806
1259
  }
807
1260
  }
808
1261
  // Consume the closing parenthesis
809
- this.consume('RIGHT_PAREN', 'Expected ) after method call');
1262
+ this.consume("RIGHT_PAREN", "Expected ) after method call");
810
1263
  }
811
1264
 
812
1265
  return {
813
- type: 'MethodCall',
1266
+ type: "MethodCall",
814
1267
  object,
815
1268
  method: name.lexeme,
816
1269
  };
817
1270
  }
818
1271
 
819
1272
  return {
820
- type: 'PropertyAccess',
1273
+ type: "PropertyAccess",
821
1274
  object,
822
1275
  name: name.lexeme,
823
1276
  };
@@ -831,21 +1284,21 @@ class Parser {
831
1284
  finishCall(callee) {
832
1285
  const args = [];
833
1286
 
834
- if (!this.check('RIGHT_PAREN')) {
1287
+ if (!this.check("RIGHT_PAREN")) {
835
1288
  do {
836
1289
  if (args.length >= 255) {
837
- throw new Error('Cannot have more than 255 arguments');
1290
+ throw new Error("Cannot have more than 255 arguments");
838
1291
  }
839
1292
  args.push(this.expression());
840
- } while (this.match('COMMA'));
1293
+ } while (this.match("COMMA"));
841
1294
  }
842
1295
 
843
- const paren = this.consume('RIGHT_PAREN', 'Expected ) after arguments');
1296
+ const paren = this.consume("RIGHT_PAREN", "Expected ) after arguments");
844
1297
 
845
1298
  return {
846
- type: 'Call',
1299
+ type: "Call",
847
1300
  callee: {
848
- type: 'Variable',
1301
+ type: "Variable",
849
1302
  name: callee.lexeme,
850
1303
  },
851
1304
  arguments: args,
@@ -859,16 +1312,16 @@ class Parser {
859
1312
  arrayLiteral() {
860
1313
  const elements = [];
861
1314
 
862
- if (!this.check('RIGHT_BRACKET')) {
1315
+ if (!this.check("RIGHT_BRACKET")) {
863
1316
  do {
864
1317
  elements.push(this.expression());
865
- } while (this.match('COMMA'));
1318
+ } while (this.match("COMMA"));
866
1319
  }
867
1320
 
868
- this.consume('RIGHT_BRACKET', 'Expected ] after array elements');
1321
+ this.consume("RIGHT_BRACKET", "Expected ] after array elements");
869
1322
 
870
1323
  return {
871
- type: 'ArrayLiteral',
1324
+ type: "ArrayLiteral",
872
1325
  elements,
873
1326
  };
874
1327
  }
@@ -880,33 +1333,33 @@ class Parser {
880
1333
  objectLiteral() {
881
1334
  const properties = [];
882
1335
 
883
- if (!this.check('RIGHT_BRACE')) {
1336
+ if (!this.check("RIGHT_BRACE")) {
884
1337
  do {
885
1338
  // Parse property name (identifier or string)
886
1339
  let name;
887
- if (this.match('IDENTIFIER')) {
1340
+ if (this.match("IDENTIFIER")) {
888
1341
  name = this.previous().lexeme;
889
- } else if (this.match('STRING')) {
1342
+ } else if (this.match("STRING")) {
890
1343
  name = this.previous().literal;
891
1344
  } else {
892
- throw new Error('Se esperaba un nombre de propiedad');
1345
+ throw new Error("Se esperaba un nombre de propiedad");
893
1346
  }
894
1347
 
895
- this.consume('COLON', 'Expected : after property name');
1348
+ this.consume("COLON", "Expected : after property name");
896
1349
  const value = this.expression();
897
1350
 
898
1351
  properties.push({
899
- type: 'Property',
1352
+ type: "Property",
900
1353
  name,
901
1354
  value,
902
1355
  });
903
- } while (this.match('COMMA'));
1356
+ } while (this.match("COMMA"));
904
1357
  }
905
1358
 
906
- this.consume('RIGHT_BRACE', 'Expected } after object properties');
1359
+ this.consume("RIGHT_BRACE", "Expected } after object properties");
907
1360
 
908
1361
  return {
909
- type: 'ObjectLiteral',
1362
+ type: "ObjectLiteral",
910
1363
  properties,
911
1364
  };
912
1365
  }
@@ -950,7 +1403,7 @@ class Parser {
950
1403
  * @returns {boolean} True if we reached the end
951
1404
  */
952
1405
  isAtEnd() {
953
- return this.peek().type === 'EOF';
1406
+ return this.peek().type === "EOF";
954
1407
  }
955
1408
 
956
1409
  /**
@@ -988,21 +1441,23 @@ class Parser {
988
1441
  this.advance();
989
1442
 
990
1443
  while (!this.isAtEnd()) {
991
- if (this.previous().type === 'EOF') return;
1444
+ if (this.previous().type === "EOF") return;
992
1445
 
993
1446
  switch (this.peek().type) {
994
- case 'VARIABLE':
995
- case 'FUNCION':
996
- case 'MOSTRAR':
997
- case 'LEER':
998
- case 'SI':
999
- case 'MIENTRAS':
1000
- case 'PARA':
1001
- case 'RETORNAR':
1002
- case 'ROMPER':
1003
- case 'CONTINUAR':
1004
- case 'INTENTAR':
1005
- case 'CAPTURAR':
1447
+ case "VARIABLE":
1448
+ case "CONSTANTE":
1449
+ case "FUNCION":
1450
+ case "CLASE":
1451
+ case "MOSTRAR":
1452
+ case "LEER":
1453
+ case "SI":
1454
+ case "MIENTRAS":
1455
+ case "PARA":
1456
+ case "RETORNAR":
1457
+ case "ROMPER":
1458
+ case "CONTINUAR":
1459
+ case "INTENTAR":
1460
+ case "CAPTURAR":
1006
1461
  return;
1007
1462
  }
1008
1463
 
@@ -1016,35 +1471,137 @@ class Parser {
1016
1471
  */
1017
1472
  tryStatement() {
1018
1473
  // Parse the try block
1019
- this.consume('LEFT_BRACE', 'Expected { after intentar');
1474
+ this.consume("LEFT_BRACE", "Expected { after intentar");
1020
1475
  const tryBlock = this.block();
1021
- this.consume('RIGHT_BRACE', 'Expected } after intentar block');
1476
+ this.consume("RIGHT_BRACE", "Expected } after intentar block");
1022
1477
 
1023
1478
  // Look for catch block
1024
- if (this.match('CAPTURAR')) {
1479
+ if (this.match("CAPTURAR")) {
1025
1480
  // Parse catch parameter (error variable name)
1026
- this.consume('LEFT_PAREN', 'Expected ( after capturar');
1481
+ this.consume("LEFT_PAREN", "Expected ( after capturar");
1027
1482
  const errorVariable = this.consume(
1028
- 'IDENTIFIER',
1029
- 'Expected error variable name'
1483
+ "IDENTIFIER",
1484
+ "Expected error variable name",
1030
1485
  );
1031
- this.consume('RIGHT_PAREN', 'Expected ) after error variable');
1486
+ this.consume("RIGHT_PAREN", "Expected ) after error variable");
1032
1487
 
1033
1488
  // Parse catch block
1034
- this.consume('LEFT_BRACE', 'Expected { after capturar');
1489
+ this.consume("LEFT_BRACE", "Expected { after capturar");
1035
1490
  const catchBlock = this.block();
1036
- this.consume('RIGHT_BRACE', 'Expected } after capturar block');
1491
+ this.consume("RIGHT_BRACE", "Expected } after capturar block");
1037
1492
 
1038
1493
  return {
1039
- type: 'TryCatch',
1494
+ type: "TryCatch",
1040
1495
  tryBlock,
1041
1496
  catchBlock,
1042
1497
  errorVariable: errorVariable.lexeme,
1043
1498
  };
1044
1499
  } else {
1045
- throw new Error('Se esperaba capturar después del bloque intentar');
1500
+ throw new Error("Se esperaba capturar después del bloque intentar");
1046
1501
  }
1047
1502
  }
1503
+
1504
+ /**
1505
+ * Parses an elegir (switch) statement
1506
+ * @returns {Object} Elegir statement
1507
+ */
1508
+ elegirStatement() {
1509
+ const discriminant = this.expression();
1510
+ this.consume("LEFT_BRACE", "Expected { after elegir expression");
1511
+
1512
+ const cases = [];
1513
+ let defaultCase = null;
1514
+
1515
+ while (!this.check("RIGHT_BRACE") && !this.isAtEnd()) {
1516
+ if (this.match("CASO")) {
1517
+ const testValue = this.expression();
1518
+ this.consume("COLON", "Expected : after caso value");
1519
+
1520
+ let consequent;
1521
+ if (this.check("LEFT_BRACE")) {
1522
+ this.advance();
1523
+ consequent = this.block();
1524
+ this.consume("RIGHT_BRACE", "Expected } after caso block");
1525
+ } else {
1526
+ consequent = [this.declaration()];
1527
+ }
1528
+
1529
+ cases.push({
1530
+ test: testValue,
1531
+ consequent,
1532
+ });
1533
+ } else if (this.match("PORDEFECTO")) {
1534
+ this.consume("COLON", "Expected : after pordefecto");
1535
+
1536
+ let consequent;
1537
+ if (this.check("LEFT_BRACE")) {
1538
+ this.advance();
1539
+ consequent = this.block();
1540
+ this.consume("RIGHT_BRACE", "Expected } after pordefecto block");
1541
+ } else {
1542
+ consequent = [this.declaration()];
1543
+ }
1544
+
1545
+ defaultCase = {
1546
+ consequent,
1547
+ };
1548
+ } else {
1549
+ throw new Error("Se esperaba caso o pordefecto dentro de elegir");
1550
+ }
1551
+ }
1552
+
1553
+ this.consume("RIGHT_BRACE", "Expected } after elegir block");
1554
+
1555
+ return {
1556
+ type: "ElegirStatement",
1557
+ discriminant,
1558
+ cases,
1559
+ defaultCase,
1560
+ };
1561
+ }
1562
+
1563
+ /**
1564
+ * Parses a hacer/mientras (do-while) statement
1565
+ * @returns {Object} HacerMientras statement
1566
+ */
1567
+ hacerMientrasStatement() {
1568
+ this.consume("LEFT_BRACE", "Expected { after hacer");
1569
+ const body = this.block();
1570
+ this.consume("RIGHT_BRACE", "Expected } after hacer block");
1571
+
1572
+ this.consume("MIENTRAS", "Expected mientras after hacer block");
1573
+ const condition = this.expression();
1574
+
1575
+ return {
1576
+ type: "HacerMientrasStatement",
1577
+ body,
1578
+ condition,
1579
+ };
1580
+ }
1581
+
1582
+ /**
1583
+ * Parses a para cada (for-each) statement
1584
+ * @returns {Object} ForEach statement
1585
+ */
1586
+ forEachStatement() {
1587
+ const iteratorName = this.consume(
1588
+ "IDENTIFIER",
1589
+ "Expected iterator variable name after cada",
1590
+ );
1591
+ this.consume("EN", "Expected en after iterator variable");
1592
+ const iterable = this.expression();
1593
+
1594
+ this.consume("LEFT_BRACE", "Expected { after iterable");
1595
+ const body = this.block();
1596
+ this.consume("RIGHT_BRACE", "Expected } after para cada block");
1597
+
1598
+ return {
1599
+ type: "ForEachStatement",
1600
+ iterator: iteratorName.lexeme,
1601
+ iterable,
1602
+ body,
1603
+ };
1604
+ }
1048
1605
  }
1049
1606
 
1050
1607
  module.exports = Parser;