0% encontró este documento útil (0 votos)
121 vistas27 páginas

Análisis Léxico con LEX y FLEX

Este documento describe el análisis léxico y el uso del generador de analizadores léxicos LEX. Explica la funcionalidad del analizador léxico, la especificación de símbolos mediante expresiones regulares, y el diseño e implementación de un analizador léxico con LEX, incluyendo la especificación de expresiones regulares, funciones y el manejo de errores.

Cargado por

tiaka23
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPT, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
121 vistas27 páginas

Análisis Léxico con LEX y FLEX

Este documento describe el análisis léxico y el uso del generador de analizadores léxicos LEX. Explica la funcionalidad del analizador léxico, la especificación de símbolos mediante expresiones regulares, y el diseño e implementación de un analizador léxico con LEX, incluyendo la especificación de expresiones regulares, funciones y el manejo de errores.

Cargado por

tiaka23
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPT, PDF, TXT o lee en línea desde Scribd

Compiladores

Unidad 2. Análisis léxico


Contenido
 Funcionalidad del analizador léxico
 Visión general de LEX
 Especificación y reconocimiento de los símbolos de un
lenguaje
 Diseño e implementación de un analizador léxico
 Formato del código fuente (LEX)
 Especificación de expresiones regulares (LEX)
 Funciones y variables proporcionadas (LEX)
 Especificación de las acciones léxicas (LEX)
 Manejo de especificaciones ambiguas (LEX)
 Especificación de la sensibilidad del contexto izquierdo (LEX)
 Control de errores léxicos
Funcionalidad del analizador
léxico
 El analizador léxico es la primera fase del compilador.
 Se encarga de dividir el programa fuente en un conjunto de
unidades sintácticas llamadas tokens.
 También realiza otras tareas auxiliares
 Tratamiento de comentarios
 Eliminación de blancos y símbolos especiales
 Manejo de errores léxicos
 Interacción con la tabla de símbolos

Programa fuente Token


Analizador Analizador
léxico sintáctico
Obtener Token

Tabla de
símbolos
Funcionalidad del analizador
léxico (2)
 El analizador léxico suele ser una rutina del
analizador sintáctico, esta separación tiene sus
razones:
 Se genera un diseño más sencillo
 Si las distintas fases del compilador trataran directamente
con los caracteres se complicaría el análisis de la
estructura del lenguaje fuente.
 Se mejora la eficiencia del compilador
 Gran parte del tiempo se consume leyendo el programa de
entrada, la separación permite optimizar la lectura.
 Se mejora la portabilidad del compilador
 Las particularidades del alfabeto pueden aislarse y tratarse
en el analizador léxico (ej. :=, =, ==).
Visión general del analizador
lexicográfico LEX
 El LEX no es un lenguaje sino un generador de
analizadores léxicos, en otras palabras un
generador de programas.
 La entrada para LEX se especifica en términos de
expresiones regulares y rutinas asociadas a ellas; la
salida es un programa en C.
 El programa en C generado por LEX recibe un flujo
de caracteres de entrada, el cuál se secciona para
extraer cadenas que empaten con las expresiones
regulares, detectado el empate se ejecuta la rutina
asociada.
 Es responsabilidad del programador definir las
expresiones regulares utilizando la sintaxis de LEX
y las rutinas asociadas a ellas en lenguaje C.
Visión general del analizador
lexicográfico LEX (2)
 El LEX se puede utilizar en:
 Transformaciones sencillas
 Análisis estadístico a nivel léxico
 La fase de análisis léxico de un traductor (compilador o
intérprete).
 Es común utilizar LEX y YACC cuando se diseñan traductores.
LEX es capaz de reconoce expresiones regulares (tokens) y
YACC empata un conjunto de expresiones regulares (tokens)
con gramáticas libres de contexto. Así lo que produce LEX se
convierte en la entrada de YACC.
Expresiones regulares Reglas gramaticales
y rutinas asociadas

LEX YACC
Flujo de caracteres tokens Flujo aceptado
de entrada yylex() yyparse() y traducido
Visión general del analizador
lexicográfico LEX (3)
 Pasos para crear un analizador léxico con LEX
 Construir la especificación LEX
 Programa fuente de LEX en términos de expresiones
regulares y rutinas asociadas a ellas.
 Compilar la especificación
 Utilizando PCLEX (MS-DOS/Windows) o LEX (Linux) se
genera programa en C.
 Compilar el programa generado por LEX en algún
compilador de C.
 En Linux gcc, en Windows Dev-cpp

Expresiones, Acciones PCLEX Analizador léxico gcc Programa de análisis


(Progesp.l) o (yylex.c) o Léxico
FLEX Dev-cpp ([Link] o [Link])
Especificación y reconocimiento
de los símbolos de un lenguaje
 Tres términos importantes:
 Token (componente léxico)
 Conjunto de cadenas para la cual se produce como salida el mismo
componente léxico
 Patrón
 Describe las reglas para concatenar los caracteres de las que forman
los tokens.
 Lexema
 Es una secuencia particular de caracteres que concuerda con el
patrón especificado para un token.
 Ejemplos de Token, Lexema y Patron:
 Token: if; Lexema: if; Descripción del patrón: una i seguida de una f
 Token: id; lexemas: pi, contador, a1; Descripción del patrón: una letra
seguida de letras o dígitos.
Especificación y reconocimiento de
los símbolos de un lenguaje (2)
 Las expresiones regulares son una notación importante para
especificar patrones.
 Las expresiones regulares servirán como nombres para conjuntos de
cadenas.
 Cuando se trabaja con expresiones regulares se deben tener en cuenta
los siguientes términos:
 El término alfabeto o clase de carácter denota cualquier conjunto finito de
símbolos.
 Ejemplos típicos de símbolos son: letras y caracteres, el conjunto de 0’s y 1’s,
el código ASCII.
 Una cadena sobre algún alfabeto es una secuencia finita de símbolos
tomados de ese alfabeto.
 La longitud de una cadena esta determinada por el número de símbolos que
aparecen en ella; la cadena vacía en la única de longitud cero .
 El término lenguaje se refiere a cualquier conjunto de cadenas de un
alfabeto fijo.
 Un metasímbolo o metacaracter son caracteres inmersos en la
especificación de una expresión regular y tienen un significado especial.
 Existen caracteres de escape que inhabilitan el propósito del metasímbolo.
Especificación y reconocimiento de
los símbolos de un lenguaje (3)
 Definición de expresiones regulares
 Expresiones regulares básicas
 Los caracteres del propio alfabeto los cuales se corresponden a sí mismos.
 La cadena vacía, es decir la cadena que no contiene ningún carácter.
 Operaciones de expresiones regulares
 Selección entre alternativas
 Indicada por el operador | (barra)
 Si r y s son expresiones regulares, entonces r|s es una expresión regular que
concuerda con r o con s.
 En términos de lenguajes: L(r|s) = L(r)  L(s).
 Concatenación
 Se indica mediante la yuxtaposición (sin un metasímbolo)
 En términos de lenguajes L(rs) = L(r)L(s).
 Repetición o cerradura
 Si Se indica mediante el carácter * (asterisco)
 Sea r una expresión regular, entonces r* denota cualquier concatenación finita de
cadenas cada una de las cuales corresponde a r incluyendo la cadena vacía
 En términos de lenguajes L(r*) = L(r)*
 La cerradura positiva se indica mediante el metasímbolo +; no incluye la cadena
vacía.
Especificación y reconocimiento de
los símbolos de un lenguaje (4)
 Propiedades algebraicas de las expresiones
regulares:
Especificación y reconocimiento de
los símbolos de un lenguaje (5)
 El objetivo del reconocimiento léxico es empatar el
flujo de la entrada con el patrón especificado en
alguna expresión regular, como resultado de este
empate se obtiene un par formado por el token
apropiado y el valor asociado (el lexema).
 El generador de analizadores LEX toma como
entrada un conjunto de expresiones regulares y
genera como salida código fuente que en realidad
es la implementación de un DFA correspondiente a
dichas expresiones regulares.
Expresiones, Acciones PCLEX Analizador léxico
(Progesp.l) o (yylex.c)
FLEX

Implementación de un
Autómata Finito Determinista
(DFA)
Diseño e implementación de
un analizador léxico
 El diseño del analizador léxico estará en función de
aquellos tokens que se quieran reconocer y el
propósito en sí de dicho reconocimiento.
 La implementación puede realizarse de dos formas:
 Transformar las expresiones regulares en NFA y los NFA
en DFA, minimizar los DFA y por último codificar el DFA
minimizado utilizando variables de estado o tablas de
transiciones.
 Utilizar un generador de analizadores lexicográficos dando
como entrada las expresiones regulares y que el
generador produzca el código del DFA.
Formato del código fuente LEX
 El formato general de una especificación LEX contiene:
 Definiciones
 Puede contener de forma opcional código en C (inclusión de librerías, declaración de
variables y constantes simbólicas).
 También contiene definiciones de expresiones regulares (NombreExpresión
ExpresiónRegular)
 Reglas
 Contiene los patrones a reconocer y las acciones (llamados a función) que se han de
realizar cuando se reconozca un patron.
 Funciones
 Contiene, cuando es necesario, las definición o implementación de las rutinas asociadas
a las reglas.
 También es el lugar de la función main.
 Para separar secciones se utiliza %% y para enmarcar el código opcional en C
%{ %}, un esqueleto de especificación LEX seria:
%{
/*Código opcional*/
%}
/*Definiciones*/
%%
/*Reglas*/
%%
/*Funciones*/
 Sin embargo el mínimo programa fuente LEX puede constar de solo %% lo cuál
significaría que la entrada pasa a ser la salida tal cual.
Formato del código fuente LEX (2)
 Un primer ejemplo:
 Contar cuantos caracteres tiene un archivo y cuantos saltos de
línea.
Compilación con PCLEX:
Programa LEX [Link] eje1.l
%{ Una vez compilado en Dev-cpp
#include<stdlib.h>
#include<stdio.h> Su ejecución podría ser:
#include<string.h> [Link] < eje1.c

int num_lin = 0, num_car = 0;


%}
%%
Compilación con lex:
[\n] num_lin++; lex eje1.l
. num_car++; Compilación con gcc:
%%
gcc -o [Link] [Link].c –ll
int main()
{ Ejecución en Linux:
yylex(); [Link] < [Link].c
printf("Numero de lineas %d\n",num_lin);
printf("Numero de caracteres %d\n",num_car);

return 0;
}
Especificación de expresiones
regulares
 Una expresión regular especifica un conjunto de cadenas a ser empatadas.
Cada expresión regular consta de caracteres y operadores.
 Los operadores que se aplican sobre los caracteres son:
 “” (comillas)
 \ (diagonal invertida)
 [] (corchetes)
 ^ (acento circunflejo )
 - (guión)
 ? (interrogación)
 . (punto)
 * (asterisco)
 + (más)
 | (barra)
 () (paréntesis)
 $ (pesos)
 / (diagonal)
 {} (llaves)
 % (porcentaje)
 <> (menor y mayor que)
 Cuando es necesario usar alguno de ellos como texto dentro de la expresión
regular se debe utilizar una secuencia de escape o debe estar entre comillas.
Especificación de expresiones
regulares (2)
 Operadores comillas (“ ”) y diagonal invertida (\)
 El “ ” especifica que lo que este dentro de ellas no forma parte
de operador alguno de LEX y se debe tomar la secuencia de
caracteres tal cual. Por ejemplo:
 xyz“++” empata con xyz++
 de igual forma se puede especificar “xyz++”
 El operador \ tiene una función similar a las comillas, solo que se
aplica a un caracter a la vez (secuencia de escape). Así para el
ejemplo anterior se tendría:
 xyz\+\+
 Otro uso común del operador \ es entre corchetes para indicar
los espacios en blanco, tabuladores, saltos de línea e inclusive
backspace. La sintaxis es igual a las secuencias de escape
utilizadas en C. Así
 [\ ] Especifica un espacio en blanco
 [\t] Especifica una tabulación
 [\n] Especifica un salto de línea
 [\b] Especifica un backspace
Especificación de expresiones
regulares (3)
 Clases de caracteres y caracteres arbitrarios
 Las clases de caracteres se pueden especificar utilizando el operador
corchetes ([ ]). Por ejemplo:
 [abc] coincide con cualquier caracter que pueda ser una a, una b o una c.
 Dentro del operador [ ] la mayoría de los otros operadores son ignorados, con
excepción de:
 Diagonal invertida (\)
 Guión (-)
 Acento circunflejo (^)
 El operador - especifica rangos. Por ejemplo:
 [a-z] especifica la clase de caracteres que contiene todos los caracteres en
minúsculas.
 Los rangos por lo regular se especifican para letras minúsculas, mayúsculas o
dígitos.
 Si se desea incluir el operador - como parte de una clase de caracteres se debe
poner al principio o al último. Por ejemplo:
 [-+0-9] coincide con todos los dígitos de signo más o menos.
 El operador ^ especifica exclusión. Debe aparecer exactamente después del
primer corchete. Por ejemplo:
 [^abc] especifica todos los caracteres excepto a, b y c.
 La especificación de un caracter arbitrario se hace mediante el operador
punto (.) que representa cualquier caracter excepto el salto de línea.
Especificación de expresiones
regulares (4)
 Expresiones opcionales, repetidas y alternadas
 Una expresión opcional se especifica mediante el operador
interrogación (?).
 La expresión ab?c coincide con las secuencias ac y abc.
 La repetición de expresiones se hace mediante los operadores
más (+) y asterisco (*).
 + especifica una o más apariciones
 [0-9]+ coincide con todos los enteros no signados posibles.
 * especifica cero o más apariciones
 [a-z]* coincide con todas las cadenas de letras minúsculas incluyendo la
cadena vacía o nula.
 La alternación y el agrupamiento son especificadas mediante los
operadores de barra (|) y paréntesis (( )) respectivamente
 ab|cd coincide con las secuencias ab o cd
 (ab|cd+)?(ef)* ¿con qué cadenas empata?
Especificación de expresiones
regulares (5)
 Especificación de sensitividad al contexto
 LEX reconoce una pequeña cantidad del contexto que
rodea a una expresión. Ello se da cuando se usan los
operadores pesos ($), acento circunflejo (^) y diagonal (/).
 Cuando se utiliza el operador ^ al principio de una
expresión, la expresión concuerda si se encuentra al
principio de una línea o de la entrada. Lo cual tiene un
significado distinto que cuando el mismo operador esta
entre [ ].
 Cuando se utiliza el operador $ al final de la expresión
coincide solo si esta al final de la línea, es decir, precedida
del salto de línea. De hecho el operador $ es un caso del
operador /.
 El operador / especifica el contexto al final (a la derecha).
Por ejemplo:
 ab/cd coincide con ab siempre que este precedido de cd.
 Así ab$ también puede especificarse como ab/\n
Especificación de expresiones
regulares (6)
 Especificación de definiciones y repeticiones
 El operador llaves ({ }) especifica:
 Definición de expansión si entre ellos se incluye el nombre
de un patrón definido anteriormente. Por ejemplo:
 digito [0-9] especifica un dígito
 {digito}+ especifica cualquier número entero sin signo.
 Repeticiones si entre el operador se incluyen números.
 a{m,n} especifica que a aparece de m a n veces. Por ejemplo:
 a{1,5} busca de una a cinco apariciones de a.

 Finalmente el operador porcentaje (%) se usa


especialmente para separar los segmentos del
código fuente de LEX.
Funciones y variables
proporcionadas por el analizador
Especificación de las acciones
léxicas
 Las acciones representan la consecuencia de que el analizador léxico haya
reconocido un determinado lexema correspondiente a un patrón.
 Existe una acción por defecto, la cual consiste en copiar la entrada
directamente a la salida.
 También se puede ignorar la entrada. Por ejemplo:
 [\t \n] ; Ignora los tres caracteres para espaciado
 Otra forma de hacer lo mismo es usando el caracter de repetición de acción |
 “ ” |
 “\t” |
 “\n” ;
 Cuando se requiere conocer el texto actual de un lexema encontrado se puede
utilizar la variable yytext. Por ejemplo:
 [a-zA-Z]+ printf(“%s”,yytext);
 Dado que el ejemplo anterior es muy común existe una forma simplificada de hacer lo
mismo utilizando ECHO.
 [a-zA-Z]+ ECHO;

 Si son necesarias más de una sentencia para especificar las acciones léxicas
deben de usarse llaves ({ })
Manejo de especificaciones
ambiguas
 LEX tiene la capacidad de manejar especificaciones
ambiguas de las siguiente forma:
 Se toma la coincidencia más larga
 Si se coincide exactamente con dos especificaciones se
toma la que se haya definido primero (ojo con esto).
 Si se requiere validar todas las alternativas se utiliza la
acción REJECT, lo cual reajusta adecuadamente el
puntero de la entrada para validar la siguiente alternativa.
Ejemplo 1

a[cd]+ {ECHO; printf(“Primera especificación \n”);}


a[bc]+ {ECHO; printf(“Segunda especificación \n”);}

Ejemplo 2 Ejemplo 3 (uso de REJECT)

el num_el++; el {num_el++; REJECT}


ella num_ella++; ella {num_ella++; REJECT}
Especificación de la sensibilidad
del contexto izquierdo
 Existen tres formas de tratar este problema. En
cada caso, hay órdenes que reconocen y reflejan la
necesidad de cambiar el entorno en el cual se
analiza el texto de entrada.
 Uso de flags
 Se utilizan en el código de la acción léxica.
 Prácticamente se libera a LEX del problema.
 Uso de condiciones start con órdenes
 Es LEX el que se encarga de tratar con la sensibilidad
izquierda a través de condiciones start.
 Uso de varios analizadores léxicos funcionando juntos
 A veces se da una mayor claridad si se utilizan diferentes
analizadores léxicos y cambiar de uno a otro según se
requiera.
Especificación de la sensibilidad del
contexto izquierdo (2)
 Ejemplo:
 Copiar la entrada a la salida sustituyendo la palabra hola por “primero”
cuando la línea donde se encuentre inicie con la letra a, por “segundo”
cuando la línea inicie con la letra b, por “tercero” cuando la línea inicie
con la letra c. Todas las demás palabra y todas la líneas no varían.

Utilizando flags Utilizando condiciones start


%{
%START AA BB CC
int flag =‘\0’;
%%
%}
^a {ECHO; BEGIN AA;}
%%
^b {ECHO; BEGIN BB;}
^a {flag = ‘a’; ECHO;}
^c {ECHO; BEGIN CC;}
^b {flag = ‘b’; ECHO;}
\n {ECHO; BEGIN 0;}
^c {flag = ‘c’; ECHO;}
<AA>hola {printf(“primero”);}
\n {flag = ‘\0’; ECHO}
<BB>hola {printf(“segundo”);}
hola switch(flag){
<CC>hola {printf(“tercero”);}
case ‘a’:
printf(“primero”); flag =‘\0’; break;
case ‘b’:
printf(“segundo”); flag =‘\0’; break;
case ‘c’:
printf(“tercero”); flag =‘\0’; break;
default: ECHO; break;
}
Control de errores léxicos
 El analizador léxico puede detectar determinados tipos de error:
 Símbolo no permitido, es decir, el símbolo no pertenece al
alfabeto del lenguaje fuente.
 Identificador mal construido o que excede la longitud máxima
permitida.
 Constate numérica mal construida o que excede la longitud
máxima permitida.
 Constante literal mal construida.
 Pueden existir errores imposibles de detectar
 Las palabras reservadas mal escritas se toman como
identificadores
 El tratamiento de estos errores puede tratarse de tres formas:
 Notificar el error y detener todo análisis
 Intentar recuperarse del error y continuar con el análisis
 La recuperación puede darse mediante la inserción o eliminación de
símbolos.
 Dejar el error en control del análisis sintáctico

También podría gustarte