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

Evaluación del return statement

47/58
Recursos

¿Cómo evaluar el Return Statement correctamente?

El Return Statement es crucial en cualquier lenguaje de programación, ya que detiene el flujo de ejecución del resto del código. Cuando encontramos un Return, el valor debe ser regresado de inmediato y se ignoran los siguientes Statements. Este proceso requiere que modifiquemos varias partes de nuestro evaluador para adecuarlo a esta funcionalidad. Profundicemos en cómo adaptarlo y cómo realizar pruebas efectivas para asegurar su correcto funcionamiento.

¿Qué cambios realizar en el evaluador?

Primero, es necesario modificar el evaluador para que, en lugar de evaluar cada Statement individualmente, reconozca y regrese el valor del Return Statement cuando lo encuentre. Esto implica crear un nuevo objeto que represente tanto el Return como el valor que está devolviendo.

  • Modificar Evaluate Statements: Cambiamos a Evaluate Program para que sea más genérico y captura el Return Statement.
  • Agregar Bloques de Evaluación: Crear una función para evaluar bloques que detenga la ejecución al encontrar un Return Statement.

¿Cómo crear un test para el Return Statement?

Es esencial realizar tests para cada forma en que podemos utilizar el Return Statement. Vamos a crear un Test llamado Test Return Evaluation que evaluará diferentes escenarios:

  1. Literal sencillo: Un Return que simplemente regresa un literal como 10, debe regresar el valor de 10.
  2. Statements múltiples: Si se encuentra un Statement después del Return, debe ser ignorado.
  3. Expresiones complejas: Podemos regresar expresiones y, tras ellas, ignorar cualquier otro Statement. Ej., si return 3 * 6, el valor debe ser 18.
  4. Bloques condicionales: Al utilizar Return dentro de un bloque, debe detenerse todo el bloque, regresando el valor del Return específico, no importando otros Returns en el bloque.

Implementación del objeto Return

Creamos un nuevo objeto en el sistema que represente el Return:

class Return(Object):
    def __init__(self, value: Object):
        self.value = value

    def type(self) -> str:
        return 'RETURN'

    def inspect(self) -> str:
        return self.value.inspect()

Este objeto es sencillo, pero a medida que el lenguaje evoluciona, se torna más robusto.

¿Cómo ajustar la función eval?

Dentro del evaluador principal, evaluamos si encontramos un nodo de tipo Return:

  • Importar y detectar el Return: Añadimos una condicional en evaluate que verifique nodos de tipo Return.
  • Evaluar valores del Return: Si el nodo Return tiene un valor de retorno no nulo, se evalúa dicho valor, pudiendo ser una expresión compleja.

¿Qué sucede si el Return no está siendo detectado?

Al crear el objeto Return, puede surgir el error de no definirlo correctamente. Debemos importarlo y asegurarnos que todos los tests asocien correctamente el valor.

from object import Return
# ...
if isinstance(node, ASTReturnStatement):
    statement = node.statement
    if statement is not None:
        evaluated = self.eval(statement)
        if evaluated is not None:
            return Return(evaluated)

Validación de los cambios: comprobación con test

Finalmente, verificamos que los tests reconozcan el comportamiento deseado:

# Comprobar tests
$ python -m unittest discover

Los tests deben confirmar el correcto funcionamiento del Return y su integración en el evaluador. Este proceso nos permite asegurar que el evaluador del lenguaje ahora maneja el Return como se espera, permitiendo además mejorar la ejecución y manejo de otros bloques en el código.

Con esto, no solo regresamos valores, sino que podemos detener la ejecución cuando sea necesario, lo que añade eficiencia y control a la programación. Sigamos innovando y aprendiendo sobre la implementación y manejo de objetos en programación. ¡Vamos por más!

Aportes 1

Preguntas 1

Ordenar por:

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

Interesante 🤔 En cierto modo me causa conflicto porque podemos poner regresa dentro de un if o directamente en el programa, en otros lenguajes de programación, los return statements solo funcionan dentro de las funciones… Pero ciertamente esto es LPP así que aquí nosotros definimos las reglas de diseño, y si quiero que mi lenguaje pueda regresar cualquier cosa en cualquier punto pues lo vamos a hacer! jaja
.
Ahora que lo pienso, la palabra regresa es una buena traducción al español, pero una mejor adaptación podría ser devuelve 🤔 como sea, no paro de confundirme en el REPL jaja, sigo escribiendo return en lugar de regresa xD Estuve media hora viendo porque en los tests si pasaban y en el REPL no funcionaba y era porque en el REPL estaba escribiendo return jaja chale