View
268
Download
5
Category
Preview:
Citation preview
Programación de sistemas
Unidad 4 Análisis léxico
Contenido Función del analizador léxico
Aspectos del análisis léxico Componentes léxicos, patrones y lexemas Errores léxicos
Manejo de los buffers de entrada Parejas de buffers Centinelas
Especificación de los componentes léxicos Cadenas y lenguajes Operaciones aplicadas a lenguajes Expresiones regulares Definiciones regulares Abreviaturas de notación
Reconocimiento de los componentes léxicos Diagramas de transición Implantación de un diagrama de transiciones
Función del analizador léxico
El análisis léxico constituye la primer fase de cualquier traductor.
Su función principal consiste en leer los caracteres de entrada y elaborar como salida una secuencia de componentes léxicos, los cuales serán utilizados por un analizador sintáctico.
Función del analizador léxico (2)
Otras funciones secundarias que también realiza un analizador léxico cuando forma parte de un compilador son: Eliminar los comentarios del programa Eliminar los espacios en blanco, tabuladores, retorno de
carro, y en general todo aquello que carezca de significado según la sintaxis del lenguaje.
Reconocer identificadores de usuario, números, palabras reservadas del lenguaje, etc. y tratarlos correctamente con respecto a la tabla de símbolos.
Llevar la cuenta del número de línea que se esta leyendo. Avisar de errores léxicos. Puede realizar funciones de preprocesador
Aspectos del análisis léxico Separar el análisis léxico del sintáctico tiene varias razones:
Permite un diseño sencillo Esta es la consideración más importante Simplifica ambas fases, pero sobre todo simplifica el análisis
sintáctico. Se construyen dos gramáticas una para el análisis léxico y otra para
el sintáctico. Se mejora la eficiencia del compilador
Contando con un procesador léxico especializado y potencialmente más eficiente.
Utilizando técnicas especializadas para el manejo de buffers. Se mejora la transportabilidad del compilador
Limitando al analizador léxico las peculiaridades del alfabeto y los dispositivos de entrada.
Componentes léxicos, lexemas y patrones Token (componente léxico)
Conjunto de secuencias de caracteres con la misma misión sintácticamente.
Es el terminal asociado a un patrón. Lexema
Lo conforman una secuencia de caracteres que encajan con un patrón.
Secuencia de caracteres que forman un token. Patrón
Regla o reglas que describen a los lexemas pertenecientes a un token.
Es una expresión regular Una vez que se ha detectado una secuencia de caracteres que
coincide con un patrón, se a detectado un lexema y debe reportarse un número que lo identifica como un cierto tipo de token.
Errores léxicos
Cuando se trata de analizar un programa fuente el analizador léxico tiene una visión restringida, por lo cual son pocos los errores que se pueden detectar.
Sin embargo se debe seguir alguna estrategia para detectar, informar y recuperarse de los errores.
Cuando un error ocurre: Es inaceptable si el analizador falla, o se entra en un ciclo
infinito. Es una buena estrategia evitar la avalancha de errores, y
más aún se debería no solo informar sino tratar de recuperarse del error.
Errores léxicos (2)
El analizador léxico tiene el mismo comportamiento que un Autómata finito, así la detección de un error se da cuando no existe transición alguna desde el estado actual a otro con el símbolo actual de la entrada.
Bajo el enfoque anterior, algunas de las estrategias posibles son: Ir al estado inicial ignorando los símbolos leídos para el
lexema. Modo panic, se ignora todo lo desconocido y no se avanza
hasta encontrar un símbolo que nos lleve a otro estado. Intentar reparar los errores en los lexemas al borrar,
insertar, intercambiar y sustituir símbolos.
Manejo de los buffers de entrada Como el analizador léxico es quien lee el programa fuente desde
almacenamiento secundario (por lo regular, caracter a caracter), es probable que se consuma mucho tiempo en esta fase.
Es importante considerar el manejo de los buffers de entrada para no demeritar la eficiencia de los traductores, más aún de los compiladores.
Existen tres métodos para la implementación de un analizador léxico. Usar un generador de analizadores léxicos (LEX). Codificar el analizador léxico en un lenguaje convencional
(lenguaje C). Manejo explícito de la entrada usando lenguaje ensamblador. Las tres opciones se relacionan en orden de dificultad creciente,
sin embargo, las implementaciones de mayor dificultad son generalmente las más rápidas.
Parejas de buffers Durante el análisis léxico, en la mayoría de los casos, es necesario
preanalizar varios caracteres antes de anunciar una concordancia. Para no leer caracter a caracter de la entrada, se utiliza un buffer
dividido en dos mitades de N caracteres cada una (N = Tam. Bloque en disco).
Se leen N caracteres de entrada en una de las mitades y otras N en la segunda mitad (siempre que sea posible).
Dos apuntadores al buffer son necesarios, uno señala el lexema en curso y otro el siguiente. Cuando el apuntador delantero esta por sobrepasar la marca de una de las mitades se leen N caracteres de la entrada nuevamente para la otra mitad.
L e x 1 l e x 2 l e x 3 l e x 4
0 N 0 N
Lexema_en_cursoDelantero
if delantero está al final de la primera mitad then begin recargar la segunda mitad; delantero := delantero + 1;endelse if delantero está al final de la segunda mitad then begin recargar la primera mitad; pasar delantero al principio de la mitadendelse delantero := delantero + 1;
Centinelas El uso de centinelas evita la necesidad de hacer
pruebas cada vez que avanza el apuntador delantero.
La técnica consiste en ampliar cada mitad del buffer para admitir un caracter centinela al final (comúnmente EOF).
Lex1 lex2 eof lex3 lex4 eof eof
0 N N+10 N N+1
Lexema_en_cursoDelantero
delantero := delantero + 1;if delantero = eof then begin if delantero está al final de la primera mitad then begin recargar la segunda mitad; delantero: = delantero + 1; end else if delantero está al final de la segunda mitad then begin recargar la primera mitad; pasar delantero al principio de la primera mitad; end else /* eof dentro de un buffer significa el final de la entrada */ terminar el análisis léxico;end
Especificación de los componentes léxicos
Las expresiones regulares son una notación importante para especificar patrones.
Cada patrón concuerda con una serie de cadenas, de modo que las expresiones regulares servirán como nombres para conjuntos de cadenas.
Los temas relacionados con la especificación de componentes léxicos que se tratarán son: Cadenas y lenguajes Operaciones aplicadas a lenguajes Expresiones regulares Definiciones regulares Abreviaturas de notación
Cadenas y lenguajes Alfabeto o clase de carácter
Se utiliza para denotar cualquier conjunto finito de símbolos. Ejemplos:
Letras y caracteres El conjunto {0,1} es el alfabeto binario
Cadena sobre algún alfabeto Es una secuencia finita de símbolos tomados de ese alfabeto. Frase y palabra se utilizan como sinónimos de cadena La longitud de una cadena s (|s|) es el número de símbolos en
ella. La cadena vacía tiene longitud 0. Lenguaje
Se refiere a cualquier conjunto de cadenas de un alfabeto. Ejemplos
El conjunto que solo tiene la cadena vacía {ε} El conjunto de todos los programas escritos en C.
Operaciones aplicadas a lenguajes
Unión de L y M
Concatenación de L y M
Cerradura de Kleene de L
Cerradura positiva de L
},|{ MsoLssML
},|{ MtyLsstLM
i
iLL
0
*
i
iLL
1
Ejemplos:
L = {A, B, …, Z a, b, …, z} yD = {0, 1, …, 9}
1.- L U D es el conjunto de letras y dígitos2.- LD es el conjunto de cadenas que consta de una letra seguida de un dígito3.- L4 es el conjunto de todas las cadenas de cuatro letras4.- L* es el conjunto de todas las cadenas, incluyendo la cadena vacía.5.- L(L U D)* es el conjunto de todas las cadenas de letras y dígitos que comienzan con una letra.6.- D+ es el conjunto de todas las cadenas de uno o más dígitos.
Expresiones regulares Son una notación que permite definir de forma precisa conjuntos.
Ej. Un identificador letra ( letra | dígito) *
Una expresión regular se compone de un conjunto de reglas definitorias más simples.
Las reglas que definen las expresiones regulares de un alfabeto Σ son: ε es una expresión regular designada por {ε}; es decir, el conjunto que
contiene la cadena vacía. Si a es un símbolo de Σ, entonces a es una expresión regular designada por
{a}. Suponiendo que r y s sean expresiones regularse representadas por los
lenguajes L(r) y L(s), entonces: (r) | (s) es una expresión regular representada por L(r) U L(s) (r)(s) es una expresión regular representada por L(r)L(s) (r)* es una expresión regular representada por (L(r))* (r) es una es una expresión regular representada por L(r)
Se dice que el lenguaje regular designado por una expresión regular es un conjunto regular.
Expresiones regulares (2)
Ejemplo Sea Σ = {a,b}
a | b designa el conjunto {a, b} (a | b)(a | b) designa el conjunto {aa, ab, ba, bb} a* designa el conjunto {ε, a, aa, aaa, …} (a | b)* es equivalente a (a*b*)*
Propiedades algebraicas de las expresiones regulares:
Definiciones regulares Es común dar un nombre a una expresión regular, dicho nombre
es usado como si fuera un símbolo de Σ. Si Σ es un alfabeto de símbolos básicos, entonces una definición
regular es una secuencia de definiciones de la forma:d1 → r1
d2 → r2
. . .dn → rn
Donde cada di es un nombre distinto, y cada ri es una expresión regular sobre los símbolos de Σ U {d1, d2, …, di-1}.
Para distinguir el nombre de una expresión regular de los símbolos, se debe hacer una distinción al escribirlo (por ejemplo en negritas).
Ejemplo: Dígito → 0 | 1 | … | 9 NumEp → Dígito Dígito*
Abreviaturas de notación Convenientes para construcciones frecuentes en una expresión
regular Uno o más casos
Se utiliza el operador + Ejemplo:
a+ representa el conjunto de todas las cadenas de una o más a. Cero o un caso
Se utiliza el operador ? Ejemplo:
- ? Digito + representa cadenas que forman números enteros positivos y negativos.
Clases de caracteres Se utiliza la notación [abc], donde a, b y c son símbolos de Σ. Ejemplos:
[abc] representa uno de los caracteres a, b o c [a-z] representa cualquier letra minúscula del alfabeto
Reconocimiento de componentes léxicos
El objetivo es construir un analizador léxico que aísle el lexema para el siguiente token del buffer de entrada y que produzca como salida un par formado por el token y el valor del atributo correspondiente al lexema.
Dicho de otra manera, el analizador léxico por cada token reconocido informa dos cosas: El tipo de token encontrado (ej. ID, NUMERO), y De cuál token en particular se trata (ej. el lexema
[“varialbe”, 12] o un apuntador a la tabla de símbolos).
Diagramas de transiciones
Son un paso intermedio en la construcción de un analizador léxico.
Representan las acciones que se realizan cuando el analizador léxico es llamado por el sintáctico.
Se trata de AFD, donde los nodos son las posiciones, es decir, los estados, y las aristas están etiquetadas con un símbolo del lenguaje.
9 10 11inicio letra
letra / digito
otro
*
Retorna (reservada, id)
Implantación de un diagrama de transiciones A cada estado le corresponde un segmento de código. Si hay aristas que salen de un estado, entonces su código lee un
caracter y selecciona una arista para seguir, si es posible. La implementación se puede realizar por medio de tablas (matrices
indexadas por caracteres), o bien por medio de sentencias case. Ej. case
switch (estado){…
case 9: c = sigcar(); if( esletra(c)) estado = 10; …
break;case 10: c = sigcar();
if(esletra(c)) estado = 10; else if(esdigito(c)) estado = 10;
else estado = 11;break;case 11: regresa(1);
return instalaid();break;
…9 10 11
inicio letra
letra/ digito
otro*
instalaid ();
Recommended