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

Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Lectura de caracteres y tokens

5/58
Recursos

Aportes 15

Preguntas 4

Ordenar por:

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

Efectivamente los errores fueron mis amigos jaja, tuve otros errores porque había escrito mal una sintaxis en Python, y me decía: “Error en la línea X” y fui a ver la línea X y así encontré mis errores 😄
.
Apenas estamos iniciando pero ando con hype jajaja, me gusta el TDD. Por cierto, algo curioso es que, por la forma en la que se leen los tokens ilegales, el orden en el que los comparamos en el expected_tokens debe ser el mismo que el orden en el que pusimos los tokens en source, de lo contrario el assert fallará aunque los tokens sí sean ilegales simplemente por el orden

A mí los test no me fallaban, solo me decía Ran 0 tests in 0.007s (se corrieron cero pruebas en 0.007 segundos) y al final lo solucioné usando el comando:

mypy . && py -m unittest discover  -p "*_test.py"

en vez de

mypy . && nosetests

A ver, entendamos lo que tenemos por acá:

def test_ilegal(self) -> None:
        source: str = '!¿@'
        lexer: Lexer = Lexer(source)
        tokens: List[Token] = []

        for i in range(len(source)):
            tokens.append(lexer.next_token())

        expected_tokens: List[Token] = [
            Token(TokenType.ILLEGAL, '!'),
            Token(TokenType.ILLEGAL, '¿'),
            Token(TokenType.ILLEGAL, '@')
        ]

        self.assertEquals(tokens, expected_tokens)

Aquí tenemos un test en el que estamos cargando una lista con caracteres inválidos para nuestro lenguaje. Luego de esto creamos una lista de los tokens esperados, en este caso queremos que el Lexer devuelva que los tres caracteres son ilegales como TokenType.ILLEGAL Para hacer esto alimentamos el lexer con la línea de texto que contiene los caracteres ilegales. Esta línea es revisada por el lexer en next_token Method validando que el carácter sea ilegal y devolviendo un TokenType.ILLEGAL Sin embargo, debemos notar que en este punto el Lexer siempre va a devolver Token.ILLEGAL para lo que sea que lea, podemos meterle cualquier carácter alfanumérico que siempre va a devolver Token.ILLEGAL ya que aún no estamos haciendo ninguna validación.

def next_token(self) -> Token:
        token = Token(TokenType.ILLEGAL, self._character)
        self._read_character()
        return token

En el método next_token devolvemos siempre Tokens Ilegales por cada carácter recibido en la línea.

def _read_character(self) -> None:
        if self._read_position >= len(self._source):
            self._character = ''
        else:
            self._character = self._source[self._read_position]
        self._position = self._read_position
        self._read_position += 1

Solamente está moviendo la lectura un carácter hacia adelante de la lista recibida en la línea de texto.

  • Rescatamos acá un par de cosas, los Lexer van a moverse linea a linea por cada archivo de código fuente.

  • Los token nos van a permitir usar los Enum para identificar el tipo de elemento que tenemos en cada línea

  • Tendremos que encontrar una forma de pasear o validar palabras más allá de caracteres simples, es como veíamos en la clase pasada lo que haremos con los espacios en blanco.

Para aquellos que “nosetests” les de algún error como ‘AttributeError’, es porque “nose” ya no es mantenido, yo he instalado nose2. Lo usarían así

mypy . && nose2

Tip: Desde python 3.9 es posible utilizar list[Token] para no tener que importar List desde typing.

para windows podemos usar `mypy .; nosetests`

Nota mental, para que los tests corran hay que estar en la carpeta raíz que contiene la carpeta lpp. De lo contrario se muestra un error como este:

. is not a valid Python package name

Tengo que darle un par de vueltas más. Complicado el tema

**nose** esta deprecado, recomiendo usar y instalar **pynose**, el comando sería: ` mypy . && pynose`
Si les da **error**: `"LexerTest" has no attribute "assertEquals" [attr-defined]` Es porque ahora se llama `assertEqual` **Fuente:** Click derecho ir a la definition, y les muestra algo asi: <https://github.com/python/typeshed/blob/119b204ed18ca3cf4892a95aabab6b3027151ab8/stdlib/unittest/case.pyi#L133C9-L133C20>
recuerden que la versión actual es python 3.9

Para aquellos que esten en windows como yo, se les sera un poco mas dificil, en especial la parte de concatenar los comandos, pero lo solucione usando la terminal de windows 10 y dando enters, otra cosa que tuve que hacer fue instalar el nose2 y renombrar el archivo a test_lexer.py

El comando final quedaria como:

mypy.
nose2

Tienen que dejar el salto de linea

Si no les funciona mypy . && nosetest y ya intentaron las soluciones de la sección de aportes, instalen nose2

requirements.txt

nose2
mypy==0.782

recuerden renombrar lexer_test.py por test_lexer.py

Y ya solo ejecutan

mypy . && nose2

Chic@s, si no les funciona el test intenten con este code.

$ mypy . && nosetests test/*_test.py

Muy cool el TDD!!!