-
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