Introducción al desarrollo de intérpretes y lenguajes de programación

1

Aprende a desarrollar lenguajes de programación con intérpretes

2

Desarrolla LPP o Lenguaje de Programación Platzi

Construcción del lexer o tokenizador

3

¿Qué es análisis léxico? Funcionamiento del lexer y tokens

4

Estructura y definición de tokens en Python

5

Lectura de caracteres y tokens

6

Tokens ilegales, operadores de un solo carácter y delimitadores

7

Reconocimiento y diferenciación entre letras y números

8

Declaración y ejecución de funciones

9

Extensión del lexer: condicionales, operaciones y booleanos

10

Operadores de dos caracteres

11

Primera versión del REPL con tokens

Construcción del parser o analizador sintáctico

12

¿Qué es un parser y AST?

13

Estructura y definición de nodos del AST en Python

14

Parseo del programa o nodo principal

15

Parseo de assignment statements

16

Parseo de let statements

17

Parseo de errores

18

Parseo del return statement

19

Técnicas de parsing y pratt parsing

20

Pruebas del AST

21

Implementación del pratt parser

22

Parseo de Identifiers: testing

23

Parseo de Identifiers: implementación

24

Parseo de enteros

25

Prefix operators: negación y negativos

26

Infix operators y orden de las operaciones: testing

27

Infix operators y orden de las operaciones: implementación

28

Parseo de booleanos

29

Desafío: testing de infix operators y booleanos

30

Parseo de expresiones agrupadas

31

Parseo de condicionales: testing y AST

32

Parseo de condicionales: implementación

33

Parseo de declaración de funciones: testing

34

Parseo de declaración de funciones: AST e implementación

35

Parseo de llamadas a funciones: testing y AST

36

Parseo de llamadas a funciones: implementación

37

Completando los TODOs o pendientes del lexer

38

Segunda versión del REPL con AST

Evaluación o análisis semántico

39

Significado de símbolos

40

Estrategias de evaluación para intérpretes de software

41

Representación de objetos

42

Evaluación de expresiones: enteros

43

Evaluación de expresiones: booleanos y nulos

44

Evaluación de expresiones: prefix

45

Evaluación de expresiones: infix

46

Evaluación de condicionales

47

Evaluación del return statement

48

Manejo de errores

49

Ambiente

50

Bindings

51

Evaluación de funciones

52

Llamadas a funciones

Mejora del intérprete

53

Implementación de strings

54

Operaciones con strings

55

Built-in functions: objeto y tests

56

Built-in functions: evaluación

Siguientes pasos

57

Retos para expandir tu intérprete

58

Continúa con el Curso de Creación de Compiladores de Software

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Parseo de expresiones agrupadas

30/58
Recursos

¿Cómo se evalúan las expresiones y declaraciones en un AST?

Entender cómo se construye y evalúa un Árbol de Sintaxis Abstracta (AST) es crucial para desarrollar un lenguaje de programación. Este árbol es fundamental en la representación de la estructura de un código fuente. Inicialmente, es importante entender que los paréntesis determinan el orden de evaluación y es algo que observaremos al construir nuestro evaluador en futuras lecciones.

¿Cuál es el orden de evaluación estándar de las expresiones?

El orden en el que se evalúan las expresiones en un AST imitador sigue las convenciones de los lenguajes de programación usuales. Digamos que tenemos algo como 3 + 4 / 5 * 7 + 10. Por defecto, se evaluaría como:

  1. 4 dividido entre 5.
  2. El resultado multiplicado por 7.
  3. Al resultado de la multiplicación sumarle 3.
  4. Finalmente, sumar 10 al resultado anterior.

Los lenguajes de programación se basan en este orden para evaluar las operaciones de forma sistemática.

¿Cómo modificar el orden de evaluación usando paréntesis?

Si queremos alterar el orden de evaluación estándar, empleamos paréntesis. Por ejemplo, al enfrentar 5 * (2 + 5), primero se evalúa 2 + 5, y luego ese resultado se multiplica por 5. En contraposición, sin paréntesis, la operación sería: multiplicar 5 por 2 y luego sumar 5.

La modificación del orden mediante paréntesis permite:

  • Personalizar el flujo lógico de las operaciones.
  • Asegurar que ciertas operaciones se evalúen primero.

¿Cómo implementar cambios en el parser?

En el parser, para tratar correctamente las expresiones agrupadas por paréntesis se necesitan ciertos ajustes en el código. Aquí hay un flujo general de cómo puede hacerse:

  1. Registrar un prefijo para el paréntesis izquierdo: Esto permite que cuando se encuentre un paréntesis izquierdo, el parser sepa que viene una expresión agrupada.

    register_prefix(token_type=PARENTHESIS_LEFT, fn=parse_group_expression)
    
  2. Crear función para expresiones agrupadas: parse_group_expression se encarga de manejar la evaluación de estas expresiones.

    def parse_group_expression():
        # Avanzar al siguiente token
        next_token()
        # Crear una nueva expresión con precedencia más baja
        expression = parse_expression(precedence=LOWEST)
        # Verificar token esperado (paréntesis derecho)
        if not is_expected_token(PARENTHESIS_RIGHT):
            return None
        return expression
    

Ejemplo de pruebas para expresiones agrupadas

Pruebas adicionales ayudan a asegurar que el parser evalúe correctamente:

  • Test de prioridad entre operaciones similares: Implementar pruebas donde, por ejemplo, a + (b * c) sea evaluado correctamente diferente a a + b * c.

  • Errores sintácticos al no cerrar paréntesis: Probar escenarios donde un paréntesis no cerrado genere errores de sintaxis.

Al agregar estas pruebas, es mejor comprarlas con errores que producen otros lenguajes, como Python o JavaScript, para tener una visión más amplia y práctica de cómo los distintos parsers manejan situaciones similares.

¿Cómo afecta esto el desarrollo de un lenguaje?

El correcto manejo de los paréntesis y el orden de evaluación es fundamental en la construcción del evaluador de un lenguaje de programación. Nos permite definir cómo los desarrolladores utilizarán el lenguaje, cómo las operaciones se calcularán y cómo los errores se manejarán. Esto partiendo de un correcto diseño del parser, que acto seguido influye en la usabilidad y eficacia del lenguaje como tal.

Por último, se invita a los estudiantes a probar modificaciones en sus tests y compartir sus experiencias, además de comparar con otros lenguajes. Este es un ejercicio excelente para familiarizarse con problemas comunes y su resolución en distintos entornos. Recuerda: cada pequeño paso en el aprendizaje es vital para crear un lenguaje sólido y eficiente. ¡Sigamos construyendo juntos el lenguaje de programación Platzi!

Aportes 4

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Vale, es sencillo, adicionando a lo que expliqué en la clase anterior, simplemente al encontrar un paréntesis dice “Uhh, paréntesis, a iniciar a parsear lo que está dentro” e inicia a parsear hasta que ya no encuentra más expresiones por parsear 😄
.
En mi caso, hice la prueba quitando el paréntesis final:

-(5 + 5;

Y me devolvió:

Se esperaba que el siguiente token fuera TokenType.RPAREN pero se obtuvo TokenType.SEMICOLON

Python devuelve:

  File "index.py", line 1
    -(5 + 5;
           ^
SyntaxError: invalid syntax

JavaScript devuelve:

VM40:1 Uncaught SyntaxError: Unexpected token ';'

PHP devuelve:

PHP Parse error:  syntax error, unexpected ';' in /var/www/html/lpp/index.php on line 3

Creo que podríamos ser más amables y en vez de volver el TokenType, mejor devolver el Token Literal jaja

Esto es lo que me lanza Rust

Esto devuelve GCC al intentar compilar -(5 + 5; en C++

Les dejo el error que lanza Haskell al no cerrar un paréntesis: