2. Análisis Léxico 2.1 Utilidad del análisis léxico Se puede utilizar para interpretar código HTML, para interpretar un archivo de tipo .ini, etc. El analizador léxico lee del fichero de entrada los caracteres secuencialmente y los agrupa en tokens con un significado conocido por el programador. Hace las funciones de preprocesador también, ya que elimina los caracteres innecesarios para el proceso de compilación. Permite obtener estructuras similares aun cuando los caracteres de entrada no lo sean. En resumen: Agrupa caracteres según categorías establecidas por la especificación del lenguaje fuente. Rechaza texto con caracteres ilegales o agrupados según un criterio no especificado. 2.2 Funcionamiento El funcionamiento del analizador léxico es: Construir tokens validos a partir de la lectura carácter a carácter del fichero de entrada. Pasar tokens validos al analizador sintáctico. Gestionar el manejo del fichero de entrada. Ignorar los espacios en blanco, comentarios y demás caracteres o tokens innecesarios ara el proceso de análisis. Avisar de los errores encontrados a esta fase. Llevar la cuenta del número de línea para incluirlos en el aviso de error. Hacer las funciones de preprocesador. Durante el proceso, el analizador léxico guarda los caracteres que lee en el buffer. Cuando encuentra un carácter que no le sirve se detiene y envía los caracteres al analizador sintáctico, mientras espera una nueva petición de lectura. Cuando la recibe, limpia el buffer y vuelve a leer el carácter donde se detuvo la vez anterior. 2.3 Términos utilizados Patrón: Representación lógica de una serie de agrupación de caracteres con características comunes. Se utilizan expresiones regulares, que son una manera de ver las entradas válidas para un autómata, por ejemplo, un identificador, que es cualquier combinación de letras y números que no comiencen con un número. Lexema: Cada una de las combinaciones de caracteres que encajan en la definición de un patrón. En el ejemplo de un identificador, “variable1”, “x”, “y12” encajan perfectamente. Token: Nombre que se le da a cada patrón definido. Representa lexemas. Atributo: Características propias al tipo del token. 2.4 Especificación del analizador léxico Para comprender el funcionamiento del analizador léxico, se especifica como una máquina de estados, llamada diagrama de transiciones o DT con las siguientes características. Debe leer la secuencia hasta completar un token y retornar luego ese token, dejando la entrada preparada para otro token. Cada secuencia no determinada es un error. Los estados de aceptación deben ser terminales. Cuando se lee un carácter que no pertenezca a ninguna secuencia especificada, se debe ir a un estado especial terminal y volver el cursor de lectura de caracteres al carácter siguiente a la secuencia correcta. 2.5 Construcción de un analizador léxico Hay algunas maneras de construir un analizador léxico, puede ser utilizando diagramas de transiciones o utilizando programas especiales que construyen analizadores a partir de pocas líneas de especificación. 2.5.1 Identificar las palabras reservadas Se tiene que poder diferenciar un identificador de una palabra reservada por lo que se pueden llevar a cabo, al menos, dos métodos: Hacer una tabla con todas las palabras reservadas y consultarla para cada identificador y ver si está en la tabla o no. Implementar cada una de las palabras reservadas en el DT para diferenciarlas del resto de identificadores, haciendo complicada su programación. 2.5.2 Construir el diagrama de transiciones A partir del autómata es relativamente sencillo llegar a la implementación del algoritmo de reconocimiento. Un primer paso es representar el autómata en una tabla de transiciones. En el ejemplo de hacer un analizador léxico para un lenguaje que reconoce números enteros sin signo, suma, incremento y producto, se deben realizar los siguientes pasos. Definir patrones o expresiones regulares: Entero ::= (“0” | “1” | “2” | “3” | “4” | “5” | “6” | “7” | “8” | “9”) + [El + indica uno o más de uno] Suma ::= (“+”) Producto ::= (“*”) Incremento ::= (“++”) Una vez definidos los patrones, se crea el autómata que los reconoce: Los estados de aceptación están marcados con un circulo doble, en mayúsculas el nombre del token, el asterisco indica que el puntero que señala la lectura del siguiente carácter debe retroceder una unidad. Cuando se llega a un estado de aceptación, se pasa el token al analizador sintáctico y se esperaría una nueva petición de este para comenzar otra vez en el estado 0 del autómata. Una vez realizado esto, y que funciona correctamente, se crea una tabla de transiciones la cual consta de tantas filas como estados el autómata y de columnas para numerar estado, las posibles entradas, para señalar el token y para numerar los retrocesos que hay que hacer. Una vez obtenida la tabla, la implementación obtiene cada estado buscando el estado que hay en la fila correspondiente al estado actual y la entrada actual. Este proceso continua hasta llegar a un estado de aceptación o de error. Si es aceptación devolverá el token con los caracteres acumulados, si hay retroceso se retrocederá el cursor de selección de entrada tantas unidades como se indique. Se borra el buffer y se comienza en el estado 0. Si se llega a un estado de error se manda el error correspondiente. Hay dos casos en que hay que decidir entre más de un token, uno es cuando hay dos lexemas, uno más largo que otro y comienzan igual, y otro cuando el mismo lexema puede asociar a más de un token, entonces se elegirá el primero que se encuentre en la especificación