Evaluación de Precedencia y Testeo de Booleanos en Parsers

Clase 29 de 58Curso de Creación de Lenguajes de Programación: Intérpretes

Contenido del curso

Construcción del lexer o tokenizador

Construcción del parser o analizador sintáctico

Evaluación o análisis semántico

Resumen

Dominar las precedencias de operadores y ampliar la cobertura de tests con booleanos son dos pasos fundamentales para construir un parser robusto. Aquí se desglosan los retos planteados para que puedas poner a prueba tu comprensión del AST, el orden de evaluación y la forma en que los nodos se agrupan internamente.

¿Por qué importa la precedencia de operadores en el parser?

La precedencia determina el orden en que el parser agrupa las expresiones dentro del AST [0:10]. Si la precedencia no es correcta, cuando llegues al evaluador los resultados serán erróneos porque las ramas del árbol estarán mal organizadas.

Para verificar esto se crea un test llamado test_operator_precedence que utiliza una lista de tuplas con tres elementos [1:14]:

  • El programa inicial como string de entrada.
  • El resultado esperado, que muestra cómo se agrupan las operaciones con paréntesis.
  • Un entero que indica cuántos statements debe contener el programa.

¿Cómo se aplica PEMDAS en los tests?

El concepto de PEMDAS (el orden de evaluación matemática: paréntesis, exponentes, multiplicación, división, adición y sustracción) es central en estos tests [2:08]. Algunos ejemplos concretos:

  • -A * B: primero se aplica el operador de prefijo negativo a A, y luego se multiplica por B. Resultado esperado: ((-A) * B).
  • !-A: primero se vuelve negativo A porque tiene mayor precedencia, y después se aplica la negación lógica. Resultado: (!(- A)).
  • A + B / C: se evalúa primero B / C y después se suma A. Resultado: (A + (B / C)) [2:16].

Si sumaras A + B antes de dividir entre C, estarías violando PEMDAS y obtendrías un resultado incorrecto.

¿Cómo se prueban múltiples statements?

Un programa puede contener varios statements separados por punto y coma [3:10]. Por ejemplo, 3 + 4; -5 * 5 genera dos statements independientes, cada uno con su propia precedencia interna. Al convertir el programa en string, los paréntesis reflejan exactamente el orden de evaluación.

El loop del test desempaqueta cada tupla en source, expected y cantidad de statements, ejecuta el setup con el lexer y el parser, y finalmente compara el resultado con lo esperado [3:22].

El primer reto consiste en inventar al menos diez tests adicionales con expresiones variadas: operadores aritméticos, booleanos, operadores de comparación y cualquier combinación que se te ocurra [4:24].

¿Cómo incorporar booleanos a los tests existentes?

Ahora que el parser ya sabe manejar nodos booleanos, es momento de extender tres tests [4:48]:

  • Test de expresiones prefijo (test_prefix_expressions): hasta ahora solo se evaluaban valores como 5 y -15. El reto es añadir casos como !verdadero o !falso, ajustando la cantidad de statements y los valores esperados [5:02].
  • Test de expresiones infijo (test_infix_expressions): se pueden agregar comparaciones booleanas como verdadero == verdadero o falso != verdadero [5:20].
  • Preparar tests para la fase de evaluación: cuando llegues al evaluador, estos booleanos también deberán funcionar correctamente [5:36].

¿Qué papel juegan los tests en la calidad del parser?

No existe una única forma correcta de escribir tests. A mayor cantidad de pruebas, mayor confianza en que el programa no tiene bugs [5:50]. Sin embargo, incluso con muchos tests pueden aparecer errores. La estrategia es clara:

  • Identificar el bug.
  • Crear un test que lo reproduzca.
  • Asegurarse de que ese test pase.

Esto no garantiza la ausencia total de errores, pero sí que ese bug específico no volverá a ocurrir [6:08]. Tu test suite crecerá de forma natural conforme el programa evolucione.

Las soluciones de referencia están disponibles en GitHub, pero cada implementación puede ser diferente. Comparte tus propias versiones en los comentarios para enriquecer la discusión.

      Evaluación de Precedencia y Testeo de Booleanos en Parsers