Automatas y Lenguajes Forales Murcia

159
Isabel Navarrete S ´ anchez Mar ´ ıa Antonia C ´ ardenas Viedma Daniel S ´ anchez Alvarez Juan Antonio Bot ´ ıa Blaya Roque Mar ´ ın Morales Rodrigo Mart ´ ınez B ´ ejar Departamento de Ingenier ´ ıa de la Informaci ´ on y las Comunicaciones Universidad de Murcia TEOR ´ IA DE AUT ´ OMATAS Y LENGUAJES FORMALES

Transcript of Automatas y Lenguajes Forales Murcia

  • Isabel Navarrete Sanchez

    Mara Antonia Cardenas Viedma

    Daniel Sanchez Alvarez

    Juan Antonio Bota Blaya

    Roque Marn Morales

    Rodrigo Martnez Bejar

    Departamento de Ingeniera de la Informacion

    y las Comunicaciones

    Universidad de Murcia

    TEORIA DE AUTOMATAS

    Y

    LENGUAJES FORMALES

  • Introduccion

    Aunque no debemos hacer una distincion tajante entre los aspectos practicos y teoricos de laInformatica, es cierto que existen materias que tienen un alto contenido formal, con desarrollosde tipo matematico, al contrario que otros temas mas cercanos a la resolucion de problemas detipo practico. La asignatura de Teora de Automatas y Lenguajes Formales sin duda trata conlas materias del primer tipo y los contenidos que se imparten constituyen el eje fundamental dediversas areas de conocimiento encuadradas dentro de lo que podramos denominar InformaticaTeorica. A veces estas disciplinas resultan para el alumno materias aridas y distanciadas delo que ellos entienden que deberan estudiar en una carrera de Ingeniera Informatica. Pero laInformatica, como cualquier otra ciencia o ingeniera, tiene unos fundamentos teoricos sobrelos que apoyarse y que cualquier ingeniero en Informatica debe conocer. As lo entienden di-versos organismos internacionales como ACM e IEEE que recomiendan al menos un curso deAutomatas y Lenguajes Formales en los curricula de las carreras relacionadas con la Informati-ca. Una motivacion para el estudio de estas materias formales la expuso Millner en un discursoque dio en 1993 al recoger el prestigioso premio Turing que se otorga a distinguidos cientcosque trabajan en el area de las Ciencias de la Computacion:

    Estas [las aplicaciones] son altamente necesarias, pero no queremos que esto ocurraen detrimento del trabajo teorico...Las Ciencias de la Computacion son tan ampliasque si no tienen una teora basica, estaremos perdidos. Tantas cosas estan avanzan-do...Como podra ocurrir esto sin una teora? Esta tiene que ir cogida de la manode la practica.

    1. Evolucion historica de la Teora de la Computacion

    La Teora de la Computacion trata con modelos de calculo abstractos que describen con distintosgrados de precision las diferentes partes y tipos de computadores. Pero estos modelos no seusan para describir detalles practicos del hardware de un determinado ordenador, sino que masbien se ocupan de cuestiones abstractas sobre la capacidad de los ordenadores, en general. As,en los curricula de Ciencias de la Computacion existen cursos separados para tratar materiascomo Arquitectura de Computadores, Teora de Circuitos, Algoritmos y Estructuras de Datos,Sistemas Operativos, etc. Todas estas areas tienen una componente teorica, pero dieren delestudio de la Teora de la Computacion fundamentalmente en dos aspectos:

    Las primeras tratan con computadores que existen realmente, mientras que los modelosabstractos de calculo abarcan todo tipo de computadores que existen, que puedan llegara existir o simplemente que uno pueda imaginar.

    En Teora de la Computacion, a diferencia de las otras materias, lo importante no esbuscar la mejor manera de hacer las cosas (optimalidad) sino estudiar que puede o nopuede hacerse con un ordenador (computabilidad).

    2

  • La historia de la Teora de la Computacion es bastante interesante. Se ha desarrollado graciasa conuencia, por afortunadas coincidencias, de distintos campos de conocimiento y descubri-mientos (fundamentalmente matematicos) realizados a principios del siglo XX. Bajo el nombreTeora de la Computacion se recogen una serie de materias que constituyen hoy en da los fun-damentos teoricos de la Informatica: Teora de Automatas, Teora de los Lenguajes Formales,Computabilidad y Complejidad Algortmica.

    Computabilidad

    El primer tema que cae claramente dentro del campo de la Teora de la Computacion es elde Computabilidad. Iniciada por Godel, Church, Post, Turing y Kleene, tiene sus races en laLogica Matematica. Al iniciar el siglo XX, los matematicos estaban a punto de efectuar grandesdescubrimientos. Los logros de los siguientes 40 anos estaban destinados a sacudir las bases delas matematicas y tuvieron consecuencias que se extendieron al campo de las Ciencias de laComputacion, aun por nacer.

    A principios de siglo XX se empezo a fraguar un dilema. Georg Cantor (1845-1918), haba inven-tado por entonces la Teora de Conjuntos, pero al mismo tiempo descubrio algunas paradojasinquietantes. Algunos de sus planteamientos podan ser comprensibles (como que hay innitosde distinto tamano), pero otros no (por ejemplo, que algun conjunto sea mayor que el conjuntouniversal). Esto dejo una nube de duda a los matematicos que ellos necesitaban disipar. El pun-to de partida de fueron las cuestiones fundamentales que David Hilbert (1845-1918) formulo en1928, durante el transcurso de un congreso internacional:

    1. Son completas las Matematicas, en el sentido de que pueda probarse o no cada aseveracionmatematica?

    2. Son las Matematicas consistentes, en el sentido de que no pueda probarse simultanea-mente una aseveracion y su negacion?

    3. Son las Matematicas decidibles, en el sentido de que exista un metodo denido que sepueda aplicar a cualquier aseveracion matematica y que determine si dicha aseveracion escierta o falsa?

    La meta de Hilbert era crear un sistema axiomatico logico-matematico completo y consistente,del cual podran deducirse todas las Matematicas, esto es, cualquier teorema matematico po-dra derivarse de los axiomas aplicando una serie nita de reglas, es decir, mediante un procesoalgortmico o computacional. Su idea era encontrar un algoritmo que determinara la verdad ofalsedad de cualquier teorema en el sistema formal. A este problema le llamo el Entscheidungs-problem.

    Por desgracia para Hilbert, en la decada de 1930 se produjeron una serie de investigaciones quemostraron que esto no era posible. Las primeras noticias en contra surgen en 1931 con KurtGodel (1906-1978) y su Teorema de Incompletitud: Todo sistema de primer orden consistenteque contenga los teoremas de la aritmetica y cuyo conjunto de axiomas sea recursivo no escompleto. Como consecuencia no sera posible encontrar el sistema formal deseado por Hilberten el marco de la logica de primer orden. Una version posterior y mas general del teorema deGodel elimina la posibilidad de considerar sistemas deductivos mas potentes que los sistemas deprimer orden, demostrando que no pueden ser consistentes y completos a la vez. Los resultadosde Godel prueban que no solo no existe un algoritmo que pueda demostrar todos los teoremasen matematicas, sino que ademas, no todos los resultados son demostrables. Entonces cabeplantearse las siguientes preguntas:

    Que pueden hacer los ordenadores (sin restricciones de ningun tipo)?

    Cuales son las limitaciones inherentes a los metodos automaticos de calculo?

    3

  • A estas cuestiones pretende responder la Teora de la Computabilidad. El primer paso en labusqueda de las respuestas a estas preguntas esta en el estudio de los modelos de computacion.Los Modelos Abstractos de Calculo tienen su origen en los anos 30, antes de que existieranlos ordenadores (el primer computador electronico de proposito general fue el ENIAC que sedesarrollo a partir del ano 1943), en el trabajo de los logicos Church, Godel, Kleene, Post, yTuring. Estos primeros trabajos han tenido una profunda inuencia no solo en el desarrolloteorico de las Ciencias de la Computacion, sino que muchos aspectos de la practicos de laInformatica fueron presagiados por ellos: incluyendo la existencia de ordenadores de propositogeneral, la posibilidad de interpretar programas, la dualidad entre software y hardware y larepresentacion de lenguajes por estructuras formales basados en reglas de produccion.

    Alonzo Church propuso la nocion de funcion -definible como funcion efectivamente calculable.La demostracion de teoremas se convierte en una transformacion de una cadena de smbolosen otra, segun un conjunto de reglas formales, que se conocen como lambda calculo. En 1936,Church hace un esquema de la demostracion de la equivalencia entre las funciones -deniblesy las funciones recursivas de Herbrand-Godel (esta equivalencia tambien haba sido probadapor Kleene ) y conjetura que estas iban a ser las unicas funciones calculables por medio de unalgoritmo a traves de la tesis que lleva su nombre (Tesis de Church) y utilizando la nocionde funcion -denible, dio ejemplos de problemas de decision irresolubles y demostro que elEntscheidungsproblem era uno de esos problemas.

    Por otra parte Kleene, pocos meses despues, demuestra de forma independiente la equivalenciaentre funciones -denibles y funciones recursivas de Herbrand-Godel, a traves del concepto defuncion recursiva y da ejemplos de problemas irresolubles.

    La tercera nocion de funcion calculable proviene del matematico ingles Alan Turing (1912-1954).Turing senalo que haba tenido exito en caracterizar de un modo matematicamente preciso, pormedio de sus maquinas, la clase de las funciones calculables mediante un algoritmo (funcionesTuring-computables), lo que se conoce hoy como Tesis de Turing (1936). Aunque no se puededar ninguna prueba formal de que unamaquina de Turing pueda tener esa propiedad, Turing dioun elevado numero de argumentos a su favor, en base a lo cual presento la tesis como un teoremademostrado. Ademas, utilizo su concepto de maquina para demostrar que existen problemas queno son calculables por un metodo denido y en particular, que el Entscheidungsproblem era unode esos problemas. Cuando Turing conocio los trabajos de Church y Kleene, demostro que losconceptos de funcion -denible y funcion calculable por medio de una maquina de Turingcoinciden. Naturalmente a la luz de esto la Tesis de Turing resulta ser equivalente a la deChurch.

    Posteriormente, se demostro la equivalencia entre lo que se poda calcular mediante una maquinade Turing y lo que se poda calcular mediante un sistema formal en general. A la vista deestos resultados, la Tesis de Church-Turing es aceptada como un axioma en la Teora de laComputacion y ha servido como punto de partida en la investigacion de los problemas que sepueden resolver mediante un algoritmo.

    Una de las cuestiones mas estudiadas en la Teora de la Computabilidad ha sido la posibilidadde construir programas que decidan si un determinado algoritmo posee o no una determinadapropiedad. Sera interesante responder de forma automatica a cuestiones como:

    Calculan los algoritmos A y B la misma funcion? (Problema de la equivalencia)

    Parara el algoritmo A para una de sus entradas? (Problema de la parada)

    Parara el algoritmo A para todas sus entradas? (Problema de la totalidad)

    Calcula el algoritmo A la funcion f? (Problema de la verificacion)

    Conforme se fueron obteniendo demostraciones individuales de la no computabilidad de cada unade estas cuestiones, fue creciendo la sensacion de que casi cualquier pregunta interesante acerca

    4

  • de algoritmos era no computable. El Teorema de Rice, conrma esta sensacion: Consideresecualquier propiedad que no sea trivial acerca de la funcion calculada por un algoritmo, entoncesla cuestion de si la funcion calculada por un algoritmo arbitrario verica dicha propiedad es nocomputable.

    Complejidad Algortmica

    Despues de que la Teora de la Computabilidad fuera desarrollada, era natural preguntarseacerca de la dicultad computacional de las funciones computables. Este es el objetivo de laparte de las Ciencias de la Computacion que se conoce como Complejidad Algortmica. Rabin fueuno de los primeros en plantear esta cuestion general explcitamente: Que quiere decir que unafuncion f sea mas difcil de computar que otra funcion g ? Rabin sugirio una axiomatica quefue la base para el desarrollo del estudio de medidas de complejidad abstracta de Blum y otros(1967).

    Una segunda aportacion que tuvo una inuencia relevante en el desarrollo posterior de estamateria fue el artculo de J. Hartmanis y R. Stearns en 1965, cuyo ttulo On the Complexity ofAlgorithms dio nombre a este cuerpo de conocimiento. En el se introduce la nocion fundamentalde medida de complejidad denida como el tiempo de computacion sobre una maquina de Turingmulticinta y se demuestran los teoremas de jerarqua.

    Un tercer hito en los comienzos del tema fue el trabajo de Cobham titulado, The Intrinsic Com-putational Difficulty of Functions (1964). Cobham enfatizo el termino intrnseco, es decir, elestaba interesado en una teora independiente de las maquinas. Esto nos conduce al un con-cepto importante desarrollado en 1965: la identicacion de la clase de problemas que se puedenresolver en tiempo acotado por un polinomio sobre la longitud de la entrada. La distincion entrealgoritmos de tiempo polinomial y algoritmos de tiempo exponencial fue hecha por primera vezen 1953 por Von Neumann. La notacion de P para la clase de los problemas resolubles en tiempopolinomial fue introducida posteriormente por Karp (1972).

    La teora de la NP-completitud es seguramente el desarrollo mas importante de la ComplejidadAlgortmica. La clase NP consta de todos los problemas decidibles en tiempo polinomial poruna maquina de Turing no determinista. Cook en 1971 introduce la nocion de problema NP-completo y demuestra que el problema de la satisfacibilidad booleana es NP-completo. La claseNP incluye una gran cantidad de problemas practicos que aparecen en la actividad empresariale industrial. Demostrar que un problema es NP-completo equivale a demostrar que no tiene unasolucion determinista en tiempo polinomial, salvo que todos los problemas de NP esten en P ,cuestion que aun no esta demostrada.

    Otro area que actualmente esta teniendo cada vez mas importancia es la Criptografa, rela-cionada con la seguridad de los sistemas informaticos y donde se ha aplicado especialmente lateora de la complejidad algortmica. Mediante la criptografa podemos conseguir el manejo deinformacion condencial en el ordenador de forma mas o menos segura.

    Maquinas Secuenciales y Automatas Finitos

    La Teora de Automatas, que engloba tambien al estudio de las Maquinas secuenciales, tienesu origen en el campo de la Ingeniera Electrica. El matematico norteameriacano Shanon (queluego se hara famoso por su Teora de la Informacion) vino a establecer las bases para laaplicacion de la Logica Matematica a los circuitos combinatorios y posteriormente Huffmanen 1954 los amplio a circuitos secuenciales y utiliza conceptos como estado de un automata ytabla de transicion. A lo largo de las decadas siguientes, las ideas de Shanon se desarrollaronconsiderablemente, dando lugar a la formalizacion de una Teora de las Maquinas Secuencialesy de los Automatas Finitos (1956). Otros trabajos importantes sobre maquinas secuenciales sondebidos a Mealy (1955) y Moore.

    5

  • Desde un frente totalmente distinto, el concepto de automata finito aparece en 1943 con elartculo de de McCulloch y Pitts titulado A Logical Calculus of the Ideas Immanet in NervousActivity, donde describen los calculos logicos inmersos en un dispositivo (neurona artificial) quehaban ideado para simular la actividad de una neurona biologica. A partir de entonces, sehan desarrollado asociaciones de neuronas para constituir redes. Podemos considerar una RNA(Red Neural Artificial) como una coleccion de procesadores elementales (neuronas), conectadasa otras neuronas o entradas externas, y con una salida que permite propagar las senales pormultiples caminos. Cada procesador pondera las entradas que recibe y estos pesos pueden sermodicados en aras de conseguir el objetivo previsto. Es lo que llamaremos funcion de apren-dizaje. Es decir, una RNA puede aprender de sus propios errores, por un proceso inductivoa partir de un conjunto de ejemplos de lo que queremos aprender, frente al proceso deduc-tivo, propio de los Sistemas Expertos. Las caractersticas que hacen interesantes a las RNAsson su capacidad para aprender (reproducir un sistema o funcion a partir de ejemplos), me-morizar (almacenar un conjunto de patrones o ejemplos), generalizar y abstraer (que permitarecuperaciones a partir de entradas defectuosas o incompletas). Las redes neuronales, dentrodel perl de Teora de la Computacion, aportan paradigmas interesantes como son el calculoparalelo, el aprendizaje inductivo y su capacidad para realizar calculos aproximados por mediode interpolacion.

    En el verano de 1951 Kleene fue invitado por la RAND Corporation para realizar un informesobre los trabajos de McCulloch-Pitts. En este informe Kleene demuestra la equivalencia entre loque el llama dos formas de denir una misma cosa: los conjuntos regulares, los cuales puedenser descritos a partir de sucesos bases y los operadores union, concatenacion y clausura, es decir,mediante expresiones regulares y los lenguajes reconocidos por un automata nito.

    Los automatas nitos son capaces de reconocer solamente un determinado tipo de lenguajes,llamados lenguajes regulares, que tambien se caracterizan mediante un tipo de gramaticas llama-das as mismo regulares. Una forma adicional de caracterizar este tipo de lenguajes es mediantelas citadas expresiones regulares, construidas mediante operadores sobre el alfabeto del lenguajey otras expresiones regulares, incluyendo el lenguaje vaco. Es facilmente comprobable que, paraun alfabeto concreto, no todos los lenguajes que se pueden construir son regulares. Ni siquieratodos los interesantes desde el punto de vista de la construccion de algoritmos para resolverproblemas. Hay entonces muchos problemas que no son calculables con estos lenguajes. Estopone de maniesto las limitaciones de los automatas nitos y las gramaticas regulares, y pro-picia el desarrollo de maquinas reconocedoras de otros tipos de lenguajes y de las gramaticascorrespondientes asociadas a los mismos, como veremos en el siguiente apartado.

    Desde su nacimiento, la Teora de Automatas ha encontrado aplicacion en campos muy diver-sos. Que tienen en comun? A primera vista no parece sencillo deducirlo. Sin embargo, podemosvislumbrar la solucion si nos damos cuenta de que en todos ellos se manejan conceptos como elcontrol, la accion, la memoria y ademas, los objetos controlados o recordados son smbolos,palabras o frases de algun tipo. Algunos de los campos donde ha encontrado aplicacion la Teorade Automatas son:

    Teora de la Comunicacion.

    Teora de Control.

    Logica de Circuitos Secuenciales.

    Reconocimiento de Patrones.

    Fisiologa del Sistema Nervioso.

    Estructura y Analisis de los Lenguajes de Programacion.

    Traduccion Automatica de Lenguajes.

    6

  • Teora Algebraica de Lenguajes.

    Cuando un automata se usa para modelar la construccion de hardware (ej. circuitos secuenciales)o software (ej. analizadores lexicos) es muy importante examinar el problema de encontrar elautomata mnimo equivalente a uno dado. Tanto Human como Moore se ocuparon de esteproblema y encontraron algoritmos practicos para minimizar un automata de estados nitos.Para un automata de n estados estos algoritmos requeran n2 pasos. Bastante mas tarde, en1971 Hopcroft encontro un metodo que lo haca en O(n log(n)) pasos. Existe un punto de vistaalgebraico sobre la minimizacion y caracterizacion de automatas nitos, debida a John Myhilly Anil Nerode. Kleene, en su intento de entender los trabajos de McCullock y Pitts, abstrajoel concepto de automata nito a partir de las redes de neuronas y el concepto de expresionregular a partir del calculo logico del modelo de McCullock y Pitts. De la misma forma, Myhilla partir de los conceptos de automatas nitos de Kleene obtuvo el de diagrama de transicion(deterministas) y a los eventos los redujo a la union de clases de equivalencia. Siguiendo estalnea de trabajo, se ha elaborado en las ultimas decadas una teora abstracta de automatas conuna fuerte base matematica que, segun dijo Arbib en 1969, constituye la matematica pura dela Informatica.

    Gramaticas y Lenguajes Formales

    El desarrollo de los ordenadores en la decada de los 40, con la introduccion de los programas enla memoria principal y posteriormente con los lenguajes de programacion de alto nivel, propicianla distincion entre lenguajes formales, con reglas sintacticas y semanticas rgidas, concretas ybien denidas, de los lenguajes naturales como el ingles, donde la sintaxis y la semantica nose pueden controlar facilmente. Los intentos de formalizar los lenguajes naturales llevan a laconstruccion de gramaticas como una forma de describir estos lenguajes, utilizando para elloreglas de produccion para construir las frases del lenguaje. Se puede entonces caracterizar unlenguaje mediante las reglas de una gramatica adecuada.

    Noam Chomsky propone en 1956 tres modelos para la descripcion de lenguajes, que son la basede su futura jerarqua de los tipos de lenguajes (1959), que ayudo tambien en el desarrollo delos lenguajes de programacion. Chomsky establecio una clasicacion de gramaticas de acuerdocon el formato de sus producciones y distinguio cuatro clases fundamentales de lenguajes yrelaciones de inclusion entre ellas.

    La Teora de los Lenguajes Formales resulto tener una relacion sorprendente con la Teora deAutomatas y la Computabilidad. Paralelamente a la jerarqua de lenguajes existe otra equiva-lente de maquinas abstractas, de tal forma que a cada una de las clases de lenguajes denidas enla jerarqua de Chomsky a partir de restricciones impuestas a las gramaticas, le corresponde untipo de maquina abstracta, que no es otra cosa que un metodo reconocedor para la descripcionde lenguajes. La relacion la podemos observar en la gura 1. Cada uno de estos tipos de maqui-nas es capaz de resolver problemas cada vez mas complejos, desde los automatas nitos (que sonlos mas simples) hasta las maquinas de Turing que determinan el lmite de los procesos com-putables. Se puede llegar as, de una forma casi natural, a considerar las maquinas de Turing,establecidas casi 20 anos antes, como maquinas reconocedoras de los lenguajes estructuradospor frases (tipo 0) e incluso a interpretar la Tesis de Turing en terminos de que un sistemacomputacional nunca podra efectuar un analisis sintactico de aquellos lenguajes que estan porencima de los lenguajes estructurados por frases en la jerarqua de Chomsky.

    2. Fundamentos Matematicos

    A continuacion haremos un repaso breve sobre varias ideas matematicas que seran utilizadas enlos proximos captulos. Estos conceptos incluyen conjuntos, relaciones, funciones y tecnicas de

    7

  • LENGUAJES

    TIPO 0

    LENGUAJES

    TIPO 1

    TIPO 2

    LENGUAJES

    TIPO 3

    LENGUAJES

    MAQUINAS

    DE TURING

    AUTOMATAS

    LINEALMENTE ACOTADOS

    AUTOMATAS

    AUTOMATAS

    FINITOS

    CON PILA

    NO ENUMERABLES

    LENGUAJES

    Figura 1: Relacion Lenguajes-Maquinas Abstractas

    demostracion matematicas.

    Conjuntos

    Un conjunto es una coleccion de objetos. Por ejemplo, la coleccion de las letras vocales forman unconjunto que podemos notar como V = {a, e, i, o, u}. Los objetos que forman parte del conjuntose llaman elementos. Por ejemplo, a es un elemento de V y se escribe a V ; por otra partepodemos decir que z / V . Dos conjuntos son iguales si y solo si tienen los mismos elementos. Nose tienen en cuenta las repeticiones de elementos ni tampoco el orden de estos. Hay un conjuntoque no tiene ningun elemento llamado conjunto vaco y lo notaremos por . Un conjunto sepuede especicar enumerando sus elementos entre llaves y separados por comas y esto es lo quese llama definicion por extension. Pero a veces esto no es posible hacerlo porque el conjunto esinnito y entonces se usa una definicion por comprension, es decir, haciendo referencia a otrosconjuntos (conjuntos referenciales) y a propiedades que los elementos puedan tener. De formageneral se denen:

    B = {x A | x cumple la propiedad P}

    Un conjunto A es un subconjunto de otro conjunto B, A B, si cada elemento de A es unelmento de B. Tambien podemos decir que A esta incluido en B. Cualquier conjunto es unsubconjunto de s mismo. Si A es un subconjunto de B pero A no es igual a B se dice que A esun subconjunto propio de B y se nota como A B. Es obvio que A para cualquier conjuntoA. Para probar que dos conjuntos A y B son iguales debemos probar que A B y B A: cadaelemento de A debe ser un elemento de B y viceversa.

    Dos conjuntos se pueden combinar para formar un tercero mediante una serie de operacionessobre conjuntos:

    union A B = {x | (x A) (x B)}

    interseccion A B = {x | (x A) (x B)}

    diferencia AB = {x | (x A) (x / B)}

    8

  • Algunas propiedades de las operaciones anteriores se pueden deducir facilmente a partir desus deniciones:

    1. Idempotencia: A A = A ; A A = A

    2. Conmutatividad: A B = B A ; A B = B A

    3. Asociatividad:(A B) C = A (B C)(A B) C = A (B C)

    4. Distributividad:A (B C) = (A B) (A C)A (B C) = (A B) (A C)

    5. Absorcion: A (A B) = A ; A (A B) = A

    6. Leyes de DeMorgan:A B = A BA B = A B

    Dos conjuntos son disjuntos si no tienen elementos en comun, o lo que es lo mismo, si suinterseccion es el conjunto vaco. Es posible formar intersecciones y uniones de mas de dosconjuntos.

    La coleccion de todos los subconjuntos de A es a su vez un conjunto llamado conjunto potenciade A y lo notamos como 2A . Al conjunto potencia de A tambien se le suele llamar conjunto delas partes de A y se nota como P(A).

    Ejemplo 0.1 Sea A = {c, d}. Entonces 2A = {, {c} , {d} , {c, d}}

    Una particion de un conjunto no vaco A es un subconjunto, , de 2A tal que:

    1. cada elemento de es no vacio;

    2. los elementos de son disjuntos;

    3.

    = A

    Ejemplo 0.2 {{a, b} , {c} , {d}} es una particion de {a, b, c, d} pero {{a, b, c} , {c, d}} no lo es.Los conjuntos de numeros pares e impares forman una particion de N.

    Relaciones y funciones

    De forma general podemos denir una relacion como un conjunto de elementos , que son enesencia combinaciones de objetos de un determinado tipo que estan relacionados de algunaforma. Llamamos par ordenado a una pareja de objetos escritos entre parentesis y separadospor comas. Por ejemplo, (a, b) es un par ordenado y a, b son los componentes del par ordenado.No es lo mismo (a, b) que {a, b} por varios motivos:

    el orden inuye: no es lo mismo (a, b) que (b, a), sin embargo {a, b} = {b, a}

    los dos componentes de un par ordenado no tienen porque ser distintos; por ejemplo, (2, 2)es un par valido.

    El producto cartesiano de dos conjuntos A y B, que notamos AB, es el conjunto de todoslos pares ordenados (a, b) donde a A y b B.

    Ejemplo 0.3 Dados los conjuntos {1, 3, 9} y {b, c, d} , el producto cartesiano es,

    {1, 3, 9} {b, c, d} = {(1, b), (1, c), (1, d), (3, b), (3, c), (3, d), (9, b), (9, c), (9, d)}

    9

  • Una relacion binaria entre dos conjuntos A y B es un subconjunto de AB.

    Ejemplo 0.4 {(9, b), (1, c), (3, d)} es una relacion binaria entre los conjuntos {1, 3, 9} y {b, c, d} .

    La relacion menor que entre los numeros naturales es una relacion binaria,

  • 1. Si (a, b) R entonces (a, b) R+.

    2. Si (a, b) R+ y (b, c) R, entonces (a, c) R+.

    3. Solo estan en R+ los pares introducidos por 1 y 2.

    La clausura reflexiva y transitiva de R, que notamos R, se dene:

    R = R+ {(a, a) | a A}

    Ejemplo 0.5 Sea R = {(1, 2) , (2, 2) , (2, 3)} una relacion sobre el conjunto {1, 2, 3}. Entoncestenemos que,

    R+ = {(1, 2) , (2, 2) , (2, 3) , (1, 3)}R = {(1, 1) , (1, 2) , (1, 3) , (2, 2) , (2, 3) , (3, 3)}

    Una funcion de un conjunto A en un conjunto B, que notamos como f : A B, es unarelacion binaria f AB con la siguiente propiedad: para cada elemento a A hay exactamenteun par ordenado en f cuya primera componente sea a. Llamamos a A el dominio de la funcionf y a B el codominio de f . Si a es un elemento cualquiera de A, f(a) sera un elemento b B talque (a, b) f y ademas por ser f una funcion este b sera unico. Al elemento f(a) lo llamaremosimagen de a bajo f. Si tenemos la funcion f anterior y A es un subconjunto de A, denimos:

    f[A]={f(a) | a A

    }que es la imagen de A bajo f. El rango de una funcion f es la imagen de su dominio. Por convenio,si el dominio de una funcion es un producto cartesiano, no hace falta que especiquemos lasparejas de parentesis de los elementos.

    Ejemplo 0.6 Si f : N N N esta definida de forma que la imagen de un par ordenado(m,n) es la suma de m y n, podemos escribir f(m,n) = m+n, en lugar de f ((m,n)) = m+ny ademas podemos decir que m,n son los argumentos de f y m + n el correspondiente valor oresultado de f .

    Monoides

    El par (M, ) es un semigrupo si M es un conjunto y es una operacion interna binaria asocia-tiva. Es decir, es una funcion de M M en M que verica lo siguiente:

    x, y, z M : x (y z) = (x y) z

    Un elemento e M es la identidad de un semigrupo (M, ) si se verica:

    x M : e x = x e = x

    Un monoide es un semigrupo con identidad. Sea el monoide (M, , e), x M y un numeronatural n. La n-esima potencia de x, representada por xn, se dene inductivamente de lasiguiente manera:

    1. x0 = e

    2. xn = x xn1, para n > 0

    Sean A y B subconjuntos del monoide (M, , e). La operacion induce de forma natural unaoperacion binaria sobre 2M , el conjunto de todos los subconjuntos de M . Esta operacion sedene por:

    A,B 2M : A B = {x y | (x A) (y B)}

    11

  • Definicion 0.1 Sea (M, , e) un monoide. Entonces(2M , , {e}

    ), donde es la operacion in-

    ducida, es tambien un monoide que llamaremos monoide inducido por (M, , e) sobre 2M .

    Definicion 0.2 Si A es un subconjunto del monoide (M, , e). Entonces:

    A es cerrado positivo sii x, y A : x y A.

    A es cerrado sii es cerrado positivo y ademas contiene a la identidad e.

    Definicion 0.3 Sea A un subconjunto cerrado de un monoide. Entonces (A, , e) donde es larestriccion de la operacion de M para los elementos de A es tambien un monoide. A tal monoidese le denomina submonoide de (M, , e).

    Definicion 0.4 Sea A cualquier subconjunto de un monoide (M, , e). El cierre positivo deA, representado por A+ , se define por:

    A+ =n=1

    An

    El cierre de A, que notaremos como A, se define como:

    A =n=0

    An

    donde An representa la n-esima potencia de A en el monoide inducido(2M , , {e}

    ).

    Un subconjunto B de un monoide M se dice que genera M sii B = M . Al conjunto B sele llama base (o generador) de M. Si B genera M entonces, por denicion, cualquier x M(distinto de e) se puede representar como x = x1 . . . xn, donde x1, . . . , xn B y n > 0. Sedice que B genera libremente a M si la representacion anterior es unica (salvo el orden, si laoperacion es conmutativa). M se dice que es un monoide libre si contiene un subconjuntoB que lo genera libremente.

    Conjuntos finitos e infinitos

    Una propiedad basica de los conjuntos nitos es su tamano o cardinalidad. Algunos aspectossobre el tamano de los conjuntos nitos son obvios, como por ejemplo, si A B entonces elcardinal de A es menor o igual que el cardinal de B; si A es subconjunto propio de B sera demenor tamano que B. Sin embargo esto no es tan simple cuando tenemos conjuntos innitos.Por ejemplo, hay mas numeros naturales que numeros pares? Aunque la intuicion nos dice ques, formalmente no podemos armarlo.

    Se dice que dos conjuntos A y B son equinumerables o equipotentes si podemos encontrar unafuncion f : A B donde f es biyectiva. Decimos que un conjunto es finito si es equinumerablecon {1, 2, . . . , n}, para algun n N, y diremos que el cardinal de A es n, esto es, |A| = n.

    Un conjunto A es infinito si puede establecerse una aplicacion biyectiva entre A y un subcon-junto propio de A. No todos los conjuntos innitos son equinumerables, por ejemplo N y R notienen la misma cardinalidad. Un conjunto se dice que es infinito numerable si es equinume-rable con N y se dice que es numerable si es nito o innito numerable. En caso contrario sedice que es no numerable, como por ejemplo el conjunto de los numeros reales R.

    Teorema 0.1 Si A es un conjunto cualquiera (incluso infinito) entonces |A| < |P (A)| .

    Ademas si A es infinito numerable entonces P(A) es no numerable.

    Teorema 0.2 La cardinalidad del conjunto de los numeros naturales es menor o igual que

    la cardinalidad de cualquier conjunto infinito.

    12

  • Principio de induccion

    El principio de induccion matematica arma lo siguiente,

    Si A es un subconjunto de numeros naturales, A N , y satisface las condiciones:

    1. 0 A

    2. si k A entonces k + 1 A

    entonces debe ser A = N.

    En la practica, el principio de induccion es usado para probar armaciones del tipo para todonumero natural k la propiedad P se cumple. Esto es lo mismo que probar que el conjunto

    A = {k N | P (k) se cumple}

    coincide con el conjunto de numeros naturales, esto es, debemos probar que A = N. Esto es loque se llama demostracion por induccion y el procedimiento a seguir es el siguiente:

    etapa base Probar que la propiedad P se cumple para 0.

    etapa de induccion Suponer que la propiedad se sumple para k (hipotesis de induccion)y probar que esto implica que se cumple para k + 1.

    conclusion Puesto que hemos probado en la etapa base que 0 A y en la etapa deinduccion que si k A entonces tambien k + 1 A, resulta que, por el principio deinduccion, podemos deducir que A = N, como queramos demostrar.

    A veces, interesa demostrar que cierta propiedad se cumple para todo k m. En este casodebemos demostrar que el conjunto,

    A = {n N | P (n+m) se cumple}

    coincide con el conjunto N. Para ello seguimos el siguiente razonamiento:

    etapa base (n = 0) Probar que P (m) se cumple.

    etapa de induccion (n > 0) Suponer que P (k) se cumple, siendo k m y probar queP (k + 1) se cumple.

    conclusion Por las etapas anteriores y el principio de induccion tenemos que A = N ypor tanto P se cumple para todo k m.

    El principio de induccion tambien se usa para denir conjuntos de objetos donde denimos elprimer objeto y el objeto k se dene en terminos del (k 1)-esimo objeto. Esto es lo que sellama definicion inductiva.

    Ejemplo 0.7 El factorial de un numero natural puede ser definido inductivamente como,

    1. 0! = 1

    2. k! = k (k 1)! para k > 0.

    13

  • 14

  • CAPITULO 1:LENGUAJES Y GRAMATICAS FORMALES

    Contenidos Teoricos

    1. Alfabetos y palabras

    1.1 Concatenacion de palabras

    1.2 Potencia de una palabra

    1.3 Inversion de palabras

    2. Lenguajes formales

    2.1 Operaciones del algebra de conjuntos

    2.2 Concatenacion, potencia e inversion de lenguajes

    2.3 Clausura de un lenguaje

    3. Gramaticas formales

    3.1 Deniciones basicas

    3.2 Notacion BNF

    3.3 Clasicacion de gramaticas

    3.4 Teorema de la jerarqua de Chomsky (enunciado)

    4. Nociones basicas sobre traductores

    1. Alfabetos y palabras

    Un alfabeto es un conjunto nito y no vaco de elementos llamados smbolos o letras. Una palabrao cadena sobre un alfabeto V es una cadena nita de smbolos del alfabeto. La longitud de unacadena w, que notaremos como |w|, es el numero de letras que aparecen en w. A la cadena queno tiene smbolos, o lo que es lo mismo, que tiene longitud 0, la llamaremos palabra vaca y senota por (o tambien , segun los autores).

    Si V es un alfabeto, llamaremos V n al conjunto de todas las palabras de longitud n sobre V .Un elemento de V n sera una cadena del tipo a1a2 . . . an donde cada ai V . Llamaremos V

    0 alconjunto cuyo unico elemento es la palabra vaca, es decir, V 0 = {} . El conjunto de todas lascadenas de cualquier longitud sobre V es:

    V =n=0

    V n

    Llamamos V + al conjunto de todas las cadenas sobre el alfabeto V excepto la vaca. Por tanto,V + = V {}.

    15

  • 1.1. Concatenacion de Palabras

    La operacion de concatenacion, que notaremos , es una operacion binaria entre palabras sobreun alfabeto V, esto es:

    : V V V

    de forma que si tenemos dos palabras x, y V donde x = a1a2 . . . an, y = b1b2 . . . bm entonces,x concatenado con y sera una palabra w V con |w| = |x|+ |y|, de forma que:

    w = x y = a1a2 . . . anb1b2 . . . bm

    Nota A veces se suele suprimir el y se puede escribir directamente w = xy

    Algunas propiedades de la concatenacion son:

    operacion cerrada 99K x, y V : x y V

    propiedad asociativa 99K x, y, z V : x (y z) = (x y) z

    elemento neutro 99K x V : x = x = x

    Por tener estas propiedades (V , , ) es un monoide. Ademas cada palabra de V se representade forma unica como concatenacion de smbolos de V , por eso es ademas un monoide libre.

    Todo monoide libre cumple la ley de cancelacion izquierda y derecha, en este caso, x, y, z V se cumple que:

    (x y = x z) (y = z) (y x = z x) (y = z)

    Decimos que una cadena z es subcadena de otra cadena w si existen cadenas x, y V tal quew = x z y. Vamos a ver dos conjuntos especiales de subcadenas:

    Prejo(w) = {x V | z V : w = x z} Sujo(w) = {x V | z V : w = z x}

    Diremos que x es un prefijo de w si x Prejo(w) y sera un prefijo propio si x 6= w. Por otraparte, diremos que x es un sufijo de w si x Sujo(w) y sera un sufijo propio si x 6= w.

    Ejemplo 1.1 Si w = abab es una palabra sobre el alfabeto {a, b}, o lo que es lo mismo, w {a, b} , tenemos que:

    ab es un prefijo propio de wabab es un prefijo de w, pero no es propiob es un sufijo de w

    1.2. Potencia de una palabra

    Llamamos potencia n-esima de una palabra, a la operacion que consiste en concatenar la palabraconsigo misma n veces. Dada una palabra w V , se dene inductivamente la potencia n-esimade w, que notaremos wn, como:

    1. w0 =

    2. wn = w wn1 para n > 0

    Ejemplo 1.2 Si w = aba es una palabra sobre el alfabeto {a, b} entonces:

    w0 = w1 = abaw2 = abaaba

    16

  • 1.3. Inversion de palabras

    Si w = a1a2 . . . an es una palabra sobre un alfabeto V entonces la palabra inversa o refleja dew se dene como: wR = anan1 . . . a1

    Ejemplo 1.3 Si w = aaba es una palabra sobre el alfabeto {a, b}, entonces wR = abaa.

    2. Lenguajes formales

    Llamamos lenguaje sobre el alfabeto V a cualquier subconjunto de V . As tenemos que,V , , y V pueden considerarse como lenguajes. Puesto que un lenguaje es tan solo una claseespecial de conjunto, podemos especicar un lenguaje finito por extension enumerando sus ele-mentos entre llaves. Por ejemplo, {aba, czr, d, f} es un lenguaje sobre el alfabeto {a, b, c, ..., z}.Sin embargo, la mayora de los lenguajes de interes son infinitos. En este caso podemos especi-car un lenguaje por comprension de la siguiente forma:

    L = {w V | w cumple la propiedad P}

    En la denicion anterior vemos que V es el conjunto referencial, que podemos llamar tambienlenguaje universal sobre V .

    Ejemplo 1.4 L = {w {0, 1} | ceros(w) = unos(w)}, palabras que tienen el mismo numerode ceros que de unos.

    2.1. Operaciones del algebra de conjuntos

    Sean L1 y L2 dos lenguajes denidos sobre el alfabeto V . Se dene la union de estos doslenguajes como el lenguaje L sobre V que se especica como:

    L = L1 L2 = {w V | (w L1) (w L2)}

    La union de lenguajes sobre el mismo alfabeto es un operacion cerrada y ademas cumple laspropiedades asociativa, conmutativa, y existe un elemento neutro que es el lenguaje vaco (no es lo mismo que el lenguaje que contiene la palabra vaca {}). El conjunto P(V )(esto es, el conjunto de las partes de V , tambien llamado 2V

    ), esta formado por todos loslenguajes posibles que se pueden denir sobre el alfabeto V . Entonces, por cumplir la union laspropiedades anteriores tenemos que (P(V ),,) es un monoide abeliano.

    De forma analoga a la union se pueden denir otras operaciones del algebra de conjuntos comola interseccion, diferencia, y complementacion de lenguajes. Por ejemplo, el complementario dellenguaje L sobre el alfabeto V sera: L = V L.

    2.2. Concatenacion, potencia e inversion de lenguajes

    Sean L1 y L2 dos lenguajes denidos sobre el alfabeto V , la concatenacion de estos dos lenguajeses otro lenguaje L denido como:

    L1 L2 = {x y V | (x L1) (y L2)}

    La denicion anterior solo es valida si L1 y L2 contienen al menos un elemento. Podemosextender la operacion de concatenacion al lenguaje vaco de la siguiente manera:

    L = L =

    17

  • La concatenacion de lenguajes sobre un alfabeto es una operacion cerrada, y ademas cumple lapropiedad asociativa y tiene un elemento neutro que es el lenguaje {}. Con lo cual, tenemosque (P (V ) , , {}) es el monoide inducido por el monoide (V , , ) sobre P (V ). Esto es, laoperacion de concatenacion de palabras induce la operacion de concatenacion de lenguajes yesta conserva las propiedades de la primera.

    Teorema 1.1 Dados los lenguajes A,B,C sobre un alfabeto V , la concatenacion de lengua-

    jes es distributiva con respecto a la union, esto es, se cumple que:

    1. A (B C) = (A B) (A C)

    2. (B C) A = (B A) (C A)

    Dem.- La demostracion se deja como ejercicio. En el primer caso se debe probar que:

    A (B C) (A B) (A C) y (A B) (A C) A (B C)

    para demostrar la igualdad y el segundo caso se demuestra de forma analoga.

    Una vez denida la concatenacion de lenguajes, podemos denir la potencia n-esima de unlenguaje como la operacion que consiste en concatenar el lenguaje consigo mismo n veces. Ladenicion inductiva es:

    1. L0 = {}

    2. Ln = L Ln1, n > 0

    Ejemplo 1.5 Si L = {ab, c} es un lenguaje sobre el alfabeto {a, b, c} entonces,

    L0 = {}L1 = L = {ab, c}L2 = L L1 = {abab, abc, cab, cc}L3 = L L2 = {ababab, ababc, abcab, abcc, cabab, cabc, ccab, ccc}

    Las deniciones de prejo y sujo de una palabra podemos extenderlas a lenguajes de la siguienteforma:

    Prejo(L) =wL

    Prejo(w) Sujo(L) =wL

    Sujo(w)

    Tambien podemos denir el lenguaje inverso o reejo de L como:

    LR ={wR | w L

    }

    2.3. Clausura de un lenguaje

    Dado un lenguaje L sobre un alfabeto V se dene la clausura positiva (o cierre positivo) de L,denotado L+, como:

    L+ =

    n=1

    Ln

    Denimos L como la clausura (o cierre) de L, como:

    L =n=0

    Ln

    18

  • En ambos casos, Ln se reere a la potencia n-esima del lenguaje L en el monoide inducido(P (V ) , , {}). El cierre o clausura de un lenguaje, por denicion, contiene cualquier palabraque se obtenga por concatenacion de palabras de L y ademas la palabra vaca.

    3. Gramaticas formales

    Hasta ahora hemos descrito los lenguajes formales como se describen los conjuntos: por extension(si son nitos) o por comprension. Aqu vamos a introducir otra forma general y rigurosa dedescribir un lenguaje formal: mediante el uso de gramaticas. Las gramaticas son mecanismosgeneradores de lenguajes, es decir, nos dicen como podemos obtener o construir palabras de undeterminado lenguaje.

    3.1. Definiciones basicas

    Definicion 1.1 Una gramatica es una cuadrupla G = (VN , VT , S, P ) donde:

    VT es el alfabeto de smbolos terminales

    VN es el alfabeto de smbolos no terminales o variables, de forma que debe ser VNVT = y denotamos con V al alfabeto total de la gramatica, esto es, V = VN VT .

    S es el smbolo inicial y se cumple que S VN

    P es un conjunto finito de reglas de produccion

    Definicion 1.2 Una regla de produccion es un par ordenado (, ) de forma que:

    (, ) (V VN V) V

    Es decir, = 1A2 donde 1, 2 (VN VT ) , A VN y (VN VT )

    . Una produccion(, ) P se suele escribir de forma infija como .

    Por convenio usaremos letras mayusculas para los smbolos no terminales; dgitos y las pri-meras letras minusculas del alfabeto para los smbolos terminales; las ultimas letras minusculasdel alfabeto para palabras que pertenezcan a V T y letras griegas para cualquier palabra quepertenezca a V . Usando este convenio, a veces se suele describir una gramatica enumerandounicamente sus reglas de produccion y cuando varias reglas tienen la misma parte izquierda, sesuelen agrupar separandolas con |.

    Ejemplo 1.6 Sea la gramatica G cuyas producciones son:

    S aSa | bSb | a | b |

    Esta gramatica tiene una sola variable S que ademas es el smbolo inicial. VT = {a, b} y Pcontiene 5 reglas de produccion.

    Definicion 1.3 Sea G una gramatica y sean las cadenas , V . Decimos que derivadirectamente en , que notamos como (derivacion directa), si y solo si existe unaproduccion P tal que = 12, = 12 con 1, 2 V

    .

    Esto quiere decir que deriva directamente en , si puede obtenerse a partir de sustituyendouna ocurrencia de la parte izquierda de una produccion que aparezca en por la parte derechade la regla de produccion.

    19

  • Nota Si es una regla de produccion de G, entonces se cumple siempreque . Cuando sea necesario distinguir entre varias gramaticas, escribiremosG , para referirnos a un derivacion directa en G.

    Por la denicion anterior se deduce que es una relacion binaria en el conjunto de cadenas dela gramatica, esto es: V V . Aqu usamos una notacion inja para indicar que en lugar de (, ) .

    Definicion 1.4 Decimos que deriva en , o bien que, es derivable de , y lo notamos como (derivacion) si y solo si se verifica una de las dos condiciones siguientes:

    1. = , (son la misma cadena), o bien,

    2. 0, 1, . . . , n V tal que 0 = , n = y 0 i < n se cumple que i i+1

    A la secuencia 0 2 . . . n la llamaremos secuencia de derivaciones directas de longitudn, o simplemente derivacion de longitud n.

    Nota Por la denicion anterior esta claro que

    es tambien una relacion binaria

    en V y ademas es la clausura reflexiva y transitiva de la relacion de derivaciondirecta . Esto quiere decir que

    es la menor relacion que cumple lo siguiente:

    Si entonces . Esto es, si dos cadenas estan relacionadas mediante

    entonces tambien lo estan mediante la relacion

    es reexiva, ya que V se cumple que

    es transitiva. En efecto, si

    y

    , entonces

    Definicion 1.5 Sea una gramatica G = (VN , VT , S, P ). Una palabra (VN VT ) se de-

    nomina forma sentencial de la gramatica, si y solo si se cumple que: S . Una forma

    sentencial w tal que w V T se dice que es una sentencia.

    Ejemplo 1.7 Sea la gramatica S aSa | bSb | a | b | , podemos afirmar lo siguiente:

    aaSb aabSbb, aunque ni aaSb ni aabSbb son formas sentenciales de G

    aabb aabb, aunque aabb no es una sentencia de G

    S, aSa, abSba, son formas sentenciales de G y ademas es una sentencia

    aabaa es una sentencia de G, ya que existe una derivacion de longitud 3 por la queS

    aabaa. En efecto:

    S aSa aaSaa aabaa

    Definicion 1.6 Sea una gramatica G = (VN , VT , S, P ) . Se llama lenguaje generado por lagramatica G al lenguaje L(G) formado por todas las cadenas de smbolos terminales que sonderivables del smbolo inicial de la gramatica (sentencias):

    L(G) ={w V T | S

    w

    }

    Ejemplo 1.8 Sea L ={w {a, b} | w = wR

    }. Este lenguaje esta formado por todos los palndro-

    mos sobre el alfabeto {a, b}. Puede probarse que la gramatica S aSa | bSb | a | b | generael lenguaje L. En general no existe un metodo exacto para probar que una gramatica generaun determinado lenguaje. Para este caso tan sencillo podemos probarlo de manera informalhaciendo una serie de derivaciones hasta darnos cuenta de que S

    w si y solo si w = wR.

    Luego veremos una demostracion formal por induccion en la seccion de aplicaciones.

    20

  • Definicion 1.7 Dos gramaticas G y G son equivalentes si y solo si generan el mismo len-guaje, es decir, sii L(G) = L(G).

    3.2. Notacion BNF

    A veces se utiliza una notacion especial para describir gramaticas llamada notacion BNF(Backus-Naus-Form). En la notacion BNF los smbolos no terminales o variables son encerradosentre angulos y utilizaremos el smbolo ::= para las producciones, en lugar de. Por ejemplo, laproduccion S aSa se representa en BNF como S ::= a S a. Tenemos tambien la notacionBNF-extendida que incluye ademas los smbolos [ ] y { } para indicar elementos opcionales yrepeticiones, respectivamente.

    Ejemplo 1.9 Supongamos que tenemos un lenguaje de programacion cuyas dos primeras reglasde produccion para definir su sintaxis son:

    programa ::= [cabecera] begin sentencias endsentencias ::= sentencia {sentencia}

    Esto viene a decir que un programa se compone de una cabecera opcional, seguido de la palabraclave begin, a continuacion una lista de sentencias (debe haber al menos una sentencia) yfinaliza con la palabra clave end. Podemos transformar las producciones anteriores para espe-cificarlas, segun la notacion que nosotros hemos introducido (estandar), de la siguiente forma:

    P C beginA end | beginA endA BA | B

    donde P es el smbolo inicial de la gramatica y corresponde a la variable programa, C corres-ponde a cabecera, A se refiere a la variable sentencias y B a sentencia.

    La simbologa utilizada para describir las gramaticas en notacion estandar y en notacion BNFnos proporcionan un herramienta para describir los lenguajes y la estructura de las sentenciasdel lenguaje. Puede considerarse a esta simbologa como un metalenguaje, es decir un lenguajeque sirve para describir otros lenguajes.

    3.3. Jerarqua de Chomsky

    En 1959 Chomsky clasico las gramaticas en cuatro familias, que dieren unas de otras en laforma que pueden tener sus reglas de produccion. Si tenemos una gramatica G = (VN , VT , S, P )clasicaremos las gramaticas y los lenguajes generados por ellas, de la siguiente forma:

    Tipo 3 (Gramaticas regulares). Pueden ser, a su vez, de dos tipos:

    Lineales por la derecha. Todas sus producciones son de la forma:

    A bCA bA

    donde A,C VN y b VT .

    21

  • Lineales por la izquierda. Con producciones del tipo:

    A CbA bA

    Los lenguajes generados por estas gramaticas se llaman lenguajes regulares y elconjunto de todos estos lenguajes es la clase L3.

    Tipo 2 (Gramaticas libres del contexto). Las producciones son de la forma:

    A

    donde A VN y (VN VT ) . Los lenguajes generados por este tipo de gramaticas se

    llaman lenguajes libres del contexto y la clase es L2.

    Tipo 1 (Gramaticas sensibles al contexto). Las producciones son de la forma:

    A

    donde , V y V + Se permite ademas la produccion S siempre y cuando Sno aparezca en la parte derecha de ninguna regla de produccion.

    El sentido de estas reglas de produccion es el de especicar que una variable A puede serreemplazada por en una derivacion directa solo cuando A aparezca en el contextode y , de ah el nombre sensibles al contexto. Ademas, las producciones de esaforma cumplen siempre que la parte izquierda tiene longitud menor o igual que la partederecha, pero nunca mayor (excepto para S ). Esto quiere decir que la gramatica esno contractil.

    Los lenguajes generados por las gramaticas de tipo 1 se llaman lenguajes sensibles alcontexto y su clase es L1.

    Tipo 0 (Gramaticas con estructura de frase) Son las gramaticas mas generales, que porello tambien se llaman gramaticas sin restricciones. Esto quiere decir que las produccionespueden ser de cualquier tipo permitido, es decir, de la forma con (V VN V

    )y V .

    Los lenguajes generados por estas gramaticas son los lenguajes con estructura defrase, que se agrupan en la clase L0. Estos lenguajes tambien se conocen en el campo dela Teora de la Computabilidad como lenguajes recursivamente enumerables.

    Teorema 1.2 (Jerarqua de Chomsky) Dado un alfabeto V , el conjunto de los lenguajes

    regulares sobre V esta incluido propiamente en el conjunto de los lenguajes libres de contextoy este a su vez esta incluido propiamente en el conjunto de los lenguajes sensibles al contexto,que finalmente esta incluido propiamente en el conjunto de lenguajes con estructura de frase.Esto es:

    L3 L2 L1 L0

    La demostracion de este teorema la iremos viendo a lo largo del curso.

    Nota En este tema hemos hecho referencia al termino lenguaje formal para di-ferenciarlo de lenguaje natural. En general, un lenguaje natural es aquel que haevolucionado con el paso del tiempo para nes de la comunicacion humana, por

    22

  • ejemplo el espanol o el ingles. Estos lenguajes evolucionan sin tener en cuenta re-glas gramaticales formales. Las reglas surgen despues con objeto de explicar, masque determinar la estructura de un lenguaje, y la sintaxis es difcil de determinarcon precision. Los lenguajes formales, por el contrario, estan denidos por reglas deproduccion preestablecidas y se ajustan con todo rigor o formalidad a ellas. Comoejemplo tenemos los lenguajes de programacion y los lenguajes logicos y matemati-cos. No es de extranar, por tanto, que se puedan construir compiladores ecientespara los lenguajes de programacion y que por contra la construccion de traductorespara lenguaje natural sea una tarea compleja e ineciente, en general. Veremos quelas gramaticas regulares y libres de contexto, junto con sus maquinas abstractasasociadas tienen especial interes en la construccion de traductores para lenguajes deprogramacion.

    4. Nociones basicas sobre traductores

    Hace apenas unas cuantas decadas, se utilizaban los llamados lenguajes de primera generacionpara hacer que los computadores resolvieran problemas. Estos lenguajes operan a nivel de codigobinario de la maquina, que consiste en una secuencia de ceros y unos con los que se instruyeal ordenador para que realice acciones. La programacion, por tanto, era difcil y problematica,aunque pronto se dio un pequeno paso con el uso de codigo octal o hexadecimal.

    El codigo de maquina fue reemplazado por los lenguajes de segunda generacion, o lenguajesensambladores. Estos lenguajes permiten usar abreviaturas nemonicas como nombres simbolicos,y la abstraccion cambia del nivel de ip-op al nivel de registro. Se observan ya los primeros pasoshacia la estructuracion de programas, aunque no puede utilizarse el termino de programacionestructurada al hablar de programas en ensamblador. Las desventajas principales del uso de loslenguajes ensambladores son, por un lado, la dependencia de la maquina y, por otro, que sonpoco legibles.

    Para sustituir los lenguajes ensambladores, se crearon los lenguajes de tercera generacion olenguajes de alto nivel. Con ellos se pueden usar estructuras de control basadas en objetosde datos logicos: variables de un tipo especco. Ofrecen un nivel de abstraccion que permitela especicacion de los datos, funciones o procesos y su control en forma independiente de lamaquina. El diseno de programas para resolver problemas complejos es mucho mas sencilloutilizando este tipo de lenguajes, ya que se requieren menos conocimientos sobre la estructurainterna del computador, aunque es obvio que el ordenador unicamente entiende codigo maquina.

    Por lo tanto, para que un computador pueda ejecutar programas en lenguajes de alto nivel,estos deben ser traducidos a codigo maquina. A este proceso se le denomina compilacion, yla herramienta correspondiente se llama compilador. Nosotros vamos a entender el terminocompilador como un programa que lee otro, escrito en lenguaje fuente, y lo traduce a lenguajeobjeto, informando, durante el proceso de traduccion, de la presencia de errores en el programafuente. Esto se reeja en la gura 1.1.

    En la decada de 1950, se considero a los compiladores como programas notablemente difciles deescribir. El primer compilador de FORTRAN, por ejemplo, necesito para su implementacion, 18anos de trabajo en grupo. Desde entonces, se han descubierto tecnicas sistematicas para manejarmuchas de las importantes tareas que surgen en la compilacion. Tambien se han desarrolladobuenos lenguajes de implementacion, entornos de programacion y herramientas de software.Con estos avances, puede construirse un compilador real incluso como proyecto de estudio enuna asignatura sobre diseno de compiladores.

    23

  • COMPILADOR

    Programa

    ObjetoProgramaFuente

    Mensajes de error

    Figura 1.1: Denicion de un compilador

    4.1. Traductores y compiladores

    Un traductor es un programa que acepta cualquier texto expresado en un lenguaje (el lenguajefuente del traductor) y genera un texto semanticamente equivalente expresado en otro lenguaje(su lenguaje destino).

    Un ensamblador traduce un lenguaje ensamblador en su correspondiente codigo maquina. Ge-neralmente, un ensamblador genera una instruccion de la maquina por cada instruccion fuente.

    Un compilador traduce desde un lenguaje de alto nivel a otro lenguaje de bajo nivel. General-mente, un compilador genera varias instrucciones de la maquina por cada comando fuente.

    Los ensambladores y compiladores son las clases mas importantes de traductores de lenguajes deprogramacion, pero no son las unicas clases. A veces se utilizan los traductores de alto nivel cuyafuente y destino son lenguajes de alto nivel. Un desensamblador traduce un codigo maquina ensu correspondiente lenguaje ensamblador. Un descompilador traduce un lenguaje de bajo nivelen un lenguaje de alto nivel.

    Nosotros estamos interesados en la traduccion de textos que son programas. Antes de realizarcualquier traduccion, un compilador comprueba que el texto fuente sea un programa correctodel lenguaje fuente. (En caso contrario genera un informe con los errores). Estas comprobacionestienen en cuenta la sintaxis y las restricciones contextuales del lenguaje fuente. Suponiendo queel programa fuente es correcto, el compilador genera un programa objeto que es semanticamenteequivalente al programa fuente, es decir, que tiene los efectos deseados cuando se ejecuta. Lageneracion del programa objeto tiene en cuenta tanto la semantica del lenguaje fuente como lasemantica del lenguaje destino.

    Los traductores, y otros procesadores de lenguajes, son programas que manipulan programas.Varios lenguajes se ven implicados: no solo el lenguaje fuente y el lenguaje destino, sino tam-bien el lenguaje en el cual el traductor se ha escrito. Este ultimo es el llamado lenguaje deimplementacion.

    4.2. Interpretes

    Un compilador nos permite preparar un programa para que sea ejecutado en una maquina,traduciendo el programa a codigo maquina. El programa entonces se ejecuta a la velocidad dela maquina. Este metodo de trabajo no esta libre de inconvenientes: todo el programa debeser traducido antes que pueda ejecutarse y producir resultados. En un entorno interactivo, lainterpretacion es un metodo de trabajo mas atractivo.

    Un interprete es un programa que acepta otro programa (el programa fuente) escrito en undeterminado lenguaje (el lenguaje fuente), y ejecuta el programa inmediatamente. Un interpretetrabaja cargando, analizando y ejecutando una a una las instrucciones del programa fuente. Elprograma fuente comienza a ejecutarse y produce resultados desde el momento en que la primerainstruccion ha sido analizada. El interprete no traduce el programa fuente en un codigo objeto.

    24

  • La interpretacion es un buen metodo cuando se dan las siguientes circunstancias:

    El programador esta trabajando en forma interactiva, y quiere ver el resultado de cadainstruccion antes de entrar la siguiente instruccion.

    El programa se va a utilizar solo una vez, y por tanto la velocidad de ejecucion no esimportante.

    Se espera que cada instruccion se ejecute una sola vez.

    Las instrucciones tiene un formato simple, y por tanto pueden ser analizadas de formafacil y eciente.

    La interpretacion es muy lenta. La interpretacion de un programa fuente, escrito en un lenguajede alto nivel, puede ser 100 veces mas lenta que la ejecucion del programa equivalente escritoen codigo maquina. Por tanto la interpretacion no es interesante cuando:

    El programa se va a ejecutar en modo de produccion, y por tanto la velocidad es impor-tante.

    Se espera que las instrucciones se ejecuten frecuentemente.

    Las instrucciones tienen formatos complicados, y por tanto su analisis es costoso en tiempo.

    Algunos interpretes mas o menos conocidos son:

    (a) Un interprete Caml: Caml es un lenguaje funcional. El interprete lee cada cada lnea hastael smbolo ;; y la ejecuta produciendo una salida, por lo que el usuario ve el resultado dela misma antes de entrar la siguiente. Existen versiones tanto para Windows como paradistintas versiones de Linux. Existen tambien varios compiladores para distintos sistemasoperativos.

    (b) Un interprete Lisp: Lisp es un lenguaje en el que existe una estructura de datos (arbol)tanto para el codigo como para los datos.

    (c) El interprete de comandos de Unix (shell): Una instruccion para el sistema operativo delusuario de Unix se introduce dando el comando de forma textual. El programa shell leecada comando, lo analiza y extrae un nombre de comando junto con algunos argumentos yejecuta el comando por medio de un sistema de llamadas. El usuario puede ver el resultadode un comando antes de entrar el siguiente. Los comandos constituyen un lenguaje decomandos, y el shell es un interprete para tal lenguaje.

    (d) Un interprete SQL: SQL es un lenguaje de preguntas (query language) a una base de datos.El usuario extrae informacion de la base de datos introduciendo una pregunta SQL, quees analizada y ejecutada inmediatamente. Esto es realizado por el interprete SQL que seencuentra dentro del sistema de administracion de la base de datos.

    4.3. Compiladores interpretados

    Un compilador puede tardar mucho en traducir un programa fuente a codigo maquina, pero unavez hecho esto, el programa puede correr a la velocidad de la maquina. Un interprete permiteque el programa comience a ejecutarse inmediatamente, pero corre muy lento (unas 100 vecesmas lento que el programa en codigo maquina).

    Un compilador interpretado es una combinacion de compilador e interprete, reuniendo algunasde las ventajas de cada uno de ellos. La idea principal es traducir el programa fuente en unlenguaje intermedio, disenado para cumplir los siguiente requisitos:

    25

  • tiene un nivel intermedio entre el lenguaje fuente y el codigo maquina

    sus instrucciones tienen formato simple, y por tanto pueden ser analizadas facil y rapida-mente.

    la traduccion desde el lenguaje fuente al lenguaje intermedio es facil y rapida.

    Por tanto un compilador interpretado combina la rapidez de la compilacion con una velocidadtolerable en la ejecucion.

    El codigo de la Maquina Virtual de Java (el JVM-code) es un lenguaje intermedio orientado aJava. Nos provee de potentes instrucciones que corresponden directamente a las operaciones deJava tales como la creacion de objetos, llamadas de metodos e indexacion de matrices. Por ellola traduccion desde Java a JVM-code es facil y rapida. Ademas de ser potente, las instruccionesdel JVM-code tienen un formato tan sencillo como las instrucciones del codigo maquina concampos de operacion y campos de operandos, y por tanto son faciles de analizar. Por ello lainterpretacion del JVM-code es relativamente rapida: alrededor de solo diez veces mas lentaque el codigo maquina. JDK consiste en un traductor de Java a JVM-code y un interprete deJVM-code, los cuales se ejecutan sobre alguna maquina M.

    4.4. Contexto de un compilador

    En el proceso de construccion de un programa escrito en codigo maquina a partir del programafuente, suelen intervenir, aparte del compilador, otros programas:

    Preprocesador: Es un traductor cuyo lenguaje fuente es una forma extendida de algunlenguaje de alto nivel, y cuyo lenguaje objeto es la forma estandar del mismo lenguaje.Realiza la tarea de reunir el programa fuente, que a menudo se divide en modulos alma-cenados en archivos diferentes. Tambien puede expandir abreviaturas, llamadas macros,a proposiciones del lenguaje fuente. El programa objeto producido por un preprocesadorpuede , entonces, ser traducido y ejecutado por el procesador usual del lenguaje estandar.

    Ensamblador: Traduce el programa en lenguaje ensamblador, creado por el compilador, acodigo maquina.

    Cargador y linkador: Un cargador es un traductor cuyo lenguaje objeto es el codigo de lamaquina real y cuyo lenguaje fuente es casi identico. Este consiste usualmente en progra-mas de lenguaje maquina en forma reubicable, junto con tablas de datos que especicanlos puntos en donde el codigo reubicable debe modicarse para convertirse en verdade-ramente ejecutable. Por otro lado, un linkador es un traductor con los mismos lenguajesfuente y objeto que el cargador. Toma como entrada programas en forma reubicable quese han compilado separadamente, incluyendo subprogramas almacenados en libreras. Losune en una sola unidad de codigo maquina lista para ejecutarse. En general, un editor decarga y enlace une el codigo maquina a rutinas de librera para producir el codigo querealmente se ejecuta en la maquina.

    En la gura 1.2 aparece resumido el contexto en el que un compilador puede trabajar, aunquees necesario tener en cuenta que no han de cumplirse estos pasos estrictamente. En cualquiercaso, dependera del lenguaje que se este traduciendo y el entorno en el que se trabaje.

    4.5. Fases y estructura de un compilador

    Podemos distinguir, en el proceso de compilacion, dos tareas bien diferenciadas:

    26

  • COMPILADOR

    en lenguaje ensambladorPrograma objeto

    EDITOR DE CARGA Y ENLACEde archivosobjeto relocalizables

    Biblioteca

    Estrctura del programa fuente

    Programa fuente

    Codigo maquina relocalizable

    Codigo Maquina absoluto

    ENSAMBLADOR

    PREPROCESADOR

    Figura 1.2: Contexto de un compilador

    Analisis: Se determina la estructura y el signicado de un codigo fuente. Esta parte delproceso de compilacion divide al programa fuente en sus elementos componentes y creauna representacion intermedia de el, llamada arbol sintactico.

    Sntesis: Se traduce el codigo fuente a un codigo de maquina equivalente, a partir de esarepresentacion intermedia. Aqu, es necesario usar tecnicas mas especializadas que duranteel analisis.

    Conceptualmente, un compilador opera en estas dos etapas, que a su vez pueden dividirse envarias fases. Estas pueden verse en la gura 1.3, donde se muestra la descomposicion tpica deun compilador. En la practica, sin embargo, se pueden agrupar algunas de estas fases, y lasrepresentaciones intermedias entre ellas pueden no ser construidas explcitamente.

    27

  • analizadorlexico

    analizadorsintactico

    analizadorsemantico

    generador codigointermedio

    optimizador decodigo

    generadorde codigo

    manejo detabla de simbolos

    manejo deerrores

    programa fuente

    programa objeto

    Figura 1.3: Fases de un compilador

    Las tres primeras fases de la gura 1.3 conforman la mayor parte de la tarea de analisis en uncompilador, mientras que las tres ultimas pueden considerarse como constituyentes de la partede sntesis del mismo.

    Durante el analisis lexico, la cadena de caracteres que constituye el programa fuente, se lee deizquierda a derecha, y se agrupa en componentes lexicos, que son secuencias de caracteres conun signicado colectivo.

    En el analisis sintactico, los componentes lexicos se agrupan jerarquicamente en coleccionesanidadas con un signicado comun.

    En la fase de analisis semantico se realizan ciertas revisiones para asegurar que los componentesde un programa se ajustan de un modo signicativo.

    Las tres ultimas fases suelen variar de un compilador a otro. Existen, por ejemplo, compiladoresque no generan codigo intermedio, o no lo optimizan y pasan directamente del analisis semanticoa la generacion de codigo.

    De manera informal, tambien se consideran fases al administrador de la tabla de smbolos y almanejador de errores, que estan en interaccion con todas las demas:

    Administracion de la tabla de smbolos: Una funcion esencial de un compilador es registrarlos identicadores utilizados en el programa fuente y reunir informacion sobre los distintosatributos de cada identicador. Estos atributos pueden proporcionar informacion sobre lamemoria asignada a un identicador, su tipo, su ambito (la parte del programa donde

    28

  • tiene validez), y, en el caso de los procedimientos, cosas como el numero y tipo de susargumentos, el metodo por que que cada argumento es pasado (valor, referencia,...) y eltipo que devuelve, si lo hay.

    Una tabla de smbolos es una estructura de datos que contiene un registro por cada iden-ticador, con campos para los atributos del identicador. La estructura de datos debepermitir encontrar rapidamente datos de ese registro.

    Cuando el analizador lexico detecta un identicador en el programa fuente, este identi-cador se introduce en la tabla de smbolos. Sin embargo, normalmente los atributos de unidenticador no se pueden determinar durante el analisis lexico. Por ejemplo, cuando elanalizador lexico reconoce los componentes lexicos de la declaracion de PASCAL

    var x, y, z : real;

    no relaciona unos componentes con otros, y, por tanto, no puede establecer el signicadode la frase (x, y y z son variables reales).

    Las fases restantes introducen informacion sobre los identicadores en la tabla de smbo-los, y despues la utilizan de varias formas. Por ejemplo, cuando se esta haciendo el analisissemantico y la generacion de codigo intermedio, se necesita conocer los tipos de los identi-cadores, para poder comprobar si el programa fuente los usa de una forma valida, y, as,poder generar las operaciones apropiadas con ellos. El generador de codigo, por lo general,introduce y utiliza informacion detallada sobre la memoria asignada a los identicadores.

    Deteccion e informacion de errores: Cada fase dentro de proceso de compilacion, puedeencontrar errores. Sin embargo, despues de detectar un error, cada fase debe tratar dealguna forma ese error, para poder continuar la compilacion, permitiendo la deteccion denuevos errores en el programa fuente. Un compilador que se detiene cuando encuentra elprimer error, no resulta tan util como debiera. Las fases de analisis sintactico y semantico,por lo general, manejan una gran porcion de errores detectables por el compilador.

    La fase de analisis lexico puede detectar errores donde los caracteres restantes de la entradano forman ningun componente lexico del lenguaje.

    Los errores donde la cadena de componentes lexicos viola las reglas de la estructura dellenguaje (sintaxis) son determinados por la fase de analisis sintactico.

    Durante la fase de analisis semantico, el compilador intenta detectar construcciones quetengan la estructura sintactica correcta, pero que no tengan signicado para la operacionimplicada. Por ejemplo, se cometera un error semantico si se intentaran sumar dos iden-ticadores, uno de los cuales fuera el nombre de una matriz, y el otro el nombre de unprocedimiento.

    4.5.1. Analisis lexico (o lineal)

    Es la primera fase de la que consta un compilador. La parte del compilador que realiza el analisislexico se llama analizador lexico (AL), scanner o explorador. La tarea basica que realiza el ALes transformar un ujo de caracteres de entrada en una serie de componentes lexicos o tokens.Se encargara, por tanto, de reconocer identicadores, palabras clave, constantes, operadores,etc.

    La secuencia de caracteres que forma el token se denomina lexema. No hay que confundir elconcepto de token con el de lexema. A un mismo token le pueden corresponder varios lexemas.Por ejemplo, se pueden reconocer como tokens de tipo ID a todos los identicadores. Aunquepara analizar sintacticamente una expresion, solo nos hara falta el codigo de token, el lexema

    29

  • debe ser recordado, para usarlo en fases posteriores dentro del proceso de compilacion. El ALes el unico componente del compilador que tendra acceso al codigo fuente. Por tanto, debede encargarse de almacenar los lexemas para que puedan ser usados posteriormente. Esto sehace en la tabla de smbolos. Por otro lado, debe enviar al analizador sintactico, aparte delcodigo de token reconocido, la informacion del lugar donde se encuentra almacenado ese lexema(por ejemplo, mediante un apuntador a la posicion que ocupa dentro de la tabla de smbolos).Posteriormente, en otras fases del compilador, se ira completando la informacion sobre cadaitem de la tabla de smbolos.

    Por ejemplo, ante la sentencia de entrada

    coste = precio * 098

    el AL podra devolver una secuencia de parejas, como la siguiente:

    [ID,1] [=,] [ID,2] [*,] [CONS,3]

    donde ID, =, * y CONS corresponderan a codigos de tokens y los numeros a la derecha de cadapareja sera ndices de la tabla de smbolos.

    Si durante la fase de analisis lexico, el AL se encuentra con uno o mas lexemas que no corres-ponden a ningun token valido, debe dar un mensaje de error lexico e intentar recuperarse.

    Finalmente, puesto que el AL es el unico componente del compilador que tiene contacto conel codigo fuente, debe encargarse de eliminar los smbolos no signicativos del programa, comoespacios en blanco, tabuladores, comentarios, etc.

    Es conveniente siempre separar esta fase de la siguiente (analisis sintactico), por razones deeciencia. Ademas, esto permite el uso de representaciones diferentes del programa fuente, sintener que modicar el compilador completo.

    4.5.2. Analisis sintactico (o jerarquico)

    Esta es la segunda fase de la que consta un compilador. La parte del compilador que realiza elanalisis sintactico se llama analizador sintactico o parser. Su funcion es revisar si los tokens delcodigo fuente que le proporciona el analizador lexico aparecen en el orden correcto (impuestopor la gramatica), y los combina para formar unidades gramaticales , dandonos como salida elarbol de derivacion o arbol sintactico correspondiente a ese codigo fuente.

    De la forma de construir este arbol sintactico se desprenden los dos tipos de analizadores sintacti-cos existentes:

    Cuando se parte del axioma de la gramatica y se va descendiendo, utilizando derivacio-nes mas a la izquierda, hasta conseguir la cadena de entrada, se dice que el analisis esdescendente

    Por el contrario, cuando se parte de la cadena de entrada y se va generando el arbol haciaarriba mediante reducciones mas a la izquierda (derivaciones mas a la derecha), hastaconseguir la raz o axioma, se dice que el analisis es ascendente.

    Si el programa no tiene una estructura sintactica correcta, el analizador sintactico no podra en-contrar el arbol de derivacion correspondiente y debera dar mensaje de error sintactico.

    La division entre analisis lexico y sintactico es algo arbitraria. Generalmente se elige una divi-sion que simplique la tarea completa del analisis. Un factor para determinar como realizarlaes comprobar si una construccion del lenguaje fuente es inherentemente recursiva o no. Lasconstrucciones lexicas no requieren recursion, mientras que las sintacticas suelen requerirla.

    30

  • Las gramaticas libres de contexto (GLC) formalizan la mayora de las reglas recursivas que pue-den usarse para guiar el analisis sintactico. Es importante destacar, sin embargo, que la mayorparte de los lenguajes de programacion pertenecen realmente al grupo de lenguajes dependientesdel contexto.

    4.5.3. Analisis semantico

    Para que la denicion de un lenguaje de programacion sea completa, aparte de las especica-ciones de su sintaxis (estructura o forma en que se escribe un programa), necesitamos tambienespecicar su semantica (signicado o denicion de lo que realmente hace un programa).

    La sintaxis de un lenguaje de programacion se suele dividir en componentes libres de contex-to y sensibles al contexto. La sintaxis libre de contexto dene secuencias legales de smbolos,independientemente de cualquier nocion sobre el contexto o circunstancia particular en queaparecen dichos smbolos. Por ejemplo, una sintaxis libre de contexto puede informarnos de queA := B + C es una sentencia legal, mientras que A := B no lo es.

    Sin embargo, no todos los aspectos de un lenguaje de programacion pueden ser descritos me-diante este tipo de sintaxis. Este es el caso, por ejemplo, de las reglas de alcance para variables,de la compatibilidad de tipos, etc. Estos son componentes sensibles al contexto de la sintaxisque dene al lenguaje de programacion. Por ejemplo, A := B + C podra no ser legal si lasvariables no estan declaradas, o son de tipos incompatibles.

    Puesto que en la mayora de los casos, como ya apuntamos en la seccion anterior, se utilizanpor simplicidad GLC para especicar la sintaxis de los lenguajes de programacion, tenemos quehacer un tratamiento especial con las restricciones sensibles al contexto. Estas pasaran a formarparte de la semantica del lenguaje de programacion.

    La fase de analisis semantico revisa el programa fuente para tratar de encontrar errores semanti-cos, y reune la informacion sobre los tipos para la fase posterior de generacion de codigo. Paraesto se utiliza la estructura jerarquica que se construye en la fase de analisis sintactico, para,por ejemplo, identicar operadores y operandos de expresiones y proposiciones. Ademas, accede,completa y actualiza con frecuencia la tabla de smbolos.

    Una tarea importante a realizar en esta fase es la verificacion de tipos. Aqu, el compiladorcomprueba si cada operador tiene operandos permitidos por la especicacion del lenguaje fuente.Muy frecuentemente, esta especicacion puede permitir ciertas conversiones de tipos en losoperandos, por ejemplo, cuando un operador aritmetico binario se aplica a un numero entero ya otro real. En este caso, el compilador puede requerir la conversion del numero entero a real,por ejemplo.

    Resumiendo, algunas de las comprobaciones que puede realizar, son:

    Chequeo y conversion de tipos.

    Comprobacion de que el tipo y numero de parametros en la declaracion de funcionescoincide con los de las llamadas a esa funcion.

    Comprobacion del rango para ndices de arrays.

    Comprobacion de la declaracion de variables.

    Comprobacion de las reglas de alcance de variables.

    4.5.4. Generacion de codigo

    La generacion de codigo constituye la ultima fase dentro del proceso de compilacion. Despues deexaminar el codigo fuente y comprobar que es correcto desde el punto de vista lexico, sintactico

    31

  • y semantico, se debe llevar a cabo la traduccion del programa fuente al programa objeto. Esteconsiste, normalmente, en un programa equivalente escrito en un lenguaje maquina o ensam-blador. Por equivalente queremos decir que tiene el mismo signicado, es decir, que produce losmismos resultados que nuestro programa fuente original.

    El arbol de derivacion obtenido como resultado del analisis sintactico, junto con la informacioncontenida en la tabla de smbolos, se usa para la construccion del codigo objeto. Existen variosmetodos para conseguir esto. Uno de ellos, que es particularmente efectivo y elegante, es elque se conoce como traduccion dirigida por la sintaxis. Esta consiste basicamente en asociar acada nodo del arbol de derivacion una cadena de codigo objeto. El codigo correspondiente a unnodo se construye a partir del codigo de sus descendientes y del codigo que representa accionespropias de ese nodo. Por tanto, se puede decir que este metodo es ascendente, pues parte delas hojas del arbol de derivacion y va generando codigo hacia arriba, hasta que llegamos a laraz del arbol. Esta representa el smbolo inicial de la gramatica y su codigo asociado sera elprograma objeto deseado.

    A veces, el proceso de generacion de codigo se puede dividir en las siguientes fases:

    Generacion de codigo intermedio

    Algunos compiladores generan una representacion intermedia explcita del programa fuen-te tras la etapa de analisis. Esta representacion intermedia se puede considerar como unprograma para una maquina abstracta, y debe cumplir dos propiedades:

    Debe ser facil de producir.

    Debe ser facil de traducir a codigo objeto.

    En general, las representaciones intermedias deben hacer algo mas que calcular expresio-nes; tambien deben manejar construcciones de ujo de control y llamadas a procedimien-tos. El codigo generado a partir del intermedio suele ser, por lo general, menos ecienteque el codigo maquina generado directamente, debido al nivel de traduccion adicional.

    Optimizacion del codigo

    La fase de optimizacion de codigo trata de mejorar el codigo intermedio, de modo que -nalmente se obtenga un codigo maquina mas eciente en tiempo de ejecucion. Hay muchavariacion en la cantidad de optimizacion de codigo que ejecutan los distintos compiladores.En los que realizan muchas operaciones de optimizacion, denominados compiladores opti-mizadores, una parte signicativa del tiempo del compilador se ocupa en esta tarea. Sinembargo, hay optimizaciones sencillas que mejoran sensiblemente el tiempo de ejecuciondel programa objeto, sin necesidad de retardar demasiado la compilacion.

    A veces, a causa del tiempo requerido en esta fase, hay compiladores que no la llevan acabo y pasan directamente a la generacion de codigo objeto. De hecho, en muchos casos,tambien se suele suprimir la fase de generacion de codigo intermedio, aunque esta tieneotras utilidades. Suele ser usual que el compilador ofrezca al usuario la posibilidad dedesactivar la opcion de optimizacion del generador de codigo durante la fase de desarrolloo depuracion de programas.

    La generacion de codigo optimo es un problema NP-completo, y, por tanto, incluso loscompiladores optimizadores no tienen por que producir codigo optimo. Es decir, no debe-mos malinterpretar el termino optimizacion, pues al tratarse de un problema NP-completo,solo supone, en general, la obtencion de codigo mejorado, pero esto no signica que sea elmejor codigo posible.

    Generacion de codigo objeto

    32

  • La fase nal del compilador es la generacion de codigo objeto, que, por lo general, consisteen codigo de maquina reubicable o codigo ensamblador. Para cada una de las variablesusadas por el programa se seleccionan posiciones de memoria. Despues, cada una de lasinstrucciones intermedias se traduce a una secuencia de instrucciones maquina que ejecu-taran la misma tarea. Una aspecto muy importante a tener en cuenta es la asignacion devariables a registros.

    Si durante el proceso de compilacion se ha generado codigo intermedio, y se ha pasado porla fase de optimizacion, solo quedara general el codigo objeto correspondiente al codigointermedio optimizado. En otro caso, podra generarse directamente codigo objeto despuesdel analisis semantico. Incluso puede realizarse al mismo tiempo que el analisis sintacticoy semantico (compiladores de una pasada).

    En cualquier caso, existen varias posibilidades en cuanto al formato que puede tener elcodigo objeto:

    Generar directamente codigo maquina, que estara, por tanto, listo para ejecutarseen la maquina correspondiente. En este caso, debe resolverse, entre otras cuestiones,la de reservar memoria para los identicadores que aparezcan en el programa. Estohace necesario construir un mapa de direcciones que asocie a cada identicador sucorrespondiente direccion en memoria.

    Generar codigo en lenguaje ensamblador de la maquina destino. Posteriormente,habra que traducirlo, mediante un ensamblador, a codigo objeto reubicable. Este,haciendo uso del cargador-linkador, se transformara en codigo ejecutable. Esta formade generar codigo es mas sencilla, y permite poder compilar por separado distintosprogramas que pueden interactuar entre s, usando libreras de rutinas, etc. De hecho,esta tecnica es muy comun en compiladores que trabajan bajo entorno UNIX, aunqueen otros casos se evita, por hacer mas ineciente el proceso de compilacion.

    En cualquier caso, la generacion de codigo es una tarea complicada, que requiere profundosconocimientos del hardware de la maquina destino, con objeto de aprovechar al maximolos recursos de la misma para que el programa ejecutable resulte lo mas eciente posible.

    4.5.5. Un ejemplo sencillo

    En la gura 1.4 se esquematiza un ejemplo de traduccion de la proposicion:

    posicion := inicial + velocidad * 60

    siguiendo cada una de las fases del proceso de compilacion, desde el analisis lexico hasta lageneracion de codigo en lenguaje ensamblador. Se supone que las constantes no se almacenanen la tabla de smbolos. Por otro lado, se realiza una conversion de tipos (la constante entera60 se convierte a real), dentro del analisis semantico. Asimismo, se genera codigo intermedio detres direcciones, que es optimizado antes de generar el codigo en lenguaje ensamblador.

    33

  • :=

    id1

    id2

    id3 60

    +

    *

    +

    *

    :=

    id1

    id2

    id3 inttoreal

    60

    temp1 := inttoreal(60)temp2 := id3 * temp1temp3 := id2 + temp2

    optimizador de codigo

    id1 := id2 + temp1

    generador de codigo

    MOVF id3, R2MULF #60,0, R2MOVF id2, R1ADDF R2, R1MOVF R1, id1

    analizador semantico

    analizador sintactico

    id1 := Id2 + id3 * 60

    analizador de lexico

    posicion := inicial + velocidad * 60

    generador codigo intermedio

    temp1 := id3 * 60.0

    id1 := temp3

    Tabla de Simbolos

    posicion

    inicial

    velocidad

    . .

    . .

    . .

    Figura 1.4: Traduccion de una sentencia

    EJERCICIOS RESUELTOS

    1. Sea L = {, a}. Obtener Ln para n = 0, 1, 2, 3. Cuantos elementos tiene Ln, en general?Describir por comprension L+.

    L0 = {} L1 = L L0 = {, a} {} = {, a}L2 = L L1 = {, a} {, a} = {, a, aa} L3 = L L2 = {, a} {, a, aa} = {, a, aa, aaa}

    34

  • Para todo n 0 se tiene que |Ln| = n + 1. Podemos denir la clausura positiva de Lcomo:

    L+ =n=1

    Ln = {am | m 0}

    2. Sean los lenguajes A = {a} y B = {b}. Describir (AB) y (AB)+.

    (AB) = {(ab)n | n 0} = {, ab, abab, ababab, . . .}(AB)+ = {(ab)n | n > 0} = {ab, abab, ababab, . . .}

    3. Demostrar que la concatenacion de lenguajes no es distributiva respecto de la interseccion.

    No se cumple que para tres lenguajes cualesquiera A (BC) = (A B)(A C). Lo vamosa demostrar con un contraejemplo. Sean los lenguajes A = {a, }, B = {}, C = {a}.Tenemos que:

    A (B C) = {a, } ({} {a}) = (A B) (A C) = {a, } {aa, a} = {a}

    Como vemos, se obtienen resultados diferentes. Luego la concatenacion no es distributivarespecto de la interseccion.

    4. Dadas dos cadenas x e y sobre V , demostrar que |xy| = |x|+ |y| (*).

    Primero denimos por induccion la longitud de una cadena, de forma que:

    1) || = 0, |a| = 1, a V2) |wa| = |w|+ 1

    Ahora demostramos (*) por induccion. Para el caso base cuando y tienen longitud ceroo uno, se cumple (*) por la denicion inductiva. Por hipotesis de induccion suponemosque (*) se cumple para toda palabra x de cualquier longitud y para toda palabra y delongitud 0 |y| n. Ahora consideramos una palabra cualquiera y de longitud n + 1.Entonces y tendra al menos un smbolo, de forma que y = wa y por la denicion inductivatenemos que |y| = |w| + 1. Tambien por denicion se tiene que |xy| = |xwa| = |xw| + 1.Pero |xw| = |x| + |w| puesto que se cumple la hipotesis de induccion para w por tenerlongitud n. En denitiva tenemos que:

    |xy| = |xwa| = |xw|+ 1 = |x|+ |w|+ 1 = |x|+ |y|, c.q.d.

    5. Sea el alfabeto V = {0, 1} y los lenguajes:

    L1 = {w {0, 1} | ceros(w) es par}

    L2 = {w {0, 1} | w = 01n, n 0}

    Demostrar que la concatenacion L1L2 es el lenguaje: L = {w {0, 1} | ceros(w) es impar}

    Tenemos que demostrar que L1 L2 L y que L L1 L2

    L1 L2 L Se cumple ya que la concatenacion de una palabra de L1 con otra de L2

    nos da una palabra con un numero impar de 0s. En efecto, una palabra de L1 tiene unnumero par de ceros y una palabra de L2 solo tiene un cero al principio y va seguida de

    35

  • cualquier numero de unos. Por tanto al concatenar las dos palabras, la palabra resultantetendra un numero impar de ceros.

    L L1 L2 Se cumple que cada palabra w con un numero impar de 0s puede obtenerse

    como concatenacion de una palabra de L1 seguida de una palabra de L2. Haremos unademostracion por casos revisando todas las posibles formas de la palabra: que termineen 0 o que termine en 1.

    a) Supongamos que w = x0. Entonces x debe tener un numero par de ceros, por tanto:

    w = xL1

    0L2

    b) Supongamos que w = x1. Nos jamos en el ultimo cero de x (tiene que haberlo a lafuerza) y partimos la cadena x de forma x = z1 z2 donde z1 llega hasta el ultimoce