Transparencias - Página personal de César Ignacio García Osorio

Anuncio
Introducción (1)
flex
! El análisis léxico es una de las primeras fases de la
etapa de análisis.
! En la construcción de analizadores léxico se pueden
seguir dos enfoques:
" Usar un lenguaje de programación como C y programar el
analizador léxico.
" Usar una herramienta como lex.
! La principal ventaja que ofrece el uso de lex es que
facilita y acorta el desarrollo. Además como el fuente
lex para un analizador léxico es más corto que el
fuente del programa C, los errores, de producirse, son
más fáciles de localizar.
! El analizador léxico lee los caracteres individuales del
lenguaje fuente y los agrupa en entidades básicas
llamadas tokenes.
! Las instancias particulares de tokenes se llaman
lexemas.
! Para describir el formato de los lexemas que se
corresponde con un token particular se puede usar
una especificación informal, pero es más conveniente
usar expresiones regulares.
C é s ar Ig n a cio García O s orio
Á r e a de L e n g u ajes y Sistem a s
Inf o r m átic o s
Universidad de Burg o s
E n ero, 1999
© César Ignacio garcía Osorio. Universidad de Burgos.
Introducción (2)
El fuente lex
! lex es un generador de programas C que realiza un
procesado léxico de tiras de caracteres
! El fuente lex se compone de sucesiones de
expresiones regulares
! Genera un autómata finito determinista que es
interpretado a la hora de analizar la entrada
! El intérprete del autómata dirige el flujo y ejecuta las
acciones como switch de C
{definiciones}
%%
{reglas}
%%
{rutinas de usuario}
! La complejidad esta fijada por el tamaño del autómata
a traducir
Expresiones regulares
Acciones
%%
[a-z][a-z0-9]*
\n
%%
{num_ident++;ECHO;}
printf("\n%d ", ++num_lines);
flex
2
! Las definiciones y rutinas de usuario no aparecen en la
mayoría de los casos
! El segundo %% es opcional
! El mínimo programa lex sería por tanto:
%%
que se traduce por un programa que copia la entrada en
la salida
Fuente
lex
Entrada
yylex()
Salida
lex.yy.c
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
3
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
4
Unos aperitivos (1)
Unos aperitivos (2)
! Complie los siguientes ejemplos, usando las siguientes
ordenes
%{
" lex eje_n.l
" gcc lex.yy.c -o eje_n -lfl
" ejen
/*
* Un ejemplo sencillo
*/
%}
%%
%%
%{
[\t ]+
/* Un ejemplo un poco mas ambicioso */
(soy|es|somos|son|sois) |
int num_id=0;
(fui|fuiste|fue|fuimos|fueron) |
int num_lines=0;
tienes |
%}
tienen |
%%
tengo
[a-z][a-zA-Z0-9]*
\n
/* ignorar blancos */
{printf(“%s: es verbo\n”, yytext);}
[a-zA-Z]+
num_id++;ECHO;
{num_lines++;
.|\n
printf(“%d(%d)”,num_lines, num_id);
%%
}
main(){
{printf(“%s: no verbo\n”, yytext);}
{ECHO; /* accion por defecto */
yylex();
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
5
Expresiones regulares
(1)
flex
6
Expresiones regulares
(2)
! Una expresión regular indica un conjunto de palabras.
Puede contener texto y operadores, representados por
caracteres especiales.
! Las letras y dígitos del alfabeto son texto de las
expresiones regulares.
#Operadores: los siguientes caracteres son operadores
que componen el resto del alfabeto del metalenguaje lex
" Cuando quiere incluirse alguno de ellos en la parte de
texto de una expresión regular, se utilizan los caractes de
escape “ y \:
“xyz++” = xyz”++” = xyz\+\+
xa\ j (para indicar un espacio entre la a y la j)
ahora\ncuando\t\\ (para indicar el final de línea, un
tabulador y la aparición del backslash)
#Carácter arbitrario: El operador “.” o el operador “,”
representan cualquier carácter excepto el LF (line feed,
salto de línea)
" Es posible especificar caracteres arbitrarios utilizando el
escape octal, aunque no es transportable
#Elementos opcionales: El operador “?” indica un
elemento opcional en una expresión regular.
#Expresiones repetidas: Las repeticiones de clases o
expresiones se indican por los operadores: “*” (0 o mas) y
“+” (1 o mas)
#Alternancia y agrupación: El operador “|” indica
alternancia, y el operador “( )” agrupación
#Sensibilidad al contexto: lex reconoce cierta
sensibilidad al contexto por medio de los operadores “^”,
“$”, “/”
^ cuando es el primer carácter de una expresión indica que la
misma se aplicará sólo a principio de línea o de fichero.
$ cuando es el último carácter de una expresión índica que la
misma se aplica a final de línea.
/ indica contexto a la derecha para una expresión
#Clases de caracteres: Conjuntos de caracteres que
pueden aparecer en una expresión regular de forma
alternativa. El operador alternativa es [].
" Dentro de [] hay tres caracteres de significado especial:
- este operador específica rangos (si va el primero forma
#Repeticiones y definiciones: El operador “{ }” indica:
parte de la clase)
^ si aparece, debe ser el primero, indica una
complementación
\ escape habitual
© César Ignacio garcía Osorio. Universidad de Burgos.
©}
César Ignacio garcía Osorio. Universidad de Burgos.
a expansión de una definición cuando encierran el nombre de
una tira predefinida
b repetición de una expresión regular cuando encierran un
rango numérico
flex
7
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
8
Expresiones regulares
(3)
Expresiones regulares
(4)
#Precedencia de los operadores: Todos los operadores
en lex obedecen a la sigueinte ordenación (de mayor a
menor precedencia)
*
?
+
%%
[\n\t] ;
-?(([0-9]+)|( [0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
{printf(“number\n”);}
concatenación
reperición {}
$
^
|
/
< >
.
ECHO;
%%
main()
! Ejemplos de expresiones regulares:
{
" Dígitos: [0-9]
" Enteros: [0-9]*
" Enteros con al menos un dígito: [0-9]+
" Enteros con un signo opcional: -?[0-9]+
" Número decimales: [0-9]*\.[0-9]+
" Decimales o enteros: ([0-9]+)|( [0-9]*\.[0-9]+)
yylex();
}
! Más ejemplos de expresiones regulares:
" Comentario de un fichero de scriptDígitos: #.*
" Cadena de caracteres entrecomillada: \”[^”\n]*[”\n]
Ojo con esta otra: \”.*\”
y con esta otra: \”[^”]*\”
" Decimales o enteros con signo:
-?(([0-9]+)|( [0-9]*\.[0-9]+))
" Exponente: [eE][-+]?[0-9]+
" Número en coma flotante:
?(([0-9]+)|( [0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
9
Acciones en lex (1)
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
10
Acciones en lex (2)
! Asociada a cada patrón hay una acción.
! La acción por defecto es copiar la entrada en la salida.
! Para no producir ninguna salida es preciso plantear
expresiones que se ajusten a todas las tiras de entrada y
absorber la salida.
! La acción nula se representa por “;”
! Cuando varias expresiones llevan asociada la misma
acción puede emplearse “|” para unir las líneas.
! Es posible acceder a distintas variables, funciones y
macros en la especificación de las acciones:
yytext
variable que almacena la secuencia de
unput(c)
devuelve el carácter c (que no puede ser EOF)
al stream de entrada para su posterior
input()
devuelve el siguiente carácter de la entrada
output(c) escribe el carácter en la salida
! Otras funciones en lex.yy.c y libl.a son:
yywrap() se llama cuando se alcanza el final de fichero. Si
devuelve 1, lex termina. Si devuelve un 0 sigue el proceso.
caracteres que se ajusta a la expresión regular
yyleng
ECHO
yymore()
variable que almacena la longitud de yytext
macro para presentar en stdout el contenido
de yytext
indica que la siguiente palabra reconocida
debe añadirse al final de la actual (no elimina
la “porción” ya ajustada)
yyless(n) macro que indica que no todos los caracteres
reconocidos se desean en el momento actual,
solo n. El resto se retornan al stream de
entrada y es como si no se hubieran procesado
y por tanto se volverán a “escanear”
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
11
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
12
Sección de definiciones
(1)
Reglas ambiguas
! Cuando más de una expresión regular sirve para
describir una palabra, lex elige la regla de acuerdo con
el siguiente criterio:
$ Se prefiere la regla con cuyo patrón concuerde el mayor
número de caracteres posible
% Entre las reglas que ajustan el mismo número de
caracteres, la escrita en primer lugar en el fichero fuente.
! Debido al criterio $ de resolución de ambigüedades,
expresiones de la forma: ‘.*’, pueden conducir a
resultados no deseados.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
13
! El usuario necesita definir las variables que introduce
en las porciones de programa C que forman la s
acciones.
! Esto puede hacerse en la sección de definiciones o de
reglas de acuerdo con los criterios:
" Cualquier línea que no forma parte de una regla o acción
y comienza por blancos o tabulador se copia en el
programa lex.yy.c:
& Si está antes del primer %% será una declaración o
frase externa a cualquier función.
& Si aparece inmediatamente después del primer %% se
incluye como parte de yylex(). Debe ir antes de
toda regla.
" Cualquier cosa que aparezca entre líneas que contienen
sólo %{ y %} se copia tal cual con las mismas
consideraciones que antes.
" Cualquier cosa situada detrás del segundo %% se copia tal
cual al final de lex.yy.c
" Las definiciones de lex propiamente dichas deben ir antes
del primer %% y comenzar en la primera columna.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
Sección de definiciones
(2)
Sensibilidad al contexto
por la izquierda (1)
! Las definiciones sirven para definir símbolos que
representan expresiones y que se utilizan a menudo
en las reglas.
! Formato de las definiciones:
! Una extensión de lex similar a la sensibilidad por la
derecha no es útil en la mayoría de los casos, por
ejemplo un contexto a la izquierda muy alejado del
carácter actual sería problemático.
! La solución se encuentra en el uso de banderas:
" Manejadas por el usuario
" Manejadas por lex
! A las banderas manejadas por lex se las denomina
condiciones de arranque.
nombre
separador
traducción
! El nombre debe comenzar en la primera columna.
! El separador puede ser un espacio o un tabulador.
! Además de las definiciones pueden aparecer otros
comandos: selección del lenguaje, tabla de caracteres,
lista de condiciones de arranque, ajuste de los
tamaños por defecto de lex, ...
14
$ Se definen con la directiva %s (%x) en la sección de
definiciones:
& %s nom1 nom2 ...
% Se activan dentro de una acción por medio de la
directiva BEGIN:
& BEGIN nom1;
& BEGIN 0;
' Se incluyen como activadoras de reglas con los
operadores < > a principio de una expresión:
& <nom1, nom2>[a-z]
acción;
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
15
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
16
Sensibilidad al contexto
por la izquierda (2)
Juego de caracteres
definidos por el usuario
! En flex (no en así en lex) hay dos primitivas para
declarar los nombres de las condiciones de arranque:
%s, %x.
! Con %s se declaran condiciones de arranque
inclusivas.
! Con %x se declaran condiciones de arranque
exclusivas.
! Si la condiciones de arranque es inclusiva, las reglas
sin condición de arranque están también activas.
! Si la condiciones de arranque es exclusiva, solo se
tienen en cuenta las reglas cualificadas con la
condición de arranque activa.
! Un conjunto de reglas que compartan la misma
condición de arranque describen un analizador léxico
independiente de cualquier otra de las reglas del
fuente flex.
! Aspectos avanzados de las condiciones de inicio:
" YY_START (en lex se usa YYSTATE)
! Puede cambiarse la asignación numérica de un
carácter a voluntad con la directiva %T
! Las limitaciones son:
" No puede asociarse el 0 a ningún carácter.
" Ningún carácter puede asignarse a un número mayor que
el número de caracteres del juego hardware de la
máquina.
! Formato:
%T
entero tira
%T
! Ejemplo:
%T
1 Aa
2 Bb
3 Cc
.
.
26 Zz
" %option stack
& void yy_push_state(int new_state)
& void yy_pop_state()
& int yy_top_state()
%T
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
17
Resumen del formato
fuente de lex (1)
18
! Reglas
! Responden al formato general:
<expresión regular> blanco o tabulador <acción>
! Las expresiones regulares utilizan el siguiente conjunto
de operadores:
! Definiciones
"x
tira de caracteres
" "x"
tira de caracteres aunque x sea un operador
" \x
carácter x aunque sea operador
" [xy] carácter x ó y
" [x-z] caracteres entre x y z del juego de caracteres
" [^x] cualquier carácter salvo los que aparecen en x
".
cualquier carácter salvo \n
" ^x
cualquier x al principio de línea
" <y>x cualquier x, dada la condición de activación y
" x$
cualquier x al final de línea o fichero
" x?
carácter x opcional
" x*
cero o más concatenaciones de x
" x+
una o más concatenaciones de x
" x|y
expresión x ó y
" (x)
expresión x
" x/y
expresión x sólo si va seguida de y
" {x}
traducción de la definición x
" x{m,n} de m a n concatenaciones de x
" Definiciones de \lex:
& nombre blanco traducción
" Fragmentos de código:
& blanco código
" Fragmentos de código:
%{
código
%}
" Condiciones de activación:
& %S nom1 nom2 ...
" Tablas de caracteres:
número blanco tira %T
" Cambios de dimensión de conjuntos internos de lex:
& %carácter número
& donde, número es un número decimal que representa una
dimensión y carácter es un carácter clave a elegir entre los
siguientes: p: posición, n: estados, e: nodos de árbol, a:
transiciones, k: clases de caracteres empaquetados, o:
tamaño conjunto de salida.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
Resumen del formato
fuente de lex (2)
{definiciones}
%%
{reglas}
%%
{rutinas}
& %T
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
19
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
20
Ejemplos y
ejercicios
Un ejemplo
! El programa mínimo flex, es el siguiente:
%%
es decir, una única línea con dos signos de porcentaje.
Compilarlo y echa un vistazo al programa C generado.
© César Ignacio garcía Osorio. Universidad de Burgos.
Otro ejemplo
22
Ejercicios
! Un programa para contar palabras:
%{
unsigned charCount = 0, wordCount = 0, lineCount = 0;
%}
word
[^ \t\n]+
eol
\n
%%
{word} {wordCount++; charCount+=yyleng; }
{eol} { charCount++; lineCount++; }
.
charCount++;
%%
main()
{
yylex();
printf(“%d %d %d\n”, lineCount, wordCount,
charCount);
}
! El programa anterior lee de la entrada estándar. Si se
modifica la función main se puede conseguir que
tome como argumentos el nombre del fichero del que
se desean saber el número de palabras
main(int argc, char **argv)
{
if(argc > 1) {
FILE *file;
file=fopen(argv[1], “r”);
if(!file) {
fprintf(stderr, “no se puede abrir %s\n”, argv[1]);
exit(1);
}
yyin = file;
}
yylex();
printf(“%d %d %d\n”, lineCount, wordCount, charCount);
}
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
flex
23
! 1.- Construir un filtro que transforme todas las letras
mayúsculas en minúsculas.
! 2.- Construir un programa que toma como
argumento un nombre de fichero y muestra
su contenido por pantalla anteponiendo a
cada línea su número, también debe poder
utilizarse como un filtro (como cat –n).
! 3.- Construir un programa que como cat –v
permita mostrar de algún modo los
caracteres no imprimibles, por ejemplo
mostrando su número ASCII entre ángulos.
! 4.- Construir un programa al que se le
pasa como argumento el nombre de un
fichero. En el fichero entre van a
aparecer números en distintas bases. La
base en la que deben interpretarse los
números se indica al final de estos,
pudiendo utilizarse varios métodos tal
como se indica a continuación:
" 1011b
" 172o
" 987d
" 6A0fCx
1011B
172O
987D
6A0fCX
1011(2)
172(8)
987(10)
6A0fC(16)
en
en
en
en
binario
octal
decimal
hexadecimal
el programa ha de presentarlos en decimal
y sin el indicador de base.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
24
Mas ejercicios
! 5.- Escribir un filtro que aplicado a la
hora que devuelve el programa date, nos
de los buenos días (7 a 15), las buenas
tardes (15 a 20) o las buenas noches.
Además nos ha de dar la hora en formato
texto tal como se muestra en el ejmplo.
Analizadores
léxicos
date | DEMO
Buenas tardes, son las tres y cuarto.
! 6.- Hacer un pequeño “eliza”, he aquí
algunas sugerencias de palabras clave a
buscar en la entrada, posibles
respuestas asociadas.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
Algunas
cuestiones sobre
implantación
25
Manejo de buffers (2)
Manejo de buffers (1)
Pareja de buffers I
! Existen tres métodos generales de implantación de un
analizador léxico:
! Se utiliza un buffer dividido en dos mitades de N
caracteres cada una. Por lo general, N es el número de
caracteres en un bloque de disco, por ejemplo, 1024 ó
4096.
" Utilizar un generador de analizadores léxicos
" Escribir el analizador léxico en un lenguaje convencional
de programación de sistemas
" Escribir el analizador léxico en lenguaje ensamblador
E
! Estos enfoques tienen un orden de dificultad creciente,
lamentablemente los enfoques más difíciles consiguen
analizadores léxicos más rápidos.
! Como el analizador léxico es la única fase que lee el
programa carácter a carácter su velocidad es un
problema en el diseño de compiladores.
! Además en muchos lenguajes el analizador léxico
necesita preanalizar varios caracteres, antes de poder
anunciar una concordancia. Los caracteres
preanalizados se tienen que devolver después a la
entrada. Como se puede consumir mucho tiempo
moviendo caracteres, se han desarrollado técnicas
especializadas en el manejo de buffers para reducir el
número de operaciones necesarias para procesar un
carácter de entrada.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
27
=
M
* C * * 2 eof
delantero
comienzo_lexema
! Se leen N caracteres de entrada en cada mitad del
buffer con una orden de lectura de sistema, en vez de
invocar una instrucción de lectura para cada carácter
de entrada. Si quedan menos de N caracteres en la
entrada, entonces se lee un carácter especial eof en el
buffer después de los caracteres de entrada.
! Se mantienen dos apuntadores al buffer de entrada.
La cadena de caracteres entre los dos apuntadores es
el lexema en curso. Al principio, los dos apuntadores
apuntan al primer carácter del próximo lexema que
hay que encontrar.
! El apuntador delantero, examina hacia delante hasta
encontrar una concordancia con un patrón. Una vez
determinado el siguiente lexema, el apuntador
delantero se coloca en el carácter de su extremo
derecho.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
28
Manejo de buffers (3)
Manejo de buffers (4)
Pareja de buffers II
Centinelas
! Después de haber procesado el lexema, ambos
apuntadores se colocan en el carácter situado
inmediatamente después del lexema.
! Cuando el apuntador delantero esta apunto de
sobrepasar por la marca intermedia del buffer, se llena
la mitad derecha con N nuevos caracteres de entrada.
! Cuando el apuntador delantero está a punto de
sobrepasar el extremo derecho del buffer, se llena la
mitad izquierda con N nuevos caracteres de entrada y
el apuntador delantero se regresa al principio del
buffer
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 delantero := delantero + 1;
! Este esquema de manejo de buffers casi siempre
funciona muy bien, pero limita la cantidad de
caracteres de preanálisis, y esto puede imposibilitar el
reconocimiento de los componentes léxicos cuando la
distancia recorrida por el apuntador delantero sea
mayor que la longitud del buffer.
! Si de usa el algoritmo tal como se ha expuesto, se
necesitan dos comparaciones para cada avance del
apuntador delantero. Se pueden reducir estas dos
pruebas a una si se amplía cada mitad del buffer para
admitir un carácter centinela al final.
! El centinela es una carácter especial. Una elección
natural es el eof.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
29
Implantación de un diagrama
de transiciones (1)
! A partir del diagrama de transiciones se puede
construir “a mano” un programa analizador léxico de
un modo sistemático.
! En general puede haber varios diagramas de
transiciones, cada uno de los cuales especifique un
grupo de componentes léxicos. Si surge un fallo
mientras se esta siguiendo un diagrama de
transiciones, se debe retroceder el apuntador
delantero hasta donde estaba en el estado inicial de
dicho diagrama, y activar el siguiente diagrama de
transiciones (igualar el apuntador delantero al valor
del apuntador de inicio de lexema). Si el fallo surge en
todos los diagramas de transiciones, es que se ha
detectado un error léxico y se invoca una rutina de
recuperación de errores.
E
=
* eof C * * 2 eof
M
delantero
comienzo_lexema
! El siguiente código realiza sólo una comparación en la
mayoría de las ocasiones
Delantero := delantero + 1;
if delantero = eof then begin
if delantero esta al final de la primera mitad then begin
recargar la segunda mitad;
delantero := delantero + 1;
end
else if delantero 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 bufer*/ terminar la entrada
©end
César Ignacio garcía Osorio. Universidad de Burgos.
flex
30
Implantación de un diagrama
de transiciones (2)
<
inicio
0
=
1
>
2
devuelve(oprel, MEI)
3
devuelve(oprel, DIF)
otro
*
devuelve(oprel, MEN)
4
=
devuelve(oprel, IGU)
5
>
=
6
otro
inicio
9
letra
*
11
*
devuelve(obten_complex(), instala_id())
dígito
dígito
12
13
.
devuelve(oprel, MAY)
8
otro
10
devuelve(oprel, MAI)
7
letra o dígito
inicio
dígito
dígito
dígito
14
15
E
16
+o-
17
dígito
inicio
dígito
25
flex
31
26
otro
18
otro
19
dígito
E
dígito
© César Ignacio garcía Osorio. Universidad de Burgos.
eof
delim
*
27
inicio
delim
28
© César Ignacio garcía Osorio. Universidad de Burgos.
29
otro
flex
*
30
32
Implantación de un diagrama
de transiciones (3)
! Una secuencia de diagramas de transiciones se puede
convertir en un programa que busque los componentes
léxicos especificados por los diagramas.
! A cada estado del diagrama le corresponde un segmento
de código. Si hay aristas que salen de un estado,
entonces su código lee un carácter y selecciona una
arista para seguir, si es posible. Si hay una arista
etiquetada con el carácter leído, o etiquetada con una
clase de caracteres que contenga el carácter leído,
entonces el control se transfiere al código del estado
apuntado por esa arista.
! Si no hay tal arista y el estado en curso de ejecución no
es el que indica que se ha encontrado un componente
léxico, entonces se llama a la rutina fallo() para hacer
retroceder el apuntador delantero a la posición del
apuntador al comienzo e iniciar la búsqueda del
componente léxico especificado por el siguiente diagrama
de transiciones.
! Si no hay que probar más diagramas de transiciones,
fallo() llama a una rutina de recuperación de errores.
Implantación de un diagrama
de transiciones (4)
int estado = 0, inicio = 0;
int valor_lexico;
int fallo(){
delantero=inicio_lexema;
switch(inicio){
case 0: inicio=9; break;
case 9: inicio=12; break;
case 12: inicio=20; break;
case 20: inicio=25; break;
case 25: recupera(); break;
default: /* error */
}
}
! Para devolver los componentes léxicos se usa una
variable global valor_lexico.
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
33
Implantación de un diagrama
de transiciones (5)
complex sigte_complex(){
while(1){
switch(estado){
case 0:
c = sigtecar();
if(c==blanco || c==tab || c==newline){
estado = 0; inicio_lexema++;
}
else if (c == `<´) estado = 1;
else if (c == `=´) estado = 5;
else if (c == `>´) estado = 6;
else estado = fallo();
break;
case 9:
c = sigtecar();
if (isletter(c)) estado = 10;
else estado = fallo();
break;
case 10:
c = sigtecar();
if (isletter(c)) estado = 10;
else if (isdigit(c)) estado = 10;
else estado = 11;
break;
case 11:
regresa(1); instala_id;
return ( obten_complex() );
. . .
. . . /* aquí los casos 11 al 24 */
. . .
case 25:
c = sigtecar();
if (isdigit(c)) estado = 26;
else estado = fallo();
break;
case 26:
c = sigtecar();
if (isdigit(c)) estado = 26;
else estado = 27;
break;
case 27:
regresa(1); instala_num();
return( NUM );}}}
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
34
Diseño de un generador de
analizadores léxicos (1)
! Supóngase que se tiene una especificación de un
analizador léxico de la forma:
p1
p2
...
pn
{acción1}
{acción2}
{acciónn}
pi es una expresión regular
accióni es un fragmento de programa que debe
ejecutarse siempre que se encuentre en la entrada
un lexema que concuerde con pi
! El problema es construir un reconocedor que busque
lexemas en el buffer de la entrada. Si concuerda más
de un patrón, el reconocedor elegirá el lexema más
largo que haya concordado. Si hay dos o más patrones
que concuerden con el lexema más largo, se elige el
primer patrón que haya concordado de la lista.
! Un método es construir la tabla de transiciones de un
autómata finito no determinista N para el patrón
compuesto p1|p2| ... | pn. Esto se puede hacer creando
primero un AFND N(pi) para cada patrón pi utilizando
el algoritmo de Thompson, añadiendo después un
nuevo estado de inicio s0, y por último enlazando s0 al
estado de inicio de cada N(pi) con una transición ε.
ε
s0
N(p1)
ε
ε
35
N(p2)
...
N(pn)
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
36
Diseño de un generador de
analizadores léxicos (2)
especificación
de LEX
simulador
del AFND
Compilador
de LEX
tabla de
transiciones
! En el AFND combinado obtenido, hay un estado de
aceptación para cada patrón pi. Cuando se simula el
AFND utilizando el algoritmo anterior, se construye la
secuencia de conjuntos de estados donde puede estar
el AFND combinado después de leer cada carácter de
entrada. Incluso si se encuentra un conjunto de
estado que contenga un estado de aceptación, para
encontrar la concordancia más larga se debe seguir
simulando el AFND hasta alcanzar terminación, es
decir, un conjunto de estados desde el que no hay
transiciones con el símbolo de entrada en curso.
flex
buffer de entrada
léxema
! Ahora podría simular el AFND con un algoritmo como
el siguiente:
S ← cerr-ε({s0 });
c ← sgtecar();
while c ≠ e.o.f do begin
S ← cerr-ε(mueve(S,c));
c ← sgtecar();
end
if S∩F≠∅ then return “SI” else return “NO”
© César Ignacio garcía Osorio. Universidad de Burgos.
Diseño de un generador de
analizadores léxicos (3)
37
Métodos para
comprensión de tablas (1)
! El proceso de análisis léxico ocupa una parte
considerable del tiempo del compilador, puesto que es
el único proceso que debe observar en la entrada un
carácter a la vez. Por tanto, el analizado léxico debe
minimizar el número de operaciones que realiza por
cada carácter de entrada. Si se utiliza un AFD para
ayudar a implantar el analizador léxico, es aconsejable
una representación eficiente de la función de
transición.
! Existen muchas formas de implantar la función de
transición de un autómata finito:
" Matriz bidimensional, indexada por estados y caracteres.
Proporciona el acceso más rápido, pero puede ocupar
demasiado espacio.
" Lista enlazada para almacenar las transiciones de salida
de cada estado, con una transición “por omisión” al final
de la lista: es un esquema mas compacto, pero más lento
! Para encontrar la concordancia adecuada, se hacen
dos modificaciones al algoritmo anterior.
! Primero, siempre que se añada un estado de
aceptación al conjunto de estados en curso, se
registran la posición en curso de entrada y el patrón pi
correspondiente a este estado de aceptación. Si el
conjunto de estados en curso ya contiene un estado
de aceptación, entonces solo se registra el patrón que
aparezca primero en la especificación de LEX.
! Segundo, se continúa haciendo transiciones hasta que
se alcanza la terminación. En la terminación se
retrocede el apuntador delantero a la posición en que
ocurrió la última concordancia. El patrón que hizo
dicha concordancia identifica al componente léxico
encontrado, y el lexema emparejado es la cadena
entre los apuntadores de inicio del lexema y los
delanteros. (Además existe un patrón de error)
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
38
Métodos para
comprensión de tablas (2)
! Existe una representación que combina el acceso rápido
de la representación por medio de matrices con la
compacidad de las estructuras de listas. Se utiliza una
estructura de datos que consta de tres matrices
indexadas por números de estados.
base
s
siguiente revisa
a
r
l
! Se utiliza la matriz base para determinar la posición
base de las entradas para cada estado almacenado en
las matrices siguiente y revisa.
function sigte_estado(s,a);
if revisa[base[s]+a] =s then
return siguiente[base[s ]+a]
else
return -1
! Para iniciar la matriz siguiente se puede usar una
estrategia de primero el que mejor ajuste. La siguiente
fila a almacenar se “mueve” sobre siguiente sin que se
produzca “colisión”
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
39
© César Ignacio garcía Osorio. Universidad de Burgos.
flex
40
Descargar