Lenguajes de Programación y Procesadores Primera Sesión de Prácticas Fecha de entrega 10 de mayo del 2019. Convocatoria de Junio. Fecha de entrega 15 de julio del 2019. Convocatoria de Septiembre.
La práctica se entregará comprimida al Tutor por correo electrónico. Una vez valida por el Tutor será subida al entorno Alf; Curso Virtual -> prácticas -> Tareas. La falta entrega tanto al Tutor como en el entorno Alf será motivo suficiente para quedar excluida de la convocatoria. Hay que registrarse en la aplicación de prácticas en Alf: Curso virtual -> práctica -> Apuntarse a grupo de prácticas: Centro Asociado: Asturias Tutor: José Pérez Gamboa Entrega al tutor un zip o rar, con el siguiente formato: LPP_nombre_apellidos.extensión Enviar al correo electrónico
[email protected], Asunto: entrega práctica LPP La entrega de la práctica consta de los siguientes elementos: Los resultados indicados en el enunciado para cada una de las partes. Los códigos fuente adecuadamente documentados. Documento pdf con la siguiente información: o Datos de la asignatura y del alumno: Nombre y código de la asignatura Nombre y Apellidos NIF Centro Asociado o Justificación de las decisiones tomadas en el desarrollo de la práctica.
Solo se realizará una única entrega que incorpore la parte 1 y la parte 2
Parte 1 Se desea generar un analizador léxico para un lenguaje de programación simplificado. Desde un punto de vista léxico, un programa escrito en nuestro lenguaje de programación es una secuencia ordenada de TOKENS. Un TOKEN es una entidad léxica indivisible que tiene un sentido único dentro del lenguaje. En términos generales es posible distinguir entre diferentes tipos de TOKENS: los operadores aritméticos, relacionales y lógicos, delimitadores como paréntesis o corchetes, o las palabras reservadas del lenguaje. A continuación se describirán en detalle cada uno de estos tipos, junto con otros elementos que deben ser tratados en la fase de análisis léxico. ... Se pide generar un analizador léxico a partir de la especificación anterior y con la ayuda de la herramienta JFlex (http://www.jflex.de/), que tomará como entrada un archivo .flex (con sintaxis flex) y generará el analizador como salida. El analizador deberá escribir los tokens reconocidos, indicando además el tipo de token. Junto con este enunciado se aporta un documento con detalles acerca de la sintaxis flex y el generador de analizadores léxicos JFLEX.
ENTREGA DE LA PARTE 1 – ANALIZADOR LÉXICO En la parte 1 del analizador léxico debe de entregarse los códigos fuentes libre de errores de compilación y comentados adecuadamente:
Analizador de la practicas con extensión .flex, p. ej: analizadorPractica.flex Clase de java que contenga el main para lanzar el analizador léxico, p. ej: mainPractica.java Otras clases de java necesarias para la ejecución de la práctica si las hubiese. Ficheros de prueba, ejemplo01.txt, ejemploErrores01.txt,…
En el caso de haber desarrollado la práctica en Eclipse o NetBeans se debe de aportar el proyecto con los ficheros de prueba.
Memoria (en pdf, y que incluya la parte 1 y la parte 2)
Indicar las directivas, macros, estados y errores que se incluyen y una pequeña descripción. Incluir los casos de pruebas utilizados y sus resultados. Indicar si se ha usado un IDE determinador y un ejemplo de ejecución del analizador léxico.
Compilador Un compilador toma como entrada un programa escrito en un lenguaje fuente normalmente de alto nivel y produce un programa equivalente escrito en lenguaje escrito en un lenguaje objeto de bajo nivel.
Estructura de un compilador
El análisis léxico, el trabajo del analizador léxico es leer los caracteres del código fuente y formatearlos en unidades lógicas para que lo aborden las partes siguientes de un compilador, un analizador léxico o scanner va leyendo caracteres del fichero hasta encontrar una entidad con significado léxico (Token). Token son una estructura de datos que contiene información acerca del mismo, tipo, lexema, número de fila y columna, valor, etc. Un Token es una unidad léxica indivisible con significado único dentro del lenguaje. Un patrón léxico es una expresión abstracta llamada expresión regular que permite identificar unívocamente un tipo de token y referenciar al conjunto de todos los tokens que se ajustan a él. La cadena de caracteres específica de un token que se ajusta al patrón léxico de su tipo se llama lexema.
Errores léxicos, los errores léxicos son aquellos que se producen en el ámbito del reconocimiento de patrones para construir tokens del lenguaje. Podemos establecer una clasificación de errores de naturaleza léxica • Errores básicos • Aparición de caracteres ajenos al conjunto T (ñ, ç, etc.) • Ausencia de concordancia con ningún patrón (1abc, +*, etc.) • Errores complejos • Cadena de caracteres sin cierre (falta “ al final) • Cadena de caracteres sin apertura (falta “ al inicio) • Comentario sin cierre ( falta */ al final) • Comentario sin apertura (falta /* al inicio) • Error de anidamiento de comentarios (/* …/*….*/)
jFlex jFlex es una herramienta para la generación de analizadores léxicos escritos en java. A partir de un fichero de especificación que describe las características léxicas de un lenguaje, jFlex genera un código fuente compilable que puede ser utilizado como analizador léxico. • • •
analizador.flex, especificación del analizador léxico. Todo cambio en el analizador léxico debe indicarse aquí. analizador.java, se genera a partir de la especificación del analizador léxico. No se debe de incluir código manualmente. analizador.class, clase compilada que puede ser interpretada por la JVM.
analizador.jflex El fichero de especificación de JFlex, está divido en tres secciones separadas por el delimitador %%. • • •
Sección de código de usuario, esta sección se utiliza para incluir cualquier declaración java (paquete, importación o clase) que sea necesario para compilar el analizador léxico. Sección de directivas, incluye las directivas para la gestión de fila, columna y lexema, declaración de estados y macros e inclusión de código de inicialización. Sección de reglas patrón-acción, donde se definen una familia de escáneres léxicos, cada uno asociado a un estado distinto (YYINITIAL, COMMENT, etc), se puede saltar de uno a otro. La definición se basa en reglas patrón acción que indican el código java que debe ejecutarse cuando la entrada encaja con determinado patrón léxico. Si el código java no acaba con un return el scanner continua buscando un nuevo token.
Sección de código de usuario La sección de código de usuario se utiliza para incluir cualquier declaración java (paquete, importación o clase) que sea necesario para compilar el analizador léxico. package practicaLPP; import java.io.*;
Sección de directivas En esta sección se incluyen directivas de gestión del token (fila, columna, lexema), declaración de estados, macros, código de inicialización o comprobación de final de fichero, etc.
Directiva %class name %public %function name %interface name %type name %integer %intwrap %yyeof %cup %state nombe nombre = valor %full %unicode %{ … %} %init{… %} %intthow{… %} %eof{… %} %eofval{… %} %eofthrow{… %} %ignorecase %char %line %column %notunix
Descripción Da nombre a la clase del analizador Define como pública la clase del analizador Da nombre a la función de análisis Declara los interfaces que debe implementar el analizador Define el tipo de retorno de la función de análisis Define el tipo de retorno de la función de análisis como un int Define el tipo de retorno de la función de análisis como un Integer Define la constante Yjlex. YYEOF (obligatorio uso de %integer) Habilita la compatibilidad con Cup Define un estado Define una macro (LETRA = [A-Za-z]) Utiliza el código ASCII extendido 8 bits Utiliza codificación UNICODE de 16 bits Se incluye el código java en la case del analizador.java Incluye código java dentro de los constructores Declara excepciones que el método del analizador puede lanzar Incluye código que se ejecuta tras encontrar el final del fichero EOF Define el valor de retorno al encontrar EOF Excepción que se lanza al encontrar el EOF No distinguir entre mayúsculas y minúsculas Contabiliza el número de caracteres en la variable yychar Contabiliza el número de líneas en la variable yyline Contabiliza el número de columnas en la varible yycolumn Reconoce \r\n como carácter de nueva línea
Declaración de directivas: %public %class analizadorPractica %type Void %full o %unicode %ignorecase En esta parte se pueden añadir además de directivas, crear macros e insertar código java: LETRA=[A-Za-z] DIGITO=[0-9] No_DIGITO=[^[0-9]] FinDeLinea = \n|\r|\r\n PalabrasReservadas = "MODULE"|"BEGIN"|"FOR"|"VAR"|"IF"|"ELSE". . . %{ private int commentCount = 0; %}
%eof{ if (commentCount>0) { // Si los comentarios no estan bien balanceados da un error System.out.println("ERROR LEXICO COMENTARIOS mal balanceados"); } %eof}
Sección de reglas de patrón-acción En la sección de reglas de patrón-acción se definen las acciones que se ejecutarán al encontrar un patrón determinado, cada una de las reglas patrón-acción irán asociadas a un estado (YYINITIAL, COMENTARIOS, CADENAS). YYINITIAL es el estado inicial y puede ser obviado si solo hay un estado.
Utilizaremos comillas para encerrar las palabras reservadas, operadores, delimitadores, etc que el analizador debe reconocer, por ejemplo: “MODULE”. Despliegue de macros, se utilizan los caracteres { y } para encerrar el nombre de la macro, definida en la sección de directivas, que se quiere desplegar, por ejemplo: {IDENTIFICADOR}. Utilización de estados, cada estado declarado en la sección de directivas corresponde con un autómata distinto. Cada regla debe asociarse a un estado precediendo al patrón léxico el nombre del mismo encerrado entre < y >. Desde una regla r de un estado A se puede saltar a un estado B mediante la instrucción yybegin(B) invocada en la acción de r. El estado inicial por defecto es YYINITIAL y no es preciso declararlo, por ejemplo:
Conflictos: • Si más de una regla puede lanzarse ante una cadena de entrada el analizador escoge la regla que se ajusta a una cadena más larga. • Ante igualdad de longitud el analizar escoge la regla que se encuentre definida en primer lugar. Por tanto el orden de ubicación de las reglas es importante • Si el analizador recibe una cadena que no se ajusta a ningún patrón se produce un error. Por tanto todas las entradas deben tener una regla. Para garantizar esto existe el patrón comodín ‘.’ que se ajusta cualquier patrón no definido antes
Secuencia \b \n \t \f \r \ddd \xdd \udddd \^C \c yychar yyline yytext()
Descripción Retroceso Nueva línea Tabulador Avance de página Retorno de carro Número octal Número hexadecimal Hexadecimal de 4 digitos Carácter de control Backslash seguido de c Número de carácter Número de línea lexema
Secuencia $ . “. . .” {name} * + ? (…..) […..] [^…..] a-z
Descripción Fin de fichero Cualquier carácter ……. Expansión de una macro Cero o más repeticones Una o más repeticones Opcional Agrupación de expresiones regulares Conjunto de caracteres Conjunto complementario Rango a-z
yycolumn
Número de columna
{PalabrasReservadas}
{ System.out.println("Encontrado PALABRA RESERVADA " + yytext() ); }
"MODULE"
{ System.out.println("Encontrado PALABRA RESERVADA " + yytext() ); }
"("
{ System.out.println("Encontrado DELIMITADOR " + yytext() ); }
"+"
{ System.out.println("Encontrado OPERADOR " + yytext() ); }
{DIGITO}
{ System.out.println("Encontrado UN DIGITO " + yytext() ); }
[0-9]
{ System.out.println("Encontrado UN DIGITO " + yytext() ); }
{CadenaErrornea}
{ System.out.println("ERROR LEXICO CadenaErronea--> "+yytext());
.
{ System.out.println("ERROR LEXICO --> "+yytext()); }
[^]
{ System.out.println("ERROR LEXICO --> "+yytext()); }
Palabras claves o identificador Las palabras claves o reservadas del lenguaje de la práctica están declaradas en mayúsculas como MODULE, AND, IF, etc.., al ser el lenguaje no case sensitive (sensible a mayúsculas) si en el código fuente del lenguaje hubiese una palabra como module o Module como debería ser considerado como una palabra clave o como un identificador. Por ejemplo: MODULE test; VAR module : INTEGER BEGIN module := 1; END test;
En el caso del ejemplo MODULE se considera una palabra clave o reservada del lenguaje, pero module se debe de considerar como un identificador por el analizador léxico. Comento esto dado que en función de su tratamiento va en concordancia con el uso de la directiva %ignorecase.
En caso de duda de un token debe ser considerado como una palabra reservada o un operador o un delimitador, usar siempre la opción más específica. Por ejemplo: • •
AND operador lógico [ o ] como operador de acceso.
Cadena de caracteres Cuando os encontréis una cadena de caracteres “HOLA MUNDO” • •
Se puede reconocer toda la cadena incluyendo las comillas iniciales y finales o Reconocer las comillas como delimitador y cadena de caracteres
WRITESTRING(“HOLA MUNDO”); //Es correcto contiene una cadena de caracteres WRITESTRING(“HOLA MUNDO” “hola mundo”); //Es correcto contiene dos cadenas de caracteres WRITESTRING(“HOLA MUNDO”hola mundo”); //Error léxico WRITESTRING(“HOLA MUNDO ”);
//Error léxico
ENTEROS Los números enteros o constantes literales enteras que debe de reconocer el analizador deben ser valores entero no negativos, por lo tanto 0, 1, ..,53,..,125 serán enteros válidos Ahora bien 01, 067, 000101 no son enteros válidos Dos opciones: • •
Emitir un error léxico si se encuentra 067 Reconocer 0 y 67 como dos enteros
La macro NumeroEntero = [0-9]* no es válida para reconocer enteros no negativos. Aunque la macro detecte un número entero no negativo, sino se crea una macro para detectar un error léxico de un entero no válido entonces el analizador se comportará como en la segunda opción.
Identificadores Los identificadores consisten en una secuencia ordenada de caracteres y dígitos que comenzarán obligatoriamente por una letra: •
Test01, a1, ejemplo, etc. son identificadores correctos
• 1ejemplo, _a1, @test, etc. se deben de reconocer como un error léxico.