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

Segunda versión del REPL con AST

38/58
Recursos

¿Cómo mejorar nuestro REPL para mostrar el árbol de evaluación?

Nos encontramos en una etapa crítica del desarrollo de nuestro lenguaje de programación, en donde tenemos nuestro parser funcionando y generando un árbol sintáctico abstracto (AST). Sin embargo, es fundamental poder visualizar este árbol para entender el orden de evaluación dentro de nuestro programa. Para lograrlo, modificaremos nuestro REPL (Read-Evaluate-Print Loop) con algunos pasos claves.

¿Cuáles son los pasos para modificar el REPL?

  1. Importar módulos necesarios:

    • Desde typing, importamos la lista, ya que estaremos generando una lista.
    • Importamos el program desde nuestro módulo AST.
    • Importamos el parser desde el módulo parser.
  2. Implementación del pipeline:

    • Primero, pasamos nuestro código al lexer, que genera tokens.
    • Luego, esos tokens se envían al parser que, a través de parse program, genera una estructura de tipo programa.
    • Debemos asegurarnos de que nuestro REPL muestra también errores de sintaxis que puedan surgir.
  3. Gestión de errores de sintaxis:

    • No es necesario detener el análisis del parser cuando se encuentra un error, se almacena para mostrarlo posteriormente.
    • Creamos una función privada llamada printParseErrors que recibe una lista de errores y los imprime.
  4. Imprimir la representación del programa:

    • Si el parser no encuentra errores, imprimimos el programa representado como un AST.

¿Cómo ejecutar y probar las modificaciones?

  • Usamos la terminal para correr nuestro intérprete.

  • Ingresamos una expresión como 2 * 3 / 5 + 4, y observamos el árbol de evaluación que refleja el orden correcto de operaciones:

    1. Primero se evalúa 2 * 3.
    2. El resultado se divide entre 5.
    3. Finalmente, se suma 4.
  • También podemos asignarle valores a variables, por ejemplo, A = 5 * 4, donde primero se realiza 5 * 4 y luego se asigna el valor a A.

¿Qué reto se propone lograr?

Se propone ajustar el string resultante del árbol de evaluación para representar el árbol de manera más visualmente comprensible. Esto podría hacerse mediante:

  • Indentaciones: para mostrar la jerarquía de operaciones de manera clara.
  • ASCII art: para dar un formato visual atractivo y funcional.

¿Cuál es el siguiente paso en el desarrollo?

Avanzamos hacia la creación del evaluador. Esta etapa crucial dará significado a nuestros símbolos matemáticos, permitiéndonos evaluar directamente el lenguaje de programación Platzi. ¡Esto representa un gran paso hacia dotar de vida a nuestro lenguaje!

Recuerda, este camino requiere tanto paciencia como creatividad. Si te sientes emocionado o tienes preguntas, comparte tu experiencia en los comentarios y sigue adelante en este fascinante viaje de construcción de un lenguaje de programación.

Aportes 2

Preguntas 0

Ordenar por:

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

Para modificar el formato de salida del REPL hay que modificar los dunder __str__ methods para cambiar su formato, pero hacer esto puede llevar a muchos fallos en los tests xD Además ya quiero empezar con el evaluador que es lo que más me da curiosidad OwO!!!
.
Por cierto, algo cursiodi es que en nuestro REPL no podemos usar las flechas de izquierda y derecha porque se imprimen caracteres raros, tampoco podemos navegar a través de la historia de los comandos que hemos escrito, muchos REPL’s si lo permiten usar.
.
Si quieren agregar este feature al REPL (que lo hace muuuuucho más elegante), solo tienen que agregar esto al inicio de repl.py:

import readline

Con eso ya pueden usar las flechitas del teclado y navegar entre todas las líneas de código que han escrito en su REPL usando las flechitas de “arriba” y “abajo”.

Otra cosa importante dentro de cualquier REPL es poder limpiar la pantalla, para hacer eso, yo agregué esta función al repl.py:

from os import system, name 

def clear(): 

    # for windows 
    if name == 'nt': 
        _ = system('cls') 

    # for mac and linux(here, os.name is 'posix') 
    else: 
        _ = system('clear') 

...

while (source := input(">> ")) != "salir()":

        if source == "limpiar()":
        	clear()

        else:

        	lexer: Lexer = Lexer(source)
		...

Y simplemente en el REPL en ejecución escribes limpiar() y se limpia la pantalla 😄
.
Aquí dejo mi REPL completo hasta ahorita por si lo quieren copiar:
.
Adicion de clear y uso de flechas dentro del REPL

Como menciona RetaxMaster, posiblemente realizar una buena implementación puede llevar realizar demasiadas modificaciones al código.

Pero invirtiendo unos minutos en el repl y trabajando el programa (de una manera no muy eficiente) se puede tabular los paréntesis para que facilite la lectura

	    str_program: str = program.__str__()
            statement: str = ''
            count_paremt = 0
            for i, caracter in enumerate(str_program):
                if caracter == '(':
                    _print_statement(caracter,count_paremt)
                    count_paremt +=1
                elif caracter == ')':
                    if statement != '':
                        _print_statement(statement,count_paremt)
                    count_paremt -=1
                    _print_statement(caracter,count_paremt)
                    statement = ''
                elif caracter == ';':
                    statement += caracter
                    if statement != '':
                        _print_statement(statement,count_paremt)
                    statement = ''
                else:
                    if not (statement == '' and caracter == ' '):
                        statement += caracter
                
            _print_statement(statement,count_paremt)

el resultado es algo como esto

((5 + (6 * 9)) + 8)
(

(
(
5 + 6 * 9
)
)

  • 8
    )