hispano-lang 2.0.0 → 2.1.1

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/README.md CHANGED
@@ -341,6 +341,41 @@ variable duplicar = funcion(x) {
341
341
  mostrar duplicar(5) // 10
342
342
  ```
343
343
 
344
+ #### Funciones Flecha
345
+
346
+ Sintaxis concisa para funciones anónimas:
347
+
348
+ ```
349
+ // Un parámetro (sin paréntesis)
350
+ variable doble = x => x * 2
351
+
352
+ // Múltiples parámetros
353
+ variable suma = (a, b) => a + b
354
+
355
+ // Sin parámetros
356
+ variable saludar = () => "Hola mundo"
357
+
358
+ // Con bloque de código
359
+ variable factorial = n => {
360
+ si (n <= 1) { retornar 1 }
361
+ retornar n * factorial(n - 1)
362
+ }
363
+
364
+ mostrar doble(5) // 10
365
+ mostrar suma(3, 4) // 7
366
+ mostrar factorial(5) // 120
367
+ ```
368
+
369
+ Funciones flecha son ideales para callbacks:
370
+
371
+ ```
372
+ variable numeros = [1, 2, 3, 4, 5]
373
+
374
+ numeros.mapear(x => x * 2) // [2, 4, 6, 8, 10]
375
+ numeros.filtrar(x => x > 2) // [3, 4, 5]
376
+ numeros.reducir((a, b) => a + b, 0) // 15
377
+ ```
378
+
344
379
  #### Funciones como Parámetros
345
380
 
346
381
  ```
package/dist/evaluator.js CHANGED
@@ -23,7 +23,7 @@ class Evaluator {
23
23
  this.execute(statement);
24
24
  }
25
25
  } catch (error) {
26
- throw new Error(`Error de ejecución: ${error.message}`);
26
+ throw new Error(error.message);
27
27
  }
28
28
 
29
29
  return this.output;
@@ -135,7 +135,6 @@ class Evaluator {
135
135
  const value = this.evaluateExpression(statement.expression);
136
136
  const output = this.stringify(value);
137
137
  this.output.push(output);
138
- console.log(output);
139
138
  }
140
139
 
141
140
  /**
@@ -307,6 +306,28 @@ class Evaluator {
307
306
  }
308
307
  }
309
308
 
309
+ /**
310
+ * Executes a function body (handles both block and arrow expression bodies)
311
+ * @param {Object} func - Function object with body and isArrowExpression flag
312
+ * @returns {any} Result of the function execution
313
+ */
314
+ executeFunctionBody(func) {
315
+ if (func.isArrowExpression) {
316
+ return this.evaluateExpression(func.body);
317
+ }
318
+ try {
319
+ for (const statement of func.body) {
320
+ this.execute(statement);
321
+ }
322
+ return null;
323
+ } catch (returnValue) {
324
+ if (returnValue instanceof ReturnException) {
325
+ return returnValue.value;
326
+ }
327
+ throw returnValue;
328
+ }
329
+ }
330
+
310
331
  /**
311
332
  * Executes an expression statement
312
333
  * @param {Object} statement - Expression statement
@@ -339,6 +360,15 @@ class Evaluator {
339
360
  body: expression.body,
340
361
  };
341
362
 
363
+ case "ArrowFunction":
364
+ return {
365
+ type: "Function",
366
+ name: null,
367
+ parameters: expression.parameters,
368
+ body: expression.body,
369
+ isArrowExpression: expression.isExpression,
370
+ };
371
+
342
372
  case "Assign":
343
373
  const value = this.evaluateExpression(expression.value);
344
374
  this.environment.assign(expression.name, value);
@@ -622,6 +652,10 @@ class Evaluator {
622
652
  const previous = this.environment;
623
653
  try {
624
654
  this.environment = environment;
655
+ // Arrow functions with expression body return the expression directly
656
+ if (callee.isArrowExpression) {
657
+ return this.evaluateExpression(callee.body);
658
+ }
625
659
  this.executeBlock(callee.body);
626
660
  return null;
627
661
  } catch (returnValue) {
@@ -856,7 +890,7 @@ class Evaluator {
856
890
  const previousEnv = this.environment;
857
891
  this.environment = callbackEnv;
858
892
  try {
859
- this.executeBlock(callback.body);
893
+ this.executeFunctionBody(callback);
860
894
  } finally {
861
895
  this.environment = previousEnv;
862
896
  }
@@ -884,14 +918,9 @@ class Evaluator {
884
918
  const prevEnv = this.environment;
885
919
  this.environment = filterEnv;
886
920
  try {
887
- this.executeBlock(filterCallback.body);
888
- } catch (returnValue) {
889
- if (returnValue instanceof ReturnException) {
890
- if (this.isTruthy(returnValue.value)) {
891
- filteredArray.push(array[i]);
892
- }
893
- } else {
894
- throw returnValue;
921
+ const result = this.executeFunctionBody(filterCallback);
922
+ if (this.isTruthy(result)) {
923
+ filteredArray.push(array[i]);
895
924
  }
896
925
  } finally {
897
926
  this.environment = prevEnv;
@@ -920,14 +949,8 @@ class Evaluator {
920
949
  const prevEnv = this.environment;
921
950
  this.environment = mapEnv;
922
951
  try {
923
- this.executeBlock(mapCallback.body);
924
- mappedArray.push(null); // If no return, push null
925
- } catch (returnValue) {
926
- if (returnValue instanceof ReturnException) {
927
- mappedArray.push(returnValue.value);
928
- } else {
929
- throw returnValue;
930
- }
952
+ const result = this.executeFunctionBody(mapCallback);
953
+ mappedArray.push(result);
931
954
  } finally {
932
955
  this.environment = prevEnv;
933
956
  }
@@ -962,13 +985,7 @@ class Evaluator {
962
985
  const prevEnv = this.environment;
963
986
  this.environment = reduceEnv;
964
987
  try {
965
- this.executeBlock(reduceCallback.body);
966
- } catch (returnValue) {
967
- if (returnValue instanceof ReturnException) {
968
- accumulator = returnValue.value;
969
- } else {
970
- throw returnValue;
971
- }
988
+ accumulator = this.executeFunctionBody(reduceCallback);
972
989
  } finally {
973
990
  this.environment = prevEnv;
974
991
  }
@@ -1001,13 +1018,7 @@ class Evaluator {
1001
1018
  const prevEnv = this.environment;
1002
1019
  this.environment = sortEnv;
1003
1020
  try {
1004
- this.executeBlock(sortCallback.body);
1005
- return 0;
1006
- } catch (returnValue) {
1007
- if (returnValue instanceof ReturnException) {
1008
- return returnValue.value;
1009
- }
1010
- throw returnValue;
1021
+ return this.executeFunctionBody(sortCallback) || 0;
1011
1022
  } finally {
1012
1023
  this.environment = prevEnv;
1013
1024
  }
@@ -1040,15 +1051,9 @@ class Evaluator {
1040
1051
  const prevEnv = this.environment;
1041
1052
  this.environment = findEnv;
1042
1053
  try {
1043
- this.executeBlock(findCallback.body);
1044
- } catch (returnValue) {
1045
- if (returnValue instanceof ReturnException) {
1046
- if (this.isTruthy(returnValue.value)) {
1047
- this.environment = prevEnv;
1048
- return array[i];
1049
- }
1050
- } else {
1051
- throw returnValue;
1054
+ const result = this.executeFunctionBody(findCallback);
1055
+ if (this.isTruthy(result)) {
1056
+ return array[i];
1052
1057
  }
1053
1058
  } finally {
1054
1059
  this.environment = prevEnv;
@@ -1076,15 +1081,9 @@ class Evaluator {
1076
1081
  const prevEnv = this.environment;
1077
1082
  this.environment = someEnv;
1078
1083
  try {
1079
- this.executeBlock(someCallback.body);
1080
- } catch (returnValue) {
1081
- if (returnValue instanceof ReturnException) {
1082
- if (this.isTruthy(returnValue.value)) {
1083
- this.environment = prevEnv;
1084
- return true;
1085
- }
1086
- } else {
1087
- throw returnValue;
1084
+ const result = this.executeFunctionBody(someCallback);
1085
+ if (this.isTruthy(result)) {
1086
+ return true;
1088
1087
  }
1089
1088
  } finally {
1090
1089
  this.environment = prevEnv;
@@ -1112,15 +1111,9 @@ class Evaluator {
1112
1111
  const prevEnv = this.environment;
1113
1112
  this.environment = everyEnv;
1114
1113
  try {
1115
- this.executeBlock(everyCallback.body);
1116
- } catch (returnValue) {
1117
- if (returnValue instanceof ReturnException) {
1118
- if (!this.isTruthy(returnValue.value)) {
1119
- this.environment = prevEnv;
1120
- return false;
1121
- }
1122
- } else {
1123
- throw returnValue;
1114
+ const result = this.executeFunctionBody(everyCallback);
1115
+ if (!this.isTruthy(result)) {
1116
+ return false;
1124
1117
  }
1125
1118
  } finally {
1126
1119
  this.environment = prevEnv;
package/dist/parser.js CHANGED
@@ -853,6 +853,11 @@ class Parser {
853
853
 
854
854
  if (this.match("IDENTIFIER")) {
855
855
  const identifier = this.previous();
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
+ }
856
861
  if (this.check("LEFT_PAREN")) {
857
862
  this.advance(); // Consume the LEFT_PAREN
858
863
  return this.finishCall(identifier);
@@ -876,6 +881,10 @@ class Parser {
876
881
  }
877
882
 
878
883
  if (this.match("LEFT_PAREN")) {
884
+ // Check if this could be arrow function params
885
+ if (this.isArrowFunctionParams()) {
886
+ return this.arrowFunctionWithParams();
887
+ }
879
888
  const expr = this.expression();
880
889
  this.consume("RIGHT_PAREN", "Expected ) after expression");
881
890
  return expr;
@@ -1042,6 +1051,109 @@ class Parser {
1042
1051
  };
1043
1052
  }
1044
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
+
1045
1157
  /**
1046
1158
  * Finishes parsing an array access
1047
1159
  * @param {Object} array - The array being accessed
package/dist/tokenizer.js CHANGED
@@ -67,6 +67,9 @@ class Tokenizer {
67
67
  if (this.peek() === "=") {
68
68
  this.advance();
69
69
  this.addToken("EQUAL_EQUAL");
70
+ } else if (this.peek() === ">") {
71
+ this.advance();
72
+ this.addToken("ARROW");
70
73
  } else {
71
74
  this.addToken("EQUAL");
72
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hispano-lang",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "Un lenguaje de programación educativo en español para enseñar programación sin barreras de idioma",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",