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

Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Parseo de condicionales: implementación

32/58
Recursos

Aportes 2

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

<h4>Aquí les dejo mi solución al reto</h4>
  1. Diseñe el test:
def test_if_else_expression(self) -> None:
	source: str = 'if x > y then {z} else {w}'
	# Aqui va el mismo codigo que en test_if_expression
        # Test alternative
        assert if_expression.alternative is not None
        self.assertIsInstance(if_expression.alternative, Block)
        self.assertEquals(len(if_expression.alternative.statements), 1)

        alternative_statement = cast(
            ExpressionStatement, if_expression.alternative.statements[0])
        assert alternative_statement.expression is not None
        self._test_identifier(alternative_statement.expression, 'w')
  1. Luego implemente lo necesario para parsear else, en parser.py dentro de _parse_if al final antes de return, agregue:
if self._peek_token is not None:
            if self._peek_token.token_type == TokenType.ELSE:
                self._advance_tokens()

                if not self._expected_token(TokenType.LBRACE):
                    return None

                if_expression.alternative = self._parse_block()

La sintaxis de SigmaF es un poco diferente, pero la implementación es casi la misma que para LPP 😉

Vale, comparto mi solución:
.
Para los tests (para no repetir código y poder hacer más tests), lo que hice fue crear un helper llamado _test_block el cual testeará todo un bloque de código, le puedes pasar cuántos statements quieres testear y una lista de los identificadores que esperas que esos statements devuelvan, el número de identificadores pasados en la lista deben ser los mismos que el número de statements:

def _test_block(self,
                    block: Block,
                    statements_number,
                    expected_identifiers: List[str]) -> None:


        self.assertIsInstance(block, Block)
        self.assertEquals(len(block.statements), statements_number)
        self.assertEquals(len(expected_identifiers), len(block.statements))

        # Recorro cada statement junto a los expected identifiers
        for statement, identifier in zip(block.statements, expected_identifiers):

            statement = cast(ExpressionStatement, statement)

            assert statement.expression is not None
            self._test_identifier(statement.expression, identifier)

Una vez implementado eso, cambié mi test de if para usar este helper:

...
# Test consequence
assert if_expression.consequence is not None

# Aquí uso la helper function
self._test_block(if_expression.consequence, 1, ["z"]) 

# Test alternative
self.assertIsNone(if_expression.alternative)
...

Y para el test del else hice lo mismo:

def test_if_else_expression(self) -> None:

    source: str = 'si (a > b) { x } si_no { z; x; }'

    lexer: Lexer = Lexer(source)
    parser: Parser = Parser(lexer)

    program: Program = parser.parse_program()

    self._test_program_statements(parser, program, 1)

    # Test correct node types
    if_expression = cast(If, cast(ExpressionStatement, program.statements[0]).expression)
    self.assertIsInstance(if_expression, If)

    # Test condition
    assert if_expression.condition is not None

    self._test_infix_expression(cast(Expression, if_expression.condition), "a", ">", "b")

    # Test consequence
    assert if_expression.consequence is not None
    self._test_block(if_expression.consequence, 1, ["x"])

    # Test alternative
    assert if_expression.alternative is not None
    self._test_block(if_expression.alternative, 2, ["z", "x"])

Y para hacer que pase, lo único que hice fue que, en el parser.py pregunto si el siguiente token es un ELSE, (primero se pone el assert de peek_token porque este es opcional y luego mypy se quja 😄)
.
Si la condición del siguiente token para un ELSE se cumple, me muevo hacia ese token y verifico que el siguiente token sea una llave de apertura, si sí lo es, pues mandamos a parsear el bloque con la función que hicimos en esta clase jaja.
.
Después miré la solución del profesor y vi que llegamos a lo mismo xD. Juro que no miré el código del profesor antes de resolver el reto!

if_expression.consequence = self._parse_block()

# Estamos en el "}", si el siguiente token es el else, entonces lo parseamos
assert self._peek_token is not None
if self._peek_token.token_type == TokenType.ELSE:

    self._advance_tokens()
    # Si el siguiente token no es una llave de apertura, hay un error de sintaxis
    if not self._expected_token(TokenType.LBRACE):
        return None

    if_expression.alternative = self._parse_block()

return if_expression