Desarrollo de un AST en Python: Creación de la Clase Programa

Clase 14 de 58Curso de Creación de Lenguajes de Programación: Intérpretes

Resumen

¿Cómo crear un nodo principal para un AST?

La creación del Abstract Syntax Tree (AST) en programación es crucial para interpretar y transformar el código de una manera estructurada. El nodo principal que representa al AST es fundamental, ya que es él quien contiene todos los sub-nodos, ya sean expresiones o declaraciones. Vamos a desarrollar una clase de programa que será un nodo principal en el AST, junto con un test básico para verificar su implementación.

¿Cómo inicializar el nodo principal del AST?

Para establecer correctamente la base de nuestro AST, necesitamos crear una clase Programa, que será de tipo AST Node. Esto requerirá también del diseño de un test denominado test_parse_program, que nos indicará si el parser es capaz de devolver correctamente un objeto de tipo Programa. Para llegar a este punto, seguimos estos pasos:

  1. Crear Archivos Necesarios:

    • Se genera un archivo parser.py que contendrá la implementación del parser.
    • Se crea un archivo parser_test.py dentro de una carpeta test para los test unitarios.
  2. Importar Dependencias:

    • Utilizamos unittest.TestCase para estructurar nuestros tests.
    • Inicializamos una clase ParserTest como subclase de TestCase.
  3. Implementación del Test:

    • Se inicia el test test_parse_program, donde el enfoque es en las aserciones.
    • Se introduce una simple estructura de programa: "variable x es igual a 5".
    • Se genera un lexer para convertir el código en tokens, y se crea un parser que usará esos tokens.

¿Cómo crear el test inicial para el parser?

Al seguir la metodología Test Driven Development (TDD), es vital desarrollar los tests primero y luego el código necesario para que los tests pasen exitosamente. Aquí algunos pasos básicos:

  • Definir las Aserciones:

    • Comprobar que el objeto generado no es None.
    • Verificar que el objeto es de tipo Programa.
  • Configurar el Parser:

    • Se busca que el parser tome un lexer y devuelva un objeto de Programa usando parse_program.

Veamos cómo se refleja esto en código:

from unittest import TestCase

class ParserTest(TestCase):
    def test_parse_program(self):
        program = "variable x = 5"
        lexer = Lexer(program)
        parser = Parser(lexer)
        parsed_program = parser.parse_program()
        
        self.assertIsNotNone(parsed_program)
        self.assertIsInstance(parsed_program, Program)

¿Cómo definir la clase Programa?

La clase Programa debe estar bien estructurada para recibir los Statements que forman parte de nuestro AST. Esta clase requiere de un constructor y una implementación mínima de varios métodos:

  1. Estructura de la Clase:

    • Se define la clase Program como una subclase de ASTNode.
    • Su constructor debe recibir una lista de Statements.
  2. Implementar Métodos Requeridos:

    • token_literal: Devuelve el token literal del primer statement o una cadena vacía en caso de que no haya ninguno.
    • __str__: Retornar una concatenación de todos los statements en forma de string.

Aquí un ejemplo simplificado de cómo se podría implementar la clase:

class Program(ASTNode):
    def __init__(self, statements):
        self.statements = statements
    
    def token_literal(self):
        if len(self.statements) > 0:
            return self.statements[0].token_literal()
        return ""
    
    def __str__(self):
        return "".join(str(statement) for statement in self.statements)

¿Cómo implementar un parser básico?

La implementación del parser es el siguiente paso después de confirmar que tenemos la clase Programa correctamente configurada. Un parser básico puede inicializarse de la siguiente manera:

  • Definir el Constructor del Parser:

    • El constructor debe aceptar un lexer.
    • Es necesario asignar el lexer a una variable interna para su uso posterior.
  • Implementar parse_program Básico:

    • Inicializar un objeto Programa con una lista vacía de statements.
    • Retornar el objeto Programa.

Aquí hay una representación básica del parser:

class Parser:
    def __init__(self, lexer):
        self.lexer = lexer

    def parse_program(self):
        return Program([])

Con cada componente correctamente implementado y testeado, podemos estar seguros de que nuestro nodo principal para el AST está listo para ampliaciones futuras. Esto incluye añadir más tipos de statements y seguir comprobando cada nueva adición con tests adicionales. Recuerda siempre acudir al sistema de comentarios ante cualquier duda o esclarecimiento necesario durante tu aprendizaje en programación.