Lexical Analysis (Scanning)

Transcription

Lexical Analysis (Scanning)
Lexical Analysis (Scanning)
José Miguel Rivero
[email protected]
Barcelona School of Informatics (FIB)
Technical University of Catalonia (UPC)
José Miguel Rivero
Lexical Analysis – p. 1/46
Lexical Analysis. Summary
2.1 El análisis léxico en la compilación
2.2 Motivación
2.3 Descripción del problema
2.4 Construcción de Thompson de AFN(er)
a partir de una er
2.5 Algoritmo para decidir si AFN(er) reconoce w
2.6 Alternativa: calcular el AFD(er) para AFN(er)
2.7 Comparación de los dos métodos
2.8 Algoritmo de análisis léxico
2.9 Tratamiento de errores léxicos
2.10 Generación automática de analizadores léxicos:
ANTLR, flex, . . .
José Miguel Rivero
Lexical Analysis – p. 2/46
2.1 Análisis léxico en la compilación
Estructura conceptual vs. estructura real
Objetivo. Tokens
Otros componentes léxicos
Atributos de los tokens
Ejemplo
José Miguel Rivero
Lexical Analysis – p. 3/46
Estructura conceptual vs. estructura real
Estructura conceptual
programa
fuente
ANALISIS
LEXICO
lista de
tokens
ANALISIS
SINTACTICO
arbol
sintactico
ANALISIS
SEMANTICO
arbol
sintactico
decorado
Estructura procedural (real)
programa
fuente
token
ANALISIS
LEXICO
ANALISIS
SINTACTICO
arbol
sintactico
ANALISIS
SEMANTICO
arbol
sintactico
decorado
necesito token
1ª pasada
2ª pasada
representaciones internas
José Miguel Rivero
Lexical Analysis – p. 4/46
Objetivo. Tokens
Objetivo:
descomponer la secuencia de caracteres
del programa fuente en una secuencia de
componentes léxicos (tokens)
¿Cúales son los tokens que debemos reconocer y
enviar al analizador sintáctico?:
palabras clave del lenguaje (while, endvars,
write, . . .)
operadores (+, /, “<=”, OR, “:=”, . . .)
otros símbolos del lenguaje (paréntesis, coma,
punto y coma, etc)
identificadores (numels), valores enteros (834),
cadenas de caracteres ("Hello world!"),
reales (3.04E-3), . . .
...
José Miguel Rivero
Lexical Analysis – p. 5/46
Otros componentes léxicos
Existen componentes léxicos que deben reconocerse
aunque no tengan interés para etapas posteriores del
compilador:
separadores: espacios, tabuladores, . . .
comentarios: /* ... */ en C, // ... en C++
cambios de línea. Interesa conocer en que línea
encontramos los tokens para situar los errores de
compilación
Atributos de los tokens
en todos: el número de línea
en identificadores, constantes numéricas, strings, . . . :
el texto del token (el prefijo reconocido)
José Miguel Rivero
Lexical Analysis – p. 6/46
Ejemplo
Programa fuente:
Program
Vars
Integer i
Real
r
EndVars
1: Program
2:
Vars
3:
Integer i
4:
Real
r
EndVars
5:
6:
7:
i := 1 ; r := 1.17
i := 1 ; r := 1.17
While i < 25 Do // 24 vueltas
While i < 25 Do // 24 vueltas 8:
9:
r := r / i ; i := i + 1
r := r / i ; i := i + 1
EndWhile
EndWhile
10:
Write ( "fin" )
Write ( "fin" )
11:
12: EndProgram
EndProgram
Secuencia de tokens:
PROGRAM VARS INTEGER IDENT("i") REAL
IDENT("r") ENDVARS IDENT("i") ASIG INTCONST("1") PUNTCOM IDENT("r") ASIG
REALCONST("3.14") WHILE IDENT("i") MENOR INTCONST("24") DO IDENT("r") ASIG IDENT("r")
DIVREAL IDENT("i") PUNTCOM IDENT("i") ASIG IDENT("i") MAS INTCONST("1") ENDWHILE
WRITE PARABR STRINGCONST("fin") PARCERR ENDPROGRAM
José Miguel Rivero
Lexical Analysis – p. 7/46
2.2 Motivación
¿Por qué definir una etapa específica que realiza el análisis
léxico de un programa?:
Conceptualmente es una tarea diferenciada: filtra la
entrada y la descompone en aquellos elementos que
tienen interés para la siguiente etapa, el análisis
sintáctico.
Las técnicas que veremos serán
sencillas y eficientes (no debemos matar moscas
a cañonazos)
flexibles (los cambios a nivel léxico se pueden
resolver fácilmente)
transportables y generales
Estas técnicas tienen otras muchas aplicaciones.
José Miguel Rivero
Lexical Analysis – p. 8/46
Otras aplicaciones
Recuperación de información (análisis de queries)
Problemas de genética
Editores de texto (editores dirigidos por la sintaxis, . . . )
Sistemas operativos (lenguajes de commandos, grep)
Ejemplo (en unix): % rm prog*.[ch]
Lenguajes de programación (del tipo patrón/acción :
(AWK )
Verificación de circuitos
...
José Miguel Rivero
Lexical Analysis – p. 9/46
2.3 Descripción del problema
Expresiones regulares
Poder expresivo de las expresiones
regulares
Problema a resolver por el analizador léxico
Ejemplos
Criterio para deshacer ambigüedades
¡Cuidado al definir el lenguaje!
José Miguel Rivero
Lexical Analysis – p. 10/46
Expresiones regulares
Los elementos léxicos de un lenguaje de programación se
especifican con expresiones regulares sobre un cierto
alfabeto Σ.
Reglas de formación:
er = ǫ es una expresión regular
er = a es una expresión regular para todo a ∈ Σ
si er1 y er2 son expresiones regulares,
er = er1 | er2 es una expresión regular
si er1 y er2 son expresiones regulares,
er = er1 er2 es una expresión regular
si er1 es una expresión regular, er = er1∗ es una expresión regular
si er1 es una expresión regular, er = (er1 )
es una expresión regular
José Miguel Rivero
Lexical Analysis – p. 11/46
Poder expresivo de las er’s
Mediante expresiones regulares no podemos
reconocer, por ejemplo, expresiones bien parentizadas
por ejemplo del tipo {an bn }: los autómatas finitos no
saben contar.
Tampoco podemos reconocer las palabras del lenguaje
{nan : n ≥ 0} =
{ 0, 1a, 2aa, 3aaa, . . . }
Strings repetidos no pueden ser especificados con
expresiones regulares. El conjunto { w c w | w ∈ (a|b)∗ }
no es un lenguaje regular, ni tampoco puede ser
descrito con gramáticas incontextuales.
José Miguel Rivero
Lexical Analysis – p. 12/46
Problema a resolver en el scanning
Tenemos una lista de expresiones regulares er1 , . . . , ern
que describen los componentes léxicos del lenguaje, y un
programa fuente a descomponer (la palabra w).
Dadas las expresiones regulares er1 , . . . , ern
y la palabra w, se trata de encontrar el prefijo
más largo v de w t.q. v ∈ L(eri ) para alguna i.
En caso de encontrar más de una expresión regular para el
prefijo v , se elegirá la eri con la i mínima.
La siguiente llamada al analizador léxico vuelve a hacer lo
mismo con el resto de la entrada por tratar (si w = v w′
después buscará el prefijo más largo de w′ ) y así hasta
agotar la entrada.
José Miguel Rivero
Lexical Analysis – p. 13/46
Ejemplos
Se trata de encontrar el prefijo más largo, por tanto
si tenemos "programacion ..." no nos dirá que
tenemos la palabra clave program seguido del
identificador acion
si tenemos "while ..." no nos dirá que tenemos
el identificador "while"
si tenemos "ab24.8 ..." no nos dirá que
tenemos el identificador "ab" seguido del real
"24.8" (a menos que los identificadores sólo
contengan caracteres alfabéticos)
José Miguel Rivero
Lexical Analysis – p. 14/46
Ejemplos (cont.)
Si tenemos "10..20 ..." nos dirá que tenemos el
entero "10" porque aunque intenta reconocer un
real que empieza por "10." cuando ve el segundo
’.’ se da cuenta que no puede y tiene que volver al
lugar en el que ya había reconocido algo. Después
reconocerá los dos puntos y por último el segundo
entero. ¡Ojo, no linealidad!
Por tanto, se tiene que recordar el punto en que acabó
el último prefijo aceptado (y por qué eri ) por si no
podemos encontrar uno aún más largo.
Si en cambio sí encontramos un prefijo aún más largo
nos olvidamos del anterior.
Para no perder linealidad podemos exigir que al buscar
un prefijo aún más largo, sólo podemos atravesar
estados aceptadores.
José Miguel Rivero
Lexical Analysis – p. 15/46
Deshacer ambigüedades léxicas
Se tiene que reconocer el prefijo más largo posible
Se tienen que especificar primero (menor i) las
expresiones regulares que corresponden a palabras
clave, que la que define los identificadores: una palabra
clave forma parte del lenguaje de las dos, pero la
tenemos que reconocer como palabra clave y no como
identificador.
Ejemplo de analizador léxico en flex:
program
vars
{...; return(PROGRAM) }
{...; return(VARS) }
...
","
{...; return(COMA) }
...
[0-9]+
[A-Za-z][A-Za-z0-9]*
...
{...; return(ENTERO) }
{...; return(IDENT) }
José Miguel Rivero
Lexical Analysis – p. 16/46
¡Cuidado al definir el lenguaje!
Es importante hacer una definición muy meditada tanto de
los componentes léxicos como de la estructura sintáctica
de un lenguaje. Por ejemplo, estas son algunas de las
situaciones que se podrían dar:
en Fortran, la expresión DO 5 I = 1,25 ...
representa el comienzo de un bucle. Si en lugar de
1,25 hubiéramos escrito 1.25 estaríamos asignando
dicho valor real a la variable DO5I: no free-format
si obligamos a que las etiquetas (pasa también en
Fortran) estén situadas en la primera columna
complicaremos el análisis léxico
si permitimos que los reales puedan tener parte
decimal vacía, el rango (de un array ) 10..40 no se
analizará correctamente
José Miguel Rivero
Lexical Analysis – p. 17/46
2.4 Constr. de Thompson de AFN(er)
Construcción del autómata N (er) para las expresiones
regulares ǫ, a, er1 |er2 , er1 er2 , er1∗ y (er1 ), donde er1 y er2
son expresiones regulares con autómatas ya
construidos N (er1 ) y N (er2 ).
er = λ
er = a
λ
er = er1 | er2
a
λ
N(er1)
λ
λ
λ
N(er2)
er = er1 er2
N(er1)
er = er1*
N(er2)
λ
λ
N(er1)
er = (er1)
λ
N(er)
N(er1)
λ
José Miguel Rivero
Lexical Analysis – p. 18/46
Construcción de Thompson (cont.)
Invariante de la construcción: todos los AFN’s tienen un
estado inicial sin aristas de entrada, y un solo estado
final sin aristas de salida
El número de estados del AFN(er) ≤ 2|er|, porque se
añaden como máximo 2 nuevos estados por cada paso
durante la construcción
Tenemos como máximo 2 aristas salientes (2
transiciones) por cada estado del autómata. Por tanto,
podemos obtener una representación compacta del
mismo.
José Miguel Rivero
Lexical Analysis – p. 19/46
Ejemplo 1
Autómata finito indeterminista para la expresión regular
er = (a|b)∗ abb. Estos son los primeros pasos de las
construcción del AFN:
er = b
er = a
er = a | b
b
a
a
λ
λ
λ
er = (a | b)*
er = (a | b)* a
λ
λ
a
λ
λ
λ
a
λ
b
λ
λ
b
λ
λ
λ
λ
λ
λ
b
λ
a
λ
λ
José Miguel Rivero
Lexical Analysis – p. 20/46
Ejemplo 2
Combinación de AFN’s en la disyunción de las eri ’s para
resolver el problema del análisis léxico
N(er1)
f1
λ
N(er2)
f2
λ
.
.
.
λ
er = er1 | er2 | ... | ern
i
N(ern)
fn
José Miguel Rivero
Lexical Analysis – p. 21/46
2.5 Algoritmo de decision para AFN(er)
Definimos en primer lugar dos funciones auxiliares:
ǫ-clausura(S) es el conjunto de estados accesibles desde los estados de
S a través de cero o más ǫ-transiciones.
move(S, a) es el conjunto de estados accesibles desde los estados de S
con una transición etiquetada con a.
Algoritmo que decide si AFN(er) reconoce w:
P re : s0 es el estado inicial del autómata AF N
F es el conjunto de estados finales de AF N
eof es el símbolo con el que acaba w
S := ǫ-clausura({s0 });
a := LeerSimbolo( );
while a ! = eof do
S := ǫ-clausura(move(S, a));
a := LeerSimbolo( );
endwhile
P ost : AF N acepta w ssi S ∩ F 6= ∅
José Miguel Rivero
Lexical Analysis – p. 22/46
Análisis del algoritmo
Coste temporal del algoritmo: O(|er| · |w|)
Coste espacial (tamaño de la tabla de transiciones del
autómata): O(|er|)
Pero recordemos que el objetivo del análisis léxico en
la compilación es obtener el prefijo más largo de w y
dar prioridad a la i mínima en caso de “empate”.
Para ello hay que modificar ligeramente el algoritmo
anterior: tenemos que mantener el estado de
aceptación más reciente (si hay varios sólo el de la i
mínima), y además guardar la longitud del prefijo
correspondiente. Así cuando ya no haya transición
posible, sabremos cúal fue el último prefijo aceptado y
por qué expresión regular.
José Miguel Rivero
Lexical Analysis – p. 23/46
2.6 Alternativa: calcular el AFD(er)
Algoritmo de determinización a partir de AFN(er)
Ejemplo
Coste espacial del ADF
Algoritmo de minimización de AFD’s
Ejemplo
Algoritmo de aceptación de w por el AFD
Técnicas de compresión
José Miguel Rivero
Lexical Analysis – p. 24/46
Algoritmo de determinización
Autómata determinista: desde ningún estado hay
ǫ-transiciones ni tampoco más de una arista de salida
para un mismo símbolo a ∈ Σ.
Técnica de cálculo de subconjuntos. Entenderemos
cada subconjunto final de estados como un estado del
autómata determinista y calcularemos las transiciones
entre estos estados.
Algoritmo: construiremos Dstate (el conjunto de
estados de AFD) y Dtran (la tabla de transiciones del
AFD). Los estados en Dstate se marcan cuando se
calculan sus transiciones en Dtran.
José Miguel Rivero
Lexical Analysis – p. 25/46
Algoritmo de determinización (cont.)
Pre: s0 es el estado inicial del autómata AF N
F
es el conjunto de estados finales de AF N
ǫ-clausura({s0 }) es el único estado en Dstate y no está marcado
while exista un estado S sin marcar en Dstate do
marca S
foreach simbolo de entrada a ∈ Σ do
S ′ := ǫ-clausura(move(S, a));
if S ′ ∈
/ Dstate then
añade S ′ (sin marcar) a Dstate
endif
Dtran[S, a] := S ′ ;
endfor
endwhile
Post: el estado inicial de AF D es ǫ-clausura({s0 })
Son estados finales de AF D todos aquellos (conjuntos de)
estados que contengan al menos un estado de F
José Miguel Rivero
Lexical Analysis – p. 26/46
Ejemplo
Calcular el autómata determinista para la expresión regular
er = (a|b)∗ abb
NFA:
0
λ
λ
λ
a
2
3
λ
1
6
λ
b
4
λ
7
a
8
b
9
b
10
λ
5
λ
ǫ-clausura({0})
=
ǫ-clausura(move(A, a))
{0, 1, 2, 4, 7} = A
=
ǫ-clausura({3, 8})
=
{1, 2, 3, 4, 6, 7, 8} = B
=
ǫ-clausura({5})
=
{1, 2, 4, 5, 6, 7} = C
Dtran[A, a] = B
ǫ-clausura(move(A, b))
Dtran[A, b] = C
...
José Miguel Rivero
Lexical Analysis – p. 27/46
Ejemplo (cont.)
Dtran:
símbolo
estado
a
b
A
B
C
B
B
D
C
B
C
D
B
E
E
B
C
DFA:
b
C
b
b
a
A
a
B
b
b
D
E
a
a
a
José Miguel Rivero
Lexical Analysis – p. 28/46
Coste espacial del AFD
El coste en espacio (número de estados de la tabla Dtran)
puede ser exponencial respecto de la longitud de er (el
número de subconjuntos distintos de un conjunto de N
elementos es 2N )
Ejemplo: Dada la expresión regular (a|b)∗ a(a|b)k
construiremos un AFN de la siguiente forma:
Un estado inicial 0 con aristas etiquetadas con a y b
hacia sí mismo, y una arista etiquetada con a hacia el
estado 1
Transiciones desde el estado i etiquetadas con a y b
hacia el estado i+1, para i ∈ [1..k]
El estado k +1 es el estado final
José Miguel Rivero
Lexical Analysis – p. 29/46
Coste espacial del AFD (cont.)
a, b
0
a
1
a, b
...
a, b
k
a, b
k+1
El tamaño del AFD correspondiente es exponencial
porque necesita recordar k + 1 bits (los últimos k + 1
símbolos leídos)
Con k = 3:
abba (estado final) −→a bbaa (estado no final)
baba (estado no final) −→b abab (estado final)
José Miguel Rivero
Lexical Analysis – p. 30/46
Algoritmo de minimización de AFD’s
Calcula particiones sucesivas del conjunto de estados.
P re : S es el conjunto de estados de AF D
s0 es el estado inicial de AF D
F es el conjunto de estados finales de AF D
P ost : AF D′ acepta el mismo lenguaje que AF D
teniendo el mínimo número de estados posible
José Miguel Rivero
Lexical Analysis – p. 31/46
Algoritmo de minimización (cont.)
Calcula particiones sucesivas del conjunto de estados.
partición inicial Π = Πnew con dos grupos :
estados finales F y estados no finales S \ F
repeat
Π := Πnew
f or each grupo G de Π do
1.
divide G en subgrupos t.q.
dos estados s y t
de G quedan en el mismo subgrupo ssi para
todo símbolo a ∈ Σ, s y t tienen transiciones
hacia estados en el mismo grupo de Π.
2.
reemplaza G en Πnew por el conjunto de
subgrupos formados
endf or
until Πnew = Π
José Miguel Rivero
Lexical Analysis – p. 32/46
Algoritmo de minimización (cont.)
Se construye AF D′ :
1.
Sus estados se definen eligiendo un representante
de cada grupo
2.
Las transiciones en AF D′ serán las que existen entre
los estados representantes de AF D
3.
El estado inicial de AF D′ será el representante del
grupo que contiene s0
4.
Los estados finales serán aquellos que tengan
representantes en F
José Miguel Rivero
Lexical Analysis – p. 33/46
Ejemplo
Minimización del autómata determinista que reconoce
(a|b)∗ abb
DFA:
b
C
b
b
a
A
a
B
b
b
D
E
a
a
a
Comentario
Particiones
estados no finales / finales
(ABCD)
(E)
A, B, C →b (ABCD) pero D →b (E)
(ABC)
(D)
(E)
(D)
(E)
A, C →b (ABC) pero B →b (D)
(AC)
(B)
partición final
José Miguel Rivero
Lexical Analysis – p. 34/46
Ejemplo (cont.)
Dtran:
símbolo
estado
a
b
AC
B
AC
B
B
D
D
B
E
E
B
AC
DFAmin :
b
b
AC
a
B
b
b
D
E
a
a
a
José Miguel Rivero
Lexical Analysis – p. 35/46
Otro forma de calcular AFD(er)
Evita determinizar el AFN(er) y aplicar después el
algoritmo de minimización.
Realiza estos dos pasos en uno.
No siempre obtiene el AFD(er) mínimo pero es una
buena técnica en la mayoria de los casos
José Miguel Rivero
Lexical Analysis – p. 36/46
Algoritmo de decision para AFD(er)
P re : s0 es el estado inicial del autómata AF D
F es el conjunto de estados finales de AF D
eof es el símbolo con el que acaba w
s := s0 ;
a := LeerSimbolo( );
while a ! = eof do
s := Dtran[s, a];
a := LeerSimbolo( );
endwhile
P ost : AF D acepta w ssi s ∈ F
Tiene un coste temporal lineal en la longitud de la entrada O(|w|)
Tiene un coste espacial (tamaño de Dtran)
O( (número de estados del AFD) ∗ (número de símbolos de Σ) ) = O(2|er| )
José Miguel Rivero
Lexical Analysis – p. 37/46
Técnicas de compresión
Existen diferentes formas de implementar la función de
transición de un AF D. La más directa es usando una tabla
de transición. El tamaño de esa tabla depende del número
de estados del autómata y del número de símbolos del
alfabeto. Dado que:
1. el número de estados puede ser bastante elevado y
2. no existen transiciones desde cada estado para todos
los símbolos (más bien al contrario, o bien es al mismo
estado para casi todos los símbolos)
esta enorme tabla se encuentra mayormente vacía (sparse)
e interesa trabajar con una representación más compacta.
José Miguel Rivero
Lexical Analysis – p. 38/46
Técnicas de compresión (cont.)
Vector unidimensional de estados. Desde cada estado
cuelga la lista de transiciones que sí están definidas
desde él, más eventualmente la transición que se hace
por defecto (o en caso de error). Es la más sencilla
pero el tiempo de cálculo del nuevo estado puede
empeorar sensiblemente.
Otras técnicas intentan aprovechar (a groso modo) las
casillas vacías contiguas que había en la tabla inicial
antes del primero y a partir del último símbolo con
transición en cada estado. En esos métodos se utilizan
varias tablas para saber, por ejemplo, donde comienza
la fila de transiciones de un estado. Así se mejora
mucho el tiempo para calcular una transición
comparado con la técnica anterior y el espacio se
aprovecha bastante mejor que utilizando la tabla inicial.
José Miguel Rivero
Lexical Analysis – p. 39/46
Técnicas de compresión (cont.)
Podemos también solapar dos o más filas siempre que
las transiciones definidas en una y en otra no coincidan.
Esta técnica se muestra en la siguiente figura:
if check[base[s] + a] = s then
next(s, a) = next[base[s] + a]
else
next(s, a) = error
base
+a
s
+b
s’
next
check
...
t
s
s
s’ s
tran(s, a) = t
s’ s’ s’
...
...
s
s’ s’
...
tran(s’, b) = error
José Miguel Rivero
Lexical Analysis – p. 40/46
2.7 Comparación de los dos métodos
Resumen de costes:
Autómata Coste temporal Coste espacial
AF N
AF D
O(|er| · |w|)
O(|w|)
O(|er|)
O(2|er| )
En general, cuando los dos métodos son viables (el coste
en espacio del AFD no es excesivamente mayor) podemos
concluir que:
AF N es adecuado cuando |er| ↓↓
AF D es adecuado cuando |w| ↑↑ o |er| ↑↑
José Miguel Rivero
Lexical Analysis – p. 41/46
Cálculo perezoso (lazy) de transiciones
Combina las necesidades de espacio del autómata AFN
con las ventajas en tiempo del autómata AFD.
Se trabaja a partir del autómata indeterminista, calculando
sólo los subconjuntos de estados que se van necesitando.
Estos subconjuntos (y sus transiciones) se guardan en una
cache de forma que no hace falta volver a calcularlos de
nuevo.
Resumiendo sus ventajas:
Menor necesidad de espacio: tamaño de la tabla del
AFN ( O(|er|) ) + tamaño de la cache
No se calculan transiciones entre estados que nunca
son utilizadas. Esto lo hace casi tan rápido como los
AFD
José Miguel Rivero
Lexical Analysis – p. 42/46
2.8 Algoritmo de análisis léxico
Ejercicio: suponiendo que los carácteres de la entrada w (contando eof )
se encuentran en un array A indexado desde 0, se trata de modificar el
algoritmo anterior para que reconozca el prefijo más largo
v = A[0] · · · A[k − 1] que encaja con alguna de las expresiones regulares
eri .
Cuando la tabla Dtran no contiene transición para un cierto estado s y
carácter a entonces contiene el valor -1. Para distinguir un estado final de
uno no final tenemos otro array DF in que dado un estado s contiene true
si s es un estado final y false en caso contrario.
El algoritmo tiene que devolver el índice k y el estado (final) s tal que:
la palabra A[0] · · · A[k − 1] es el prefijo más largo que encaja con
alguna eri . Si no existe tal prefijo devuelve -1.
en ese momento el autómata se encuentra en estado s (que es el
estado final de la expresión regular eri correspondiente)
José Miguel Rivero
Lexical Analysis – p. 43/46
Algoritmo de análisis léxico (cont.)
p := f := 1; l := 0;
∀i : 1 ≤ i ≤ n : qi := Inii ;
// Estados iniciales
while p ≤ m do
∀i : 1 ≤ i ≤ n : qi := δi ( qi , IN[p] ); p :=p + 1;
// Transición de estados
if ∃ i : 1 ≤ i ≤ n : qi ∈ Fi then
// Estado f inal
l := p − 1; a = smallest i su ch that qi ∈ Fi ;
elseif ∀ i : 1 ≤ i ≤ n : qi ∈ Erri then
// Estados pozo
if l ≥ f then
Generate token of type a with word ; IN[f.. l]
p := f := l + 1;
l := f − 1;
∀i : 1 ≤ i ≤ n : qi := Inii ;
else
Lexical error
endif
endif
endwhile
if l < f then Lexical error endif
José Miguel Rivero
Lexical Analysis – p. 44/46
2.9 Tratamiento de errores léxicos
¿Cuándo se produce un error léxico?
Recordemos que el analizador léxico intentar obtener el
prefijo v más largo de la palabra w que está analizando. Ese
prefijo tiene que formar parte del lenguaje de alguna de las
expresiones regulares er1 , er2 , . . . , ern .
En este proceso se puede llegar a un estado s desde el cual
no haya transición definida para el símbolo en curso a. Esto le
obligaría a devolver el prefijo anterior más largo que ya había
reconocido.
¿Qué ocurre si todavía no había podido reconocer ningún
prefijo que encajara en alguna expresión regular antes de intentar
buscar otro aún más largo? Pues que la seqüencia que comienza
con el primer carácter de w no forma parte del lenguaje de
ninguna de las eri : ⇒ se produce un error léxico.
José Miguel Rivero
Lexical Analysis – p. 45/46
Tratamiento de errores léxicos (cont.)
Hay diferentes formas de tratar estas situaciones de error.
Estas son algunas de ellas:
En modo pánico. Se ignora el primer carácter de w (y sucesivos si
también es necesario) hasta que podamos reconocer algún prefijo en
la entrada
Sólo se borran caracteres extraños. Por ejemplo, aquellos que no
forman parte del alfabeto del lenguaje: ’¿’, ’ç’, ’@’, . . . en lenguajes
como C o Pascal
Se realizan correcciones: insertar un carácter, reemplazar un
carácter por otro distinto, intercambiar adyacentes (wihle pasa a ser
while), etc. En general se intenta que el número de correciones sea
el mínimo necesario. No es una técnica muy frecuente en la práctica
José Miguel Rivero
Lexical Analysis – p. 46/46

Documents pareils