13048- PROCESADORES DE LENGUAJE Curso: 2003-2004 Práctica 1: Diseño de un Analizador Léxico y Sintáctico Se desea implementar un analizador léxico y sintáctico de un pequeño subconjunto del lenguaje de consulta de Bases de Datos SQL. Como sabéis una sentencia de este lenguaje viene definida por tres clausulas o partes: la clausula SELECT, la clausula FROM y la clausula WHERE. Un ejemplo de una consulta que permite mostrar los nombres y apellidos de los estudiantes de edad superior a 20, es: select estudiantes.nombre, estudiantes.apellidos from estudiantes where (estudiantes.edad>20); Otro tipo de sentencias un tanto mas complejas son las que usan la función group by. Por ejemplo si queremos mostrar la edad media de los alumnos por titulación a la que pertenecen y que estén en segundo curso. Hemos supuesto que toda esta información está en una única tabla (Tabla estudiantes). select avg(estudiantes.edad) from estudiantes where (estudiantes.curso=2) group by estudiantes.titulacion; Otro caso a considerar es el caso en el que la información se encuentre en varias tablas. Por ejemplo, supongamos que queremos mostrar los nombres de las asignaturas del alumno con DNI 25400000. select asignaturas.nombre from estudiantes, asignaturas where (estudiantes.DNI=2540000) and (estudiantes.modulo=asignaturas.modulo) Análisis Léxico (1ª sesión) Se trata de implementar un analizador léxico, en el que exista una función que devuelva el siguiente token dentro del programa fuente, es decir, que devuelva el tipo de token en forma de constante entera, y su lexema, en forma de cadena. En esta primera fase de la práctica, y con la finalidad de comprobar que funciona correctamente, esta función será llamada por la función main, que solicitará nuevos tokens hasta que se agote el texto del fichero de que contiene las consultas en SQL. El programa deberá imprimir una lista de tokens de la forma (tipo_de_token, lexema) como resultado, deteniéndose en el caso de un error léxico e indicando la línea y columna del texto fuente donde éste se produjo. Los componentes léxicos a reconocer son los siguientes: TKN_NUM = digito+ (. digito+)? TKN_ID = letra (letra | digito)* siendo digito = 0 | 1 | ... | 9 Símbolos especiales TKN_APAR ( TKN_CPAR ) letra = a | b | ... | z | A | B | ... | Z TKN_PTOCOMA ; TKN_PTO . Palabras reservadas: SELECT TKN_SELECT FROM TKN_FROM WHERE TKN_WHERE GROUP TKN_GROUP BY TKN_BY Funciones sobre campos agregados: AVG SUM MAX MIN TKN_AVG //calcula el promedio TKN_SUM //calcula la suma TKN_MAX //calcula el máximo TKN_MIN //calcula el mínimo Operadores: TKN_MENOR TKN_MAYOR TKN_MENORIG TKN_MAYORIG TKN_IG TKN_DISTINTO TKN_Y TKN_O < > <= >= = != and or NOTA: La distinción entre identificadores y palabras reservadas debe hacerse mediante el método de añadir las palabras reservadas a una lista de tokens predefinidos y comprobar cada candidato si es un identificador o palabra reservada. Análisis Sintáctico (2ª y 3ª sesión) Partiendo del analizador léxico ya programado, se trata de implementar un analizador sintáctico direccional determinista descendente basado en el uso de una gramática LL(1). El programa deberá imprimir como resultado la lista de producciones que generan la derivación más a la izquierda. En el caso de que haya errores sintácticos, el programa debe proporcionar la información sobre la línea y columna del texto original donde se produjo el error, e intentar efectuar una recuperación del error en modo de pánico. Diseño de una gramática LL(1) Construir una Gramática LL(1) que genere el lenguaje a reconocer: un subconjunto del lenguaje SQL. - Una sentencia SQL se compone de al menos de la clausula SELECT y FROM. La clausula WHERE y GROUP BY son opcionales. Si aparecen todas el orden es: SELECT, FROM, WHERE y GROUP BY. - En la clausula SELECT pueden aparecer varios identificadores separados por comas, o funciones de agregados. - Los identificadores pueden venir indicados mediante el atributo o bien mediante nombre_de_la_tabla.atributo. - En la clausula FROM pueden aparecer varios nombres de tablas separados por comas. - - En la clausula WHERE pueden aparecer combinaciones de expresiones relacionales y operadores lógicos. Recordad que los operadores lógicos tienen mayor prioridad que los relacionales y que puede existir anidamiento en las expresiones. Supondremos que el programador puede escribir más de una sentencia SQL consecutiva. Una sentencia viene separada de otra por el carácter ; Implementación del analizador sintáctico descendente Implementar el analizador sintáctico predictivo recursivo correspondiente para la gramática que habéis diseñado. Implementación de un sencillo traductor dirigido por la sintaxis Imaginemos que queremos enseñar SQL a una persona que no sabe inglés y que queremos traducir todos los ejemplos de nuestro manual de referencia al castellano (o valenciano). Es decir, donde aparece SELECT, debe aparecer la palabra SELECCIONAR. Igual para el resto de palabras reservadas. Añade las acciones semánticas a tu código para que durante el proceso de análisis sintáctico se realice la traducción y vuelque a un fichero de salida las sentencias traducidas. También habría que traducir los operadores AND por Y, OR por O. El primer ejemplo traducido a castellano sería: seleccionar estudiantes.nombre, estudiantes.apellidos desde estudiantes donde (estudiantes.edad>20); Implementación de un mecanismo de recuperación de errores Implementa un sencillo mecanismo de recuperaciones de errores sintácticos por el método de recuperación en modo de pánico. Duración: 3 sesiones Fecha de entrega máxima: para todos los grupos, Día 7 de Enero de 2004 Entregar: ficheros con la implementación y la gramática al profesor correspondiente.