Documente Academic
Documente Profesional
Documente Cultură
INFORMÁTICA DE OVIEDO
CURSO ACADÉMICO: 2005-2006
TEORÍA DE AUTÓMATAS
javac analizador.java
Obtendremos una clase (Analex.class u otro nombre con extensión .class) que
contiene al analizador léxico listo para empezar a trabajar con él.
2. FICHERO DE ESPECIFICACIÓN
Un fichero de especificación JLex tiene la siguiente organización
Código de usuario
%%
directivas JLex
%%
Reglas para las expresiones regulares
Los caracteres %% distinguen una sección de otra. Deben estar colocados al principio
de la línea y el resto de la línea no debe usarse.
Para hacer referencia posteriormente a estas definiciones basta con poner el nombre
entre paréntesis: {DIGITO} o {IDENT}. En el primer caso DIGITO denota a cadenas de un
solo carácter numérico e IDENT denota cadenas de cualquier longitud cuyo primer carácter
sea alfabético y el resto, si lo hubiera, alfanumérico.
Si posteriormente escribimos {DIGIT}+"."{DIGIT}* es equivalente a escribir la
expresión regular ([0-9])+"."([0-9])* que denota cadenas de uno o más dígitos seguidos por
un punto y por cero o más dígitos (por ejemplo: 23.46, 1.1, 34. )
Si la entrada fuese 33, se elegiría la segunda regla, ya que la primera capturaría cada
dígito por separado, mientras la segunda capturaría la cadena entera.
Si aún así, sigue habiendo conflicto, se elige la primera regla que se encuentre en el
fichero que encaje con la entrada.
Las reglas del fichero de especificación deben cubrir cualquier posible entrada, ya que
si no encuentra una regla para una entrada, el analizador léxico eleva un error. Para garantizar
esta condición, es suficiente poner como última regla:
. Acciones a realizar cuando llega algo que no encaje en las reglas anteriores.
2.3.1 Expresiones Regulares en JLex
Las expresiones regulares no deben contener espacios en blanco, que se interpretan
como final de la expresión regular. A menos que vayan entre comillas dobles : “ “ lo que se
interpreta como una expresión regular que encaja con un espacio en blanco en el texto.
El alfabeto para JLex es el conjunto de caracteres ASCII y los siguientes son caracteres
especiales o metacaracteres:
? * + | ( ) ^ $ . [ ] { } “ \
3.1 EJEMPLO 1
Se puede encontrar en el fichero ejemplo1.lex
import java.lang.System;
class Analex {
public static void main(String argv[])
throws java.io.IOException {
Yylex yy = new Yylex(System.in);
while (yy.yylex() != null) {}
}
}
class Yytoken {
Yytoken () {}
}
%%
%%
a {System.out.println("A");}
b {System.out.println("B");}
\n { }
.+ {System.out.println(yytext());}
Una vez procesado con JLex, tendremos un programa que lee de teclado y reproduce el
carácter leído a la salida (pantalla). Excepciones: la letra a, que imprime A, la letra b (imprime
B) y el salto de línea, que no lo reproduce.
Sección de declaración, sirve para dar nombre a ciertas expresiones de forma que se
simplifique la escritura de las reglas o para declarar variables que usen más adelante. En
nuestro ejemplo esta sección está vacía, pero más adelante veremos en otros ejemplos cómo
se puede usar.
Las reglas son las que especifican qué acción se debe realizar cuando se encuentran
cadenas que responden a cada expresión regular. En nuestro caso, las expresiones regulares
son muy sencillas: la expresión regular a denota al carácter "a", la expresión regular b denota
"b", \n, denota el salto de línea y .+ denota cualquier combinación de símbolos (salvo el \n).
Veremos que las expresiones pueden ser mucho más complicadas (en realidad se
pueden utilizar todos los operadores que vimos en la primera práctica con XEmacs). Las
acciones que hemos incluido en nuestro ejemplo también son muy sencillas. Hemos usado la
función System.out.println, que se usa para escribir en la salida estándar (la pantalla) y hemos
indicado que cuando se encuentre "a" se escriba "A" y viceversa.
La primera parte incluye el código de usuario, que indica qué debe hacer realmente el
programa. La situación más sencilla es la que aparece en nuestro ejemplo. La función main
llama a la función yylex. Esta función yylex es la que genera lex a partir de las reglas que
hemos especificado en el fichero de entrada. Y lo que hace es precisamente procesar la
entrada e ir aplicándolas. Más adelante veremos código de usuario un poco más elaborado.
3.1.1 Funcionamiento
La obtención de un programa ejecutable a partir de una especificación lex incluye
varios pasos:
Obtener código java a partir de la especificación lex:
java JLex.Main ejemplo1.lex
En este ejemplo, se genera un fichero llamado ejemplo1.lex.java. Se recomienda
renombrarlo (mv ejemplo1.lex.java ejemplo1.java). Compilar el código java
generado, para obtener el programa ejecutable.
Para compilarlo: javac ejemplo1.java. Generará un conjunto de clases (ficheros
.class). Uno de ellos es Analex.class que contiene la función main.
Ejecución. Tras los dos pasos anteriores tendremos un programa que lee de la entrada
estándar y va aplicando las reglas que hemos escrito. Para ejecutarlo: java Analex y a
continuación escribimos texto. Veremos cómo el programa vuelve a escribirlo convirtiendo la
a y b minúsculas en mayúsculas. Para finalizar la conversión pulsaremos Ctrl+D, que indicará
al programa que la entrada ha finalizado.
También podemos utilizar el programa sobre un fichero ya escrito, en lugar de sobre el
texto que vamos escribiendo. Para ello, habrá que redireccionar la entrada estándar del
programa del siguiente modo: java Analex < prueba siendo prueba el fichero que
queremos procesar.
3.2 EJEMPLO 2
La acción más fundamental que puede llevarse a cabo con JLex es la sustitución. En el
ejemplo anterior hemos visto cómo el programa generado sustituía cada aparición de "a" por
"A" y de “b” por “B”. Estas sustituciones pueden involucrar simplemente caracteres, cadenas
o expresiones regulares complejas.
Por ejemplo, el siguiente código serviría para cambiar cada aparición de "np" por "mp"
y de “nb” por “mb” (ejemplo2.lex).
import java.lang.System;
class Analex {
public static void main(String argv[])
throws java.io.IOException {
Yylex yy = new Yylex(System.in);
while (yy.yylex() != null){}
}
}
RET 80 cm;
……
import java.lang.System;
class Analex {
public static void main(String argv[])
throws java.io.IOException {
Yylex yy = new Yylex(System.in);
while (yy.yylex() != null) {}
}
}
4.1 EJERCICIO 1
Pruebe el analizador léxico anterior con el fichero PruebaMovelan. ¿A qué se deben los
mensajes de error que aparecen? Justifica cada uno de ellos.
4.2 EJERCICIO 2
Modifique el fichero .lex anterior para que reconozca, además de los enteros, los
números reales (con punto decimal, con o sin signo, con o sin parte entera, pero al menos con
parte entera o decimal). Añada también las unidades de giro grados (GRA) y radianes (RAD).
En el proceso de análisis, escribirá, de forma análoga a como se hace con los enteros, el
mensaje real <valor>, por cada número real que encuentre en el fichero de entrada. Para
las nuevas unidades el mensaje será unidad de giro<grados> o unidad de
giro<radianes>.
4.3 EJERCICIO 3
Añada, a los operadores del lenguaje ensam, la operación de rotación (ROT) y la
repetición de órdenes (REP) así como el paréntesis abierto y el cerrado. Imprima por pantalla
los mensajes Rotar, Repetir, paréntesis abierto y paréntesis cerrado,
respectivamente.
4.4 EJERCICIO 4
Modificar las expresiones regulares para que reconozcan las órdenes aunque estén
formadas por la combinación de mayúsculas y minúsculas (p.e. aVa, ReT, Rep, rOT, ...).