Análisis léxico

Nota: Antes de profundizar en la primera fase de un compilador, es fundamental repasar tus conocimientos sobre lenguajes formales, en particular el tema de autómatas. Estos conceptos son esenciales para entender cómo se verifican los tokens y cómo se aplican las expresiones regulares.

El análisis Léxico es la primera fase de un compilador, y toma el programa fuente de los pre-procesadores que está escrito en forma de declaraciones.La primera fase del compilador se caracteriza por ser la que lee el flujo de caracteres que conforman el programa fuente y los agrupa en tokens. Un token es la unidad mínima de información de un lenguaje de programación; esta puede ser un carácter, un número, una palabra clave, un operador u otro símbolo.

En la Tabla1 se muestra la clasificación que puede haber dentro de la fase de tokenización y ejemplos de ellos. 

Tabla1.Clasificacion de tokens.Elaborado por el autor.23/Nov/2024
Tabla1.Clasificacion de tokens.Elaborado por el autor.23/Nov/2024

La clasificación de tokens que pueden encontrarse en un programa durante el análisis léxico. 

  1. Categoría: Indica el tipo de token que se clasifica según su función o rol dentro del lenguaje de programación.
  2. Descripción: Proporciona una breve explicación del propósito o uso de cada categoría.
  3. Ejemplos: Presenta ejemplos específicos de cada categoría para ilustrar su uso.

Detalle de cada categoría:

  1. Palabras clave

Descripción: Son elementos reservados del lenguaje de programación que tienen un significado específico y no pueden usarse como identificadores (nombres de variables o funciones).

Ejemplos: if, for, return.

2.      Identificadores

Descripción: Representan nombres definidos por el programador, como nombres de variables, funciones o clases.

Ejemplos: x, miFuncion.

3.       Números

Descripción: Se refieren a literales numéricos, tanto enteros como decimales, que son usados en cálculos o asignaciones.

Ejemplos: 10, 3.14.

4 .      Operadores

Descripción: Son símbolos utilizados para realizar operaciones matemáticas, lógicas o relacionales.

Ejemplos: +, -, =.

5.       Delimitadores

Descripción: Caracteres que marcan límites o separaciones en la estructura del código, como en listas, bloques o instrucciones.

Ejemplos: ;, {, }.

En un lenguaje de programación, palabras reservadas, constantes, identificadores, cadenas de texto, números, operadores y signos de puntuación pueden ser considerados como tokens.  

El propósito principal del analizador léxico es transformar una secuencia de caracteres del código fuente en una secuencia de tokens, que son elementos básicos que el compilador puede interpretar y procesar en etapas posteriores.

Generalmente, el analizador léxico omite los espacios en blanco, las tabulaciones y los comentarios, ya que estos no tienen importancia para la sintaxis del programa, aunque son fundamentales para la legibilidad del código.
En la Figura3 se muestra un programa de una calculadora básica que acepta dos números y un operador, que pueda realizar ya sea una (suma, resta, multiplicación, división) como entrada del usuario y devuelve el resultado. En la Tabla2 se clasifican los tokens de dicho lenguaje.


Figura3.Código de una calculadora básica..Elaborado por el autor.23/Nov/2024
Figura3.Código de una calculadora básica..Elaborado por el autor.23/Nov/2024
Tabla2.Clasificación de tokens.Elaborado por el autor.23/Nov/2024
Tabla2.Clasificación de tokens.Elaborado por el autor.23/Nov/2024

En la imagen anterios se presenta un programa escrito en el lenguaje de programación C que implementa una calculadora básica capaz de realizar operaciones como suma, resta, multiplicación y división. Este lenguaje sigue un conjunto de reglas que el analizador léxico reconoce y verifica al escanear cada línea del código. Durante este proceso, el código se divide en unidades mínimas de información, conocidas como tokens, las cuales son clasificadas según su tipo en la Tabla2. 


Escanear nuestros tokens:

Línea 1: #include <stdio.h>

  • #include: Palabra clave del preprocesador de C para incluir librerías.
  • <stdio.h>: Identificador que indica la librería estándar de entrada/salida.

Línea 3: int main()

  • int: Palabra clave que especifica el tipo de dato que devuelve la función main (en este caso, un entero).
  • main: Identificador que nombra la función principal del programa.
  • (): Delimitadores que indican que es una función.

Línea 4: char operador;

  • char: Palabra clave que indica el tipo de dato (un carácter).
  • operador: Identificador que define el nombre de la variable.

Línea 5: double num1, num2, resultado;

  • double: Palabra clave que indica el tipo de dato (números de punto flotante de doble precisión).
  • num1, num2, resultado: Identificadores de las variables.
  • ;: Delimitador que marca el final de la declaración.

Línea 7-8: printf y scanf

  • printf: Identificador de una función de salida (imprime texto en la consola).
  • scanf: Identificador de una función de entrada (lee datos desde la consola).
  • ": Delimitador que encierra cadenas de texto.
  • %c y %lf: Especificadores de formato para caracteres y números de tipo double, respectivamente.
  • ,: Separador para argumentos de las funciones.
  • ;: Finaliza cada instrucción.

Línea 19: switch (operador)

  • switch: Palabra clave para el control condicional múltiple.
  • ( y ): Delimitadores que encierran la variable a evaluar (operador).
  • operador: Identificador (la variable sobre la que se basa el switch).

Líneas 20-43: case, default, break

  • case: Palabra clave que indica una opción dentro del switch.
  • '+' y demás operadores (-, *, /): Literales que representan los casos posibles.
  • break: Palabra clave que finaliza la ejecución de un caso.
  • default: Caso por defecto si ningún case coincide.

Operaciones y lógica en el switch  

  • resultado = num1 + num2;
  • resultado, num1, num2: Identificadores de variables.
  • =: Operador de asignación.
  • +: Operador aritmético.
  • ;: Delimitador que cierra la instrucción.
  • if (num2 != 0) (en la división):
  • if: Palabra clave para el control condicional.
  • ( y ): Delimitadores que encierran la condición lógica.
  • num2 != 0: Expresión lógica (comprueba que num2 no sea cero).!=: Operador de comparación "distinto de".

Salida: printf

  • El resultado de %.2lf ...: Cadena de texto que utiliza especificadores de formato (%.2lf) para mostrar el resultado con dos decimales.

Línea 45: return 0;

  • return: Palabra clave que indica el valor de retorno de la función.
  • 0: Literal numérico (valor de retorno para indicar que el programa finalizó correctamente).
  • ;: Delimitador que cierra la instrucción.

En la Tabla2 se muestran dichas clasificaciones, lo que facilita la identificación y comprensión de los elementos del programa. 

 Si el compilador detecta un elemento que no tiene sentido o no cumple con las reglas del lenguaje, como un símbolo desconocido, genera un mensaje de error. Esto permite al programador identificar y corregir los errores en el código desde las primeras etapas del desarrollo. 


Comprobación de los tokens con los lenguajes formales, autómatas y el uso de expresiones regulares. 

Para poder empezar, primero hay que recordar qué son los términos de autómatas y cómo se utilizan para comprobar los tokens y sus expresiones regulares.

Expresiones Regulares: Una expresión regular es una notación importante para la especificación de patrones. Cada patrón coincide con un conjunto de cadenas, de modo que las expresiones regulares se utilizan como nombres para un conjunto de cadenas. Se emplean para definir los patrones de los tokens.

En la Tabla3 se representan las diferentes expresiones regulares que se pueden utilizar con la calculadora básica. También se muestran algunos errores que no serán validados por las expresiones.

Tabla3.Expresiones regulares.Elaborado por el autor.23/Nov/2024
Tabla3.Expresiones regulares.Elaborado por el autor.23/Nov/2024

Lo que se muestra en tabla es que relaciona funciones del análisis léxico con expresiones regulares , descripciones y ejemplos. 

1. Reconocer números

  • Expresión Regular: ^[0-9]+(\.[0-9]+)?$
  • ^[0-9]+: Busca números enteros que comienzan con uno o más dígitos (0-9).
  • (\.[0-9]+)?: De manera opcional, puede contener un punto (.) seguido de uno o más dígitos decimales.
  • $: Asegura que toda la cadena cumple con este patrón.
  • Descripción: Detecta números enteros o decimales.
  • Ejemplos Válidos: 123, 45.67.
  • Ejemplos Inválidos: .45, 123., abc (porque no son números válidos en este contexto).

2. Reconocer operadores

  • Expresión Regular: ^[\+\-\*\/]$
  • [\+\-\*\/]: Reconoce cualquiera de los símbolos de los operadores aritméticos básicos: +, -, *, /.
  • ^ y $: Indican que debe ser un operador único en toda la cadena.
  • Descripción: Identifica operadores aritméticos.
  • Ejemplos Válidos: +, -, *, /.
  • Ejemplos Inválidos: ++, %, a (porque no son operadores básicos válidos).

3. Detectar entradas inválidas

  • Expresión Regular: [^0-9.\+\-\*\/]
  • [^...]: Indica un conjunto de caracteres que no son válidos. En este caso, cualquier carácter que no sea un dígito (0-9), un punto (.) o un operador (+, -, *, /).
  • Descripción: Detecta caracteres fuera del rango permitido.
  • Ejemplos Válidos: Ninguno (esta expresión se usa para invalidar entradas, no para validarlas).
  • Ejemplos Inválidos: abc, 1a2, 12+34 (porque incluyen caracteres no válidos como letras).


El análisis léxico se puede representar como un autómata, específicamente un autómata finito determinista (AFD), que pasa por una serie de estados mientras lee los caracteres y reconoce los tokens. Cada estado representa una parte del patrón que se está reconociendo. 

Autómatas Finitos Deterministas: El AFD se utiliza para representar y procesar secuencias de caracteres (o entradas) de manera sistemática, evaluando si cumplen con ciertas reglas definidas.

Una vez que se tiene el AFD, este puede procesar el código fuente carácter por carácter, siguiendo las transiciones de estado. Si el AFD llega a un estado final (de aceptación) después de procesar una secuencia, esa secuencia es un token válido.

Si quieres saber más a detalle sobre el uso de los autómatas, te estoy proporcionando el siguiente video donde se detalla mucho más su uso.

Piñeiro, M. A. (@matematicamaravillosa). (s.f.). Autómatas finitos y máquinas de estados [Video]. YouTube. https://youtu.be/ScR1T1DME14 

En la Figura4 se muestra un ejemplo de un autómata que puede validar expresiones aritméticas y determinar si una cadena de caracteres dada corresponde a una expresión matemática válida, según las reglas que hemos definido. También se muestra su tabla de transiciones para validar cada movimiento del estado inicial al final. 

Figura4.Autómata de expresiones aritméticas simples..Elaborado por el autor.23/Nov/2024
Figura4.Autómata de expresiones aritméticas simples..Elaborado por el autor.23/Nov/2024

El autómata de la Figura4 compone de:

  • Estados: Representados por círculos (S0, S1, S2 y S3). Cada círculo representa un posible estado en el que se puede encontrar el autómata.
  • Transiciones: Representadas por flechas que conectan los estados. Cada flecha indica un cambio de estado en respuesta a una entrada específica.
  • Entradas: Asociadas a las transiciones y escritas sobre las flechas. En este caso, las entradas son dígitos (0-9) y operadores aritméticos (+, -, *, /).
  • Estado inicial: Indicado por una flecha que apunta al estado inicial (S0). Es el estado en el que comienza el autómata.
  • Estado final: Indicado por un doble círculo (S3). Es el estado en el que el autómata termina si la secuencia de entrada es válida.
  • Alfabeto: El conjunto de todos los posibles símbolos de entrada (dígitos y operadores).
  • Validacion de los tokens:

  • Inicio: El autómata comienza en el estado S0.
  • Lectura de dígitos: Si se lee un dígito, el autómata permanece en el estado S1. Esto indica que se está leyendo una secuencia de dígitos.
  • Lectura de operador: Si después de una secuencia de dígitos se lee un operador, el autómata pasa al estado S2. Esto indica que se ha encontrado un operador entre dos números.
  • Lectura de más dígitos: Si después del operador se lee otro dígito, el autómata vuelve al estado S1, esperando más dígitos.
  • Aceptación: Si después de leer un operador y más dígitos, se llega al final de la entrada, el autómata llega al estado final S3, lo que significa que la expresión aritmética es válida.
  • Este AFD reconoce expresiones aritméticas simples que consisten en una secuencia de dígitos, seguida de un operador, y luego otra secuencia de dígitos.  

    Otro ejemplo representado en la Figura5 es un autómata que solo está validando los operadores básicos de nuestra calculadora. 

    Figura5.Autómata de operadores..Elaborado por el autor.23/Nov/2024
    Figura5.Autómata de operadores..Elaborado por el autor.23/Nov/2024

    Esta se compone de los siguientes elementos:

  • Estados: Los círculos S0 y S1 representan los estados posibles del autómata. En un momento dado, el autómata se encuentra en uno y solo uno de estos estados.
  • Transiciones: La flecha que conecta S0 con S1 representa una transición. Esta transición ocurre cuando el autómata lee un símbolo de entrada que pertenece al conjunto {+, -, *, /}.
  • Estado inicial: El estado S0 tiene una flecha entrante que lo señala como el estado inicial. Es decir, el autómata siempre comienza su ejecución en este estado.
  • Estado final: El estado S1 está rodeado por un doble círculo, lo que indica que es un estado final. Si el autómata llega a este estado al final de la entrada, se dice que la entrada ha sido aceptada.
  • Alfabeto: El conjunto de símbolos que el autómata puede leer como entrada está definido como {+, -, *, /}.
  • Proceso de validación:

     Inicio: El autómata comienza en el estado S0.

    Lectura de entrada: El autómata lee un símbolo de la entrada.

    Transición: Si el símbolo leído pertenece al conjunto {+, -, *, /}, el autómata realiza una transición del estado S0 al estado S1.

    Aceptación: Si el autómata llega al estado S1 al final de la entrada, la entrada es aceptada. De lo contrario, la entrada es rechazada.

    Este AFD en particular es muy simple y reconoce cualquier secuencia de símbolos que contenga al menos un operador aritmético (+, -, *, /). En otras palabras, este autómata acepta cualquier cadena que tenga al menos uno de estos símbolos.


    Referencias:

    Autómatas, L. Y. (s. f.). Lenguajes y autómatas I. https://lenguajesyautomatas1998.blogspot.com/2019/04/unidad-4-4.html

    Compilador Diseño - expresiones regulares. (s. f.). https://www.tutorialspoint.com/es/compiler_design/compiler_design_regular_expressions.htm


    ¡Crea tu página web gratis! Esta página web fue creada con Webnode. Crea tu propia web gratis hoy mismo! Comenzar