Lexer: Identificación de Keywords y Tokens Complejos

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

Contenido del curso

Construcción del lexer o tokenizador

Construcción del parser o analizador sintáctico

Evaluación o análisis semántico

Resumen

Construir un lexer capaz de reconocer palabras reservadas, identificadores y números enteros es el paso que transforma un analizador léxico básico en una herramienta real de compilación. Aquí se detalla cómo lograrlo siguiendo Test Driven Development, creando primero los tests que fallan y después las funciones que los hacen pasar.

¿Qué tokens nuevos necesita reconocer el lexer?

Hasta ahora el lexer solo tokenizaba delimitadores y operadores simples. Ahora es momento de soportar asignaciones, keywords como variable, identificadores como el nombre de una variable, y enteros [0:48]. Para validar este comportamiento se escribe un test llamado test_assignment que representa la sentencia variable cinco = 5; y espera cinco tokens:

  • Un token de tipo LET con literal variable.
  • Un token de tipo IDENT con literal cinco.
  • Un token de tipo ASSIGN con literal =.
  • Un token de tipo INT con literal 5.
  • Un token de tipo SEMICOLON con literal ;.

Cada llamada a next_token debe devolver exactamente uno de estos valores [2:30].

¿Cómo determinar si un carácter es una letra válida?

La función is_letter recibe un carácter y devuelve un booleano [4:15]. Internamente usa una expresión regular con un character set que incluye a-z, A-Z, el guion bajo y, como el lenguaje soporta español, también vocales acentuadas (á, é, í, ó, ú) en minúsculas y mayúsculas, además de la ñ y Ñ [5:10]. El patrón se envuelve con bool(match(...)) para garantizar que el retorno sea estrictamente booleano.

¿Qué hace read_identifier?

Una vez confirmado que el carácter actual es una letra, read_identifier avanza posición por posición mientras siga encontrando letras [6:20]. El algoritmo es directo:

  • Guarda la posición inicial en initial_position.
  • Ejecuta read_character en un ciclo while is_letter(self._character).
  • Retorna el slice del source desde initial_position hasta self._position.

Este slice es la literal completa del identificador o keyword que se acaba de leer.

¿Cómo distinguir un keyword de un identificador?

La función lookup_token_type vive en el archivo token porque su responsabilidad está ligada al tipo de token, no al lexer [7:35]. Dentro de ella se define un diccionario keywords de tipo Dict[str, TokenType] que mapea palabras reservadas a su tipo correspondiente. Por ejemplo, la llave "variable" apunta a TokenType.LET. Cuando la literal leída coincide con alguna llave del diccionario, se devuelve ese tipo; en caso contrario, se devuelve TokenType.IDENT [8:40].

¿Por qué es necesario ignorar los espacios en blanco?

En este lenguaje de programación el espacio en blanco no tiene significado semántico, así que debe descartarse antes de procesar cada token [9:30]. La función skip_whitespace se invoca al inicio de next_token y utiliza la expresión regular \s para detectar whitespace. Mientras el carácter actual coincida, simplemente llama a read_character para avanzar sin generar ningún token.

¿Cómo se tokenizan los números enteros?

El enfoque es idéntico al de los identificadores, pero con dígitos. Primero, is_number valida si el carácter actual es un dígito mediante la expresión regular ^\d$ [11:08]. Después, read_number guarda la posición inicial y avanza mientras encuentre dígitos, retornando el slice correspondiente. Finalmente, next_token crea un token de tipo TokenType.INT con esa literal [11:50].

La estructura de read_number es prácticamente un espejo de read_identifier:

  • initial_position = self._position.
  • while is_number(self._character): self._read_character().
  • return self._source[initial_position:self._position].

Con estas funciones implementadas, los tests pasan correctamente y el lexer ya reconoce operadores, delimitadores, keywords, identificadores y enteros [12:50]. El siguiente reto es soportar declaraciones de funciones con el keyword procedimiento y llamadas a funciones, temas que se abordan en la clase siguiente. ¿Qué otros keywords agregarías a tu lenguaje? Comparte tus ideas en los comentarios.