MemoriaLéxico

Anuncio
El analizador léxico lo hemos realizado con la herramienta JFlex, el cual es una
herramienta para generar analizadores léxicos y es para lenguaje Java. Hemos decidido
utilizado está herramienta porque creemos que nos reduce el esfuerzo de tener que
programar un analizador léxico sin ayuda de una herramienta, de esta forma sólo nos
tenemos que preocupar de diseñar las expresiones regulares, con sus acciones
correspondientes, y JFlex nos genera el “switch” que reconoce a esas expresiones.
Para construir el analizador léxico basta con darle las expresiones regulares formadoras
de los componentes léxicos que componen el lenguaje y las acciones semánticas
asociadas al reconocimiento de cada token y JFlex te construye el analizador léxico para
esas expresiones. En caso que una misma cadena de caracteres se pueda corresponder
con dos o más patrones o reglas JFlex seleccionará seguirá leyendo caracteres de la
entrada hasta que no case con ningún patrón en cuyo caso devolverá el carácter a la
entrada, retrocediendo el puntero de lectura del archivo una posición y eliminará el
carácter de la cadena actual que está intentando reconocer. Después de hacer esto, si ya
sólo se puede corresponder con un patrón se reconoce la palabra como un token
correspondiente a ese patrón y si aún existe ambigüedad respecto a que patrón podría
pertenecer la palabra se escoge la primera regla que se encuentre respecto a la que haya
ambigüedad. Una vez que se haya reconocido un conjunto de caracteres como
pertenecientes a un token se ejecutan el bloque de sentencias asociadas al
reconocimiento de ese patrón.
Como casi siempre que se utiliza un analizador léxico se hace conjuntamente a un
analizador sintáctico y semántico, supongo que los token los solicita el analizador
sintáctico al léxico mediante la función “siguiente carácter”. Esta función produce que
el léxico empiece a procesar caracteres del fichero de entrada hasta que encuentre el
conjunto de caracteres mayor que pueda reconocer caso en el que devuelve token al
analizador sintáctico y se queda a las espera de la siguiente orden “siguiente token”, es
decir, devuelve el control al analizador sintáctico.
EXPRESIONES REGULARES RECONOCIDAS
Los tipos de token que reconocemos en el analizador léxico son: número entero, número
real, operador de asignación, operador lógico, operador relacional, operador
matemático, palabra clave, puntero, identificador, cadena (tipo string), carácter (tipo
char), signo de puntuación y fin de fichero.
Cada tipo de token tiene asociado una, o varias, expresiones regulares para poder
identificarlos. La notación que he seguido para describir los patrones que reconocen a
los token es la usada por JFlex por lo que antes de describir los patrones de las
expresiones regulares daré una pequeña explicación de la notación de JFlex.
 “”: sirve para encerrar cualquier cadena de literales. Por regla general no es
necesario encerrar los literales entre comillas a no ser que incluyan símbolos
especiales, esto es, el patrón “WHILE” y el patrón WHILE son equivalentes;
pero para representar p.ej. el inicio de comentario en Modula-2 sí es necesario
entrecomillar los caracteres que componen al patrón: “(*”, ya que éste
contiene, a su vez, símbolos especiales.
 \: hace literal al siguiente carácter. Ej.: \” reconoce unas comillas. También se
utiliza para expresar aquellos caracteres que no tienen representación directa
por pantalla: \n para el retorno de carro, \t para el tabulador, etc.
 [ ]: permiten especificar listas de caracteres, o sea uno de los caracteres que
encierra, ej.: [abc] reconoce o la ‘a’, o la ‘b’, o la ‘c’, ( [abc] / (a|b|c) ).
Dentro de los corchetes los siguientes caracteres también tienen un sentido
especial:
o -: indica rango. Ej.: [A-Z0-9] reconoce cualquier carácter de la ‘A’ a
la ‘Z’ o del ‘0' a ‘9'.
o ^: indica compleción cuando aparece al comienzo, justo detrás de “[”.
Ej.: [^abc] reconoce cualquier carácter excepto la ‘a’, la ‘b’ o la ‘c’.
Ej.: [^A-Z] reconoce cualquier carácter excepto los de la ‘A’ a la
‘Z’.
 ?: aquello que le precede es opcional1. Ej.: a? / ( a | g ). Ej.: [A-Z]? reconoce
cualquier letra de la ‘A’ a la ‘Z’ o bien g. Ej.: a?b / ab | gb.
 .: representa a cualquier carácter (pero sólo a uno) excepto el retorno de carro
(\n). Es muy interesante porque nos permite recoger cualquier otro carácter
que no sea reconocido por los patrones anteriores.
 |: indica opcionalidad (OR). Ej.: a|b reconoce a la ‘a’ o a la ‘b’. Ej.: .|\n reconoce
cualquier carácter. Resulta curioso el patrón (.|\n)* ya que por aquí entra el
programa entero, y como yylex() tiene la premisa de reconocer el lexema más
largo, pues probablemente ignorará cualquier otro patrón. También resulta
probable que durante el reconocimiento se produzca un error por
desbordamiento del espacio de almacenamiento de la variable yytext que,
recordemos, almacena el lexema actual.

*: indica repetición 0 o más veces de lo que le precede.

+: indica repetición 1 o más veces de lo que le precede.

( ): permiten la agrupación (igual que en las expresiones aritméticas).
 { }: indican rango de repetición. Ej.: a{1,5} / aa?a?a?a? Las llaves vienen a ser
algo parecido a un * restringido. También nos permite asignarle un nombre
a una expresión regular para reutilizarla en múltiples patrones; esto se verá
más adelante.
 !: encaja con cualquier lexema excepto con los que entran por el patrón que le
sucede. Ej.: !(ab) encaja con cualquier cosa excepto el lexema “ab”.
Una vez que ya se conoce la notación de JFlex comenzamos a redactar los patrones:
LETRAS= [a-zA-Z]
DIGITOS= [0-9]
NUMERO_Entero= {DIGITOS}+
NUMERO_Real= {DIGITOS}+("."{DIGITOS}+)?(e("+"|"-")?{DIGITOS}+)?
OperadorLogico= “and” | “or” | “not”
OperadorMatematico= “+” | “-” | “*” | “/” | “div” | “mod” | “shr” | “shl “| “in”
PalabraClave= “And” | “Array” | “Begin” | “Case” | “Char” | “Const” | “Div” | “Do” |
“Downto” | “Else” | “End” | “File” | “For” | “Function” | “Goto” | “If” | “In” | “Integer” |
“Label” |”Mod” |”Nil” |”Not” |”Of” |”Or” |”Packed” |”Procedure” |
“Program” |”Record” |”Repeat” |”Set” |”Shl” |”Shr” |”String” |”Then” |”To” |”Type”
|”Unit” |”Until” |”Uses” |”Var” |”While” |”With”
Puntero= “^”
IDENTIFICADOR={LETRAS}({LETRAS}|{DIGITOS})*
CHAR= (\'[^\']\')|(\'\'\'\')|("#"{NUMERO_Entero})
CADENA= (\"[^\"]*\")|(\"[^\"]*\"(\"[^\"]*\")*)
SALTOLINEA= \r|\n|\r\n
OperadorRelacional= “<“ | “<=“ | “>“ | “>=“ | “=“ | “<>“
SignoPuntuacion=,|"."|;|:|\"|\'|\(|\)|\[|\]
OperadorAsignacion= :=
ESPACIOS=" "+
ERRORCARACTER=[^LETRAS]
LITERALMALO="#"{LETRAS}+
NUMEROMALO={DIGITOS}+"."{DIGITOS}+"."{DIGITOS}+
DESCRIPCIÓN DE LOS TOKENS
Los token los he diseñado como objetos con 4 atributos en vez de los 2 habituales. A los
dos atributos tradicionales “tipo de token” y “atributo” le he añadido el número de línea
y de columna del token para facilitar la gestión de errores, ya que de esta forma es
mucho más fácil determinar la localización del error ya sea léxico, sintáctico o
semántico, bastaría con mirar la línea y columna del primer token que produce el error y
notificar el error en dicha línea y columna.
Para poder consultar cualquiera de los cuatro atributos se invoca a su correspondiente
función getter.
El campo “tipo de token” es una clase que imita a un enumerado tradicional como los de
C o Pascal. Los posibles valores de este enumerado y sus correspondientes atributos
son:
 IDENTIFICADOR: representa a los identificadores del programa, pero que no
sean palabras clave del programa, es decir, definidos por el programador.
Su atributo es un puntero a su posición en la tabla se símbolos.

PALRESER: representa a todas las palabras clave o reservadas del programa.
Su atributo es un enumerado con la palabra clave de la cual se trata.

OPERAMATE: representa a los operadores matemáticos.
Su atributo es un enumerado con el operador del cual se trata.

OPERARELA: representa a los operadores relacionales.
Su atributo es un enumerado con el operador relacional del cual se trata.

OPERAASIG: representa al operador de asignación.
Su atributo es un puntero a null ya que no se requiere de información extra.

OPERALOGI: representa a los operadores lógicos.
Su atributo es un enumerado con el operador lógico del cual se trata.

SIGNOPUNTUACION: representa a los signos de puntuación.
Su atributo es un enumerado con el signo de puntuación del cual se trata.

CADENA: representa a las cadenas de caracteres. Las cadenas de caracteres se
considerar a todo lo que se encuentre entre dos comillas dobles.
Su atributo es un String de Java con el conjunto de caracteres de la cadena.

LITERAL: representa a los caracteres individuales. Los caracteres individuales
van encerrados entre comillas simples y sólo puede ir uno, o también se puedes
representar con una almohadilla y su número ASCII.
Su atributo es un String de Java con carácter.

ENTERO: representa a los números enteros.
Su atributo es un Integer de Java con el valor del número entero.

REAL: representa a los números reales.
Su atributo es un double de Java con el valor del número real.

PUNTERO: representa al signo de puntero.
Su atributo es un puntero a null ya que no se requiere de información extra.

FINFICHERO: representa al fin del fichero.
Su atributo es un puntero a null ya que no se requiere de información extra.
GESTIÓN DE ERRORES
Para gestionar los posibles errores que se produzcan al procesar la información de
entrada, ya sea en el analizador léxico, sintáctico o semántico, se tiene un módulo que
interaccionará con todos ellos, el gestor de errores.
Al gestor de errores se le comunican los mensajes de error conjuntamente con su línea y
columna. La forma de comunicar un error es mediante la función añadir error y la forma
de mostrarlos mediante la función mostrar errores que los muestra por pantalla (sirve
para depurar) y también genera un documento con los errores acaecidos, en caso de que
se haya producido alguno.
Los errores que hemos pensado son errores al introducir caracteres no reconocidos fuera
de las cadenas de caracteres (ERRORCARACTER), la formación de un carácter de
forma incorrecta colocando letras en vez de un número detrás de una almohadilla
(LITERALMALO), formar un número decimal con dos puntos (NUMEROMALO) y
cambiar el orden de los caracteres del operador de asignación poniendo primero el igual
y después los dos puntos (ASIGNACIONMALA).
TABLA DE SÍMBOLOS
Nuestra tabla de símbolos sólo tendrá una tabla para identificadores, en realidad varias
para gestionar los ámbitos pero todas de identificadores, ya que no JFlex nos reconoce
directamente los distintos tipos de token y no es necesario que los introduzcamos en la
tabla de símbolos ni las palabras clave, ni operadores, ni cadenas, ni los caracteres.
Como para Java todo son punteros no hay problema en que en el token vaya la cadena
de texto directamente ya que en realidad es solamente un puntero a esta. Tampoco
necesitamos una tabla con palabras reservadas porque JFlex nos las identifica y no hay
que comprobar que este en dicha tabla, insertándola si no está o si está indicando que
palabra clave es.
CLASES
En e paquete principal tenemos el analizador léxico (Liner), el gestor de errores y la
clase token.
La clase del analizador léxico la hemos llamado Liner, los parámetros son la ruta del
archivo que se vamos a analizar, el gestor de errores que se va a utilizar y la tabla de
símbolos. Se incorporan estos parámetros porque sino no podría comunicar entre ellos
de esta forma se utiliza el puntero que se tiene para hacer llamadas a añadir errores y a
insertar de la tabla de símbolos. Para que el analizador léxico te devuelva el siguiete
token se utiliza la función nextToken.
El Gestor de errores es un array que va guardando los mensajes con su correspondiente
línea y columna.
En el paquete tipos tenemos todos los tipos que necesitamos, básicamente los
enumerados.
Descargar