Upload
vohanh
View
331
Download
10
Embed Size (px)
Citation preview
Johan Jeuring
Dept. of Computer Science
Utrecht University
S. Doaitse SwierstraDept. of Computer Science
Utrecht [email protected]
Traducci�on: Lenny de Lafuente
Michelle Butr�on
Revisi�on T�ecnica: Pablo Azero
Victor Hugo Monta~no
Colaborador: Robert Hoepfner
Primera Impresi�on, Marzo 2000
Copyright c 2000, Utrecht University
Impresi�on: Programa MEMI, Universidad Mayor de San Sim�on
�Indice General
1 Objetivos 7
1.1 Historia 8
1.2 An�alisis gramatical de gram�aticas libres de contexto 9
1.3 Composicionalidad 10
1.4 Mecanismos de abstracci�on 10
2 Gram�aticas Libres de Contexto 11
2.1 Lenguajes 12
2.2 Gram�aticas 15
2.2.1 Convenciones de notaci�on 18
2.3 El lenguaje de una gram�atica 19
2.3.1 Algunos lenguajes b�asicos 21
2.4 �Arboles de An�alisis 23
2.4.1 De gram�aticas libres de contexto a tipos de datos 25
2.5 Transformaciones de gram�aticas 26
2.6 Sintaxis concreta y abstracta 31
2.7 Construcciones sobre Gram�aticas 34
2.7.1 SL: un ejemplo 35
2.8 An�alisis Sint�actico 37
2.9 Ejercicios 39
3 Combinadores para el an�alisis sint�actico 43
3.1 El tipo Parser 45
3.2 Analizadores sint�acticos Elementales 47
3.3 Combinadores de an�alisis sint�actico 50
3.3.1 Ejemplo: emparejamiento de par�entesis 55
3.4 M�as combinadores de an�alisis sint�actico 57
3.4.1 Combinadores de an�alisis sint�actico para EBNF 57
3.4.2 Separadores 60
3.5 Expresiones Aritm�eticas 62
3.6 Expresiones generalizadas 64
3.7 Exercises 65
1
2 �INDICE GENERAL
4 Gram�aticas y dise~no de analizadores sint�acticos 69
4.1 Paso 1: Una gram�atica para el lenguaje 70
4.2 Paso 2: Analizando la gram�atica 70
4.3 Paso 3: Transformando la gram�atica 71
4.4 Paso 4: Decidiendo sobre los tipos 71
4.5 Paso 5: Construyendo el analizador sint�actico b�asico 73
4.5.1 Analizadores sint�acticos b�asicos de cadenas 73
4.5.2 Analizadores sint�acticos b�asicos de s��mbolos 74
4.6 Paso 6: A~nadiendo funciones Sem�anticas 75
4.7 Paso 7: >Consigui�o lo que esperaba? 77
4.8 Ejercicios 77
5 Lenguajes regulares 79
5.1 Aut�omatas de estado �nito 80
5.1.1 Aut�omatas de estado �nito determin��sticos 80
5.1.2 Aut�omatas de estado �nito no determin��sticos 82
5.1.3 Implementaci�on 85
5.1.4 Construcci�on de un AFD a partir de un AFN 87
5.1.5 Evaluaci�on parcial de los AFNs 90
5.2 Gram�aticas Regulares 90
5.2.1 Equivalencia entre gram�aticas regulares y aut�omatas �nitos 94
5.3 Expresiones regulares 95
5.4 Pruebas 99
5.5 Ejercicios 102
6 Composicionalidad 107
6.1 Listas 108
6.1.1 Listas prede�nidas 108
6.1.2 Listas de�nidas por el usuario 110
6.1.3 Listas in�nitas 111
6.2 �Arboles 112
6.2.1 �Arboles binarios 112
6.2.2 �Arboles para emparejamiento de par�entesis 113
6.2.3 �Arboles de expresi�on 115
6.2.4 �Arboles generales 116
6.2.5 E�ciencia 117
6.3 Sem�anticas algebraicas 119
6.4 Expresiones 119
6.4.1 Evaluaci�on de expresiones 119
6.4.2 Adici�on de variables 120
�INDICE GENERAL 3
6.4.3 Adici�on de de�niciones 122
6.4.4 Compilaci�on a una m�aquina de pila 124
6.5 Lenguajes estructurados en bloques 126
6.5.1 Bloques 126
6.5.2 Generaci�on de c�odigo 127
6.6 Ejercicios 130
7 Calculando con los analizadores sint�acticos 133
7.1 Inserci�on de funci�ones sem�anticas en el analizador 133
7.2 Aplicando un fold a la sintaxis abstracta 133
7.3 Deforestaci�on 134
7.4 Usando una clase en lugar de sintaxis abstracta 135
7.5 Pasar un �algebra al analizador sint�actico 136
8 Gram�aticas de atributos 137
8.1 Programas Circulares 138
8.2 El problema rep min 138
8.2.1 Una soluci�on directa 139
8.2.2 Lambda lifting 139
8.2.3 El producto cartesiano de los c�alculos 140
8.2.4 Fusi�on de los productos cartesianos 141
8.3 Un peque~no compilador 142
8.3.1 El lenguaje 142
8.3.2 Una m�aquina de pila 143
8.3.3 Compilaci�on a la m�aquina de pila 144
8.4 Ejercicios 147
9 Pumping Lema: el poder de expresi�on de los lenguajes 149
9.1 El Pumping Lemma para lenguajes regulares 149
9.2 El Pumping Lemma para lenguajes libres de contexto 151
9.3 Demostraciones de los Pumping Lemma 154
9.4 Ejercicios 157
A El m�odulo Stack 161
B Respuestas a los ejercicios 163
Prefacio
Este texto est'a basado en textos de a~nos anteriores producidos como parte del
proyecto Kwaliteit en Studeerbaarheid.1
El texto se ha mejorado los dos 'ultimos a~nos, pero estamos abiertos a sugerencias
para a'un m'as mejoras, en especial respecto a su relaci'on con otras materias.
Muchas personas han contribuido al presente estado de este texto ya sea escribien-
do o comentando (parte de) el texto. Agradecimientos especiales merecen Jeroen
Fokker, Rik van Geldrop y Luc Duponcheel, que han ayudado a escribir un(varios)
cap'itulo(s) del texto. Entre los que han hecho comentarios est'an: Arthur Baars,
Gijsbert Bol, Martin Bravenboer, Graham Hutton, Daan Leijen, Erik Meijer, en
Vincent Oostindi�e.
Para terminar queremos dar algunos consejos de estudio:
� En nuestra experiencia la explicaci'on del material a otra persona aclara qu'e
partes todav'ia no est'an bien comprendidas (beheerst). Cuando est'as seguro
de que has entendido bien un cap'itulo, entonces prueba ponerlo en tus propias
palabras. Hemos tratado de estimular esta \autoformulaci'on" en los ejercicios
de tarea.
Om een en ander niet geheel vrijblijvend te maken komen op het tentamen
ook een aantal opgaven van deze vorm terug
Entonces esperamos que puedas explicarnos algo en un espacio de entre media
y una p'agina de ingl'es o espa~nol escrito correcto.
� Oefening baart kunst. Naarmate er meer aandacht wordt besteed aan de pre-
sentatie van de stof, en naarmate er meer voorbeelden gegeven worden, is het
verleidelijker om, na lezing van een hoofdstuk, de conclusie te trekken dat je
een en ander daadwerkelijk beheerst.
\Entender" no es lo mismo que \conocer", \conocer" es algo distinto de \be-
heersen" y \beheersen" a su vez es distinto de \poder hacer algo con". Haz
entonces los ejercicios que est'an en este texto por ti mismo si mirar las solu-
ciones que otros han encontrado.
Probeer voor jezelf bij te houden welk stadium je bereikt hebt met betrekking
tot alle genoemde leerdoelen. In het ideale geval zou je in staat moeten zijn
een mooi tentamen in elkaar te zetten voor je mede-studenten!
� Preocupate de estar actualizado. Contrariamente a lo que pasa en otras mate-
rias, en esta materia es f'acil perder la pista del material. No aparecen nuevas
1NT: Calidad y Estudiabilidad.
5
6 �INDICE GENERAL
oportunidades peri'odicamente. Hemos tratado de hacer algo en la redacci'on
de este texto, pero el contenido total no deja mucha libertad en este aspecto.
Si has perdido algunos dias en el seguimiento del material es muy posible que
no entiendas el material nuevo. El tiempo para recuperar que tienes en las
clases te'oricas y pr'acticas no es su�ciente, y la consecuencia es que a menudo
has perdido ya mucho tiempo (que ya no tienes) para estudiar todo el material
para el examen.
� Hacemos uso del lenguaje Hugs para presentar muchos conceptos y algoritmos.
Si tienes di�cultades con el lenguaje Hugs ?? tienes que pedir ayuda. Caso
contrario haces tu vida m'as complicada.
We maken gebruik van de taal Hugs om veel concepten en algoritmen te pre-
senteren. Als je nog moeilijkheden hebt met de taal Hugs aarzel dan niet direct
hier wat aan te doen, en zonodig hulp te vragen. Anders maak je jezelf het
leven heel moeilijk. Goed gereedschap is het halve werk, en Hugs is hier ons
gereedschap.
Veel sterkte, en hopelijk ook veel plezier,
Johan Jeuring y Doaitse Swierstra
Cap��tulo 1
Objetivos
introducci�on
Los cursos sobre Gram�aticas, An�alisis Sint�actico y Compilaci�on de lenguajes de
programaci�on han sido siempre algunos de los componentes centrales del plan de
estudios de ciencias de computaci�on. Esto se debe a que desde el principio de es-
tos planes de estudios, �esta ha sido una de las pocas �areas donde el desarrollo de
m�etodos formales y la aplicaci�on de t�ecnicas formales en la construcci�on de progra-
mas han venido juntos. Por largo tiempo, la construcci�on de compiladores ha sido
una de las pocas �areas en la que ten��amos metodolog��a disponible, donde pose��amos
herramientas para la generaci�on de partes de compiladores tomados de descripciones
formales de las tareas a ser realizadas, y donde los generadores de programas estaban
realmente generando programas que hubieran sido imposibles de haber sido creados
manualmente. Para muchos expertos de la computaci�on el curso sobre construcci�on
de compiladores todav��a es uno de los cursos sobresalientes de su educaci�on.
Sin embargo, una de las cosas que no estaba muy clara era d�onde exactamente se
originaba el inter�es: las t�ecnicas ense~nadas ten��an de�nitivamente una cierta ele-
gancia, pod��amos construir programas que otra persona no pod��a |d�andonos as�� la
sensaci�on que cont�abamos con \el material correcto"| y, al completar los ejercicios
pr�acticos, que invariablemente consist��an de la construcci�on de un compilador para
alg�un lenguaje \juguete", ten��amos esa usual sensaci�on de satisfacci�on. Esta sensa-
ci�on creci�o debido que unos meses antes no se hubiera tenido la m�as m��nima idea de
c�omo terminar este producto, y ahora s�� supimos \c�omo hacerlo".
Esta situaci�on se ha mantenido as�� durante a~nos, y solamente en los �ultimos a~nos
hemos comenzado a descubrir y hacer expl��cita la raz�on de por que esta �area atrajo
tanto inter�es. Muchas de las t�ecnicas que fueron ense~nadas basandose en \es as��
c�omo usted resuelve este tipo de problemas", han sido provistas con un apunte
te�orico el cu�al explica como funcionan estas t�ecnicas. Como un efecto colateral
bene�cioso tambi�en hemos aprendido gradualmente a ver donde jug�o un papel m�as
importante el concepto descubierto, enlazando as�� esta �area con muchas otras �areas
de las ciencias de la computaci�on; y no s�olo eso, dando tambi�en los medios para
explicar tales uniones, enfatizar su importancia, mostrar correspondencia y transferir
los nuevos conocimientos de un �area de inter�es a otras.
objetivos
Los objetivos de este texto pueden dividirse en: objetivos primarios, estos van aso-
7
8 Objetivos
ciados con el tema espec���co de estudio, y objetivos secundarios |pero no menos
importantes| asociados con el desarrollo de habilidades que se espera que cada
cient���co en computaci�on posea. Los objetivos primarios, de cierta forma m�as tra-
dicionales son:
� entender c�omo describir estructuras (es decir \f�ormulas") utilizando gram�aticas ;
� saber como hacer el an�alisis sint�actico, es decir como reconocer (construir)
tales estructuras en (de) una secuencia de s��mbolos;
� saber como analizar gram�aticas para ver si las propiedades espec���cas son
v�alidas o no;
� entender el concepto de composicionalidad ;
� ser capaz de aplicar estas t�ecnicas en la construcci�on de todos los tipos de
programas;
� familiarizarse con el concepto de computabilidad.
Los objetivos secundarios m�as desa�antes son:
� desarrollar la capacidad de abstracci�on;
� entender los conceptos de interpretaci�on abstracta y evaluaci�on parcial ;
� entender el concepto de lenguaje de dominio espec���co;
� mostrar c�omo las formalizaciones apropiadas pueden ser usadas como un punto
inicial para la construcci�on de herramientas �utiles ;
� mejorar las destrezas de programaci�on en general;
� mostrar una amplia variedad de t�ecnicas de programaci�on �utiles;
� mostrar como desarrollar programas en un estilo calculacional.
1.1 Historia
Cuando a �nes de los cincuenta el uso de computadoras lleg�o a ser m�as y m�as di-
fundido, y su �abilidad se increment�o lo su�ciente para justi�car su aplicaci�on a
una amplia gama de problemas, ya no era el hardware el que causaba la mayor��a
de los problemas. El desarrollo de programas cada m�as grandes por cada vez m�as
gente estimul�o el desarrollo del primer lenguaje de programaci�on m�as o menos inde-
pendiente de la m�aquina: FORTRAN (FORmula TRANslator); el cual fue seguido
poco despu�es por ALGOL-60 y COBOL.
Para los desarrolladores del lenguaje FORTRAN, del cual John Backus fue el prin-
cipal arquitecto, el problema de c�omo describir el lenguaje no fue un aspecto impor-
tante debido a que exist��an problemas mucho m�as importantes por resolver, tales
como lo que debe o no debe estar en el lenguaje, c�omo construir un compilador para
el lenguaje que pudiera caber en las peque~nas memorias disponibles en ese entonces
(kilobytes en lugar de megabytes), y c�omo generar c�odigo de m�aquina que no ser��a
ridiculizado por programadores que hasta entonces lo hab��an escrito a mano. Como
consecuencia, el lenguaje estaba de�nido mucho m�as impl��citamente por lo que era
aceptado por el compilador y lo que no era.
Inmediatamente despu�es del desarrollo del FORTRAN, un grupo de trabajo inter-
nacional empez�o a trabajar en el dise~no de un lenguaje de programaci�on de alto
nivel independiente de la m�aquina que se conoci�o con el nombre de ALGOL 60.
Como efecto importante de esta tarea, y probablemente causado por la necesidad de
intercambiar proposiciones escritas, no s�olo se produjo un lenguaje est�andar, sino
1.2 An�alisis gramatical de gram�aticas libres de contexto 9
tambi�en una notaci�on para describir lenguajes de programaci�on que fue propuesta
por Naur, y fue usada para describir el lenguaje en el famoso reporte de ALGOL 60.
Desde entonces esta notaci�on fue introducida y pronto se conoci�o como el forma-
lismo Backus Naur (BNF), y ha sido utilizada como la herramienta primaria para
describir la estructura b�asica de los lenguajes de programaci�on.
No fue mucho tiempo antes que los expertos en computaci�on, y especialmente per-
sonas que escrib��an compiladores, descubrieron que el formalismo no s�olo era �util
para expresar qu�e lenguaje deber��a ser aceptado por sus compiladores, sino que
tambi�en que podr��a ser utilizado como gu��a para estructurar sus compiladores. Una
vez que la relaci�on entre una parte del BNF y un compilador fue bien comprendida
emergieron programas que utilizaban como entrada una parte de la descripci�on del
lenguaje, y produc��an un esqueleto del compilador deseado. Tales programas son
ahora conocidos bajo el nombre de generadores de analizadores sint�acticos.
Adem�as de estos objetivos muy mundanas, es decir la construcci�on de compiladores,
el formalismo BNF tambi�en lleg�o a ser pronto un tema de estudio para quienes se
interesaban m�as en lo te�orico. En efecto, parec��a que el formalismo BNF era miembro
de una jerarqu��a de clases gramaticales que hab��a sido formulada unos a~nos antes
por el ling�uista Noam Chomsky en un intento de capturar el concepto de \lenguaje".
Surgieron preguntas sobre la expresividad del BNF, es decir, qu�e clases de lenguajes
pueden ser expresados por medio del BNF y cuales no, y consecuentemente c�omo
expresar restricciones y propiedades para las cuales el formalismo BNF no es lo
su�cientemente poderoso. En el texto veremos muchos ejemplos de esto.
1.2 An�alisis gramatical de gram�aticas libres de contexto
Hoy en d��a se usa cada vez menos la palabra Backus-Naur, y en lugar de ella e
inspirados por la jerarqu��a de Chomsky, a menudo utilizamos el t�ermino gram�aticas
libres de contexto. Para la construcci�on de los compiladores corrientes de lenguajes
habituales parece que esta clase es todav��a demasiado grande. Si utilizamos todo
el poder de los lenguajes libres de contexto obtenemos por lo general compiladores
ine�cientes y que probablemente no son buenos en el manejo de entradas err�oneas.
Puede que este �ultimo hecho no sea tan importante desde el punto de vista te�orico,
pero s�� desde un punto de vista pragm�atico. La mayor��a de las invocaciones de com-
piladores tienen a�un como su meta primaria el descubrir errores cometidos cuando
se transcribe un programa, y no tanto la generaci�on de c�odigo real. Este aspecto
est�a a�un m�as presente en lenguajes fuertemente tipados como Java y Hugs, don-
de el tipo de veri�caci�on realizada por los compiladores es una de las principales
contribuciones al incremento de la e�ciencia en el proceso de programaci�on.
Al construir un reconocedor para un lenguaje descrito por una gram�atica libre de
contexto uno a menudo quiere veri�car si la gram�atica tiene o no propiedades es-
pec���cas deseables. Desafortunadamente para los humanos no siempre es f�acil, y
a menudo es pr�acticamente imposible ver si estas propiedades son o no v�alidas.
Adem�as puede que sea muy caro veri�car si las propiedades deseadas son o no
v�alidas. Esto ha llevado a toda una jerarqu��a de clases de las gram�aticas libres de
contexto, algunas de las cuales son m�as potentes, y otras son f�aciles de veri�car por la
m�aquina, y algunas que pueden ser f�acilmente veri�cadas por una simple inspecci�on
humana. En este curso veremos muchos ejemplos de estas clases. La observaci�on
general es que cuanto m�as precisa sea la respuesta a una pregunta espec���ca, ma-
10 Objetivos
yor el esfuerzo computacional requerido y cada vez menor la posibilidad de que la
pregunta sea respondida por un ser humano.
1.3 Composicionalidad
Como veremos, la estructura de muchos compiladores es consecuencia directa de
la gram�atica que describe el lenguaje a ser compilado. Una vez reconocido este
fen�omeno se le dio el nombre de compilaci�on dirigida por sintaxis. En un an�alisis
m�as detallado, y bajo la in uencia de un estilo de programaci�on m�as orientado
a la programaci�on funcional, se reconoci�o que en efecto los compiladores son una
forma especial de homomor�smo, un concepto hasta entonces conocido s�olo por
los matem�aticos y por te�oricos de la computaci�on que estudian la descripci�on del
signi�cado de un lenguaje de programaci�on.
Esto no deber��a ser una sorpresa puesto que este reconocimiento es una consecuen-
cia directa de la tendencia de que cada vez partes m�as grandes de los compiladores
son m�as o menos generadas autom�aticamente a partir de una descripci�on formal de
alg�un aspecto de un lenguaje de programaci�on. Por ejemplo haciendo uso de una
descripci�on de su aspecto externo o haciendo uso de una descripci�on de la sem�antica
(signi�cado) de un lenguaje. Veremos muchos ejemplos de tales mapeos. Como efec-
to colateral adquiriremos una forma especial de escribir programas funcionales, que
a menudo simpli�ca sorprendentemente la soluci�on a primera vista de trabajos de
programaci�on bastante complicados. Veremos que el concepto de evaluaci�on pere-
zosa juega un papel importante para posibilitar estas implementaciones de manera
e�ciente y directa.
1.4 Mecanismos de abstracci�on
Una de las principales razones por las cuales lo que antes era el cometido de un equipo
de varias personas puede ahora ser f�acilmente realizado por un par de estudiantes de
primer a~no en cuesti�on de d��as o semanas, es que en los �ultimos treinta a~nos hemos
descubierto el tipo correcto de abstracciones a utilizarse y la forma m�as e�ciente de
dividir un problema en componentes m�as peque~nos. Desafortunadamente no existe
una manera f�acil de ense~nar las t�ecnicas que nos han llevado tan lejos. La �unica
manera que vemos es tomar una visi�on hist�orica y comparar las situaciones actuales
con las antiguas.
Sin embargo, afortunadamente han habido algunos desarrollos en el dise~no de len-
guajes de programaci�on, de los cuales queremos mencionar los desarrollos en el �area
de la programaci�on funcional. A�rmamos que la combinaci�on de un sistema de ti-
pos moderno, aunque un tanto elaborado, combinado con el concepto de evaluaci�on
perezosa, proporciona una plataforma ideal para desarrollar y practicar las habili-
dades de abstracci�on. No existe otro formalismo f�acilmente ejecutable que pueda
servir como una herramienta igualmente poderosa. Esperamos que al presentar mu-
chos algoritmos, y fragmentos de estos, en un lenguaje funcional moderno, podamos
mostrar el poder real de abstracci�on, e incluso encontrar alguna inspiraci�on para des-
arrollos futuros en el dise~no de lenguajes: es decir encontrar claves acerca de c�omo
extender tales lenguajes permiti�endonos explicitar patrones comunes, que hasta la
fecha solo hab��an sido demostrados mediante ejemplos.
Cap��tulo 2
Gram�aticas Libres de Contexto
introducci�on
A menudo queremos reconocer una estructura espec���ca escondida en una sucesi�on
de s��mbolos. Por ejemplo, al leer esta frase, la estructuramos autom�aticamente por
medio de nuestra comprensi�on del idioma Espa~nol. Por supuesto, no cualquier se-
cuencia de s��mbolos es una frase en Espa~nol. >Entonces c�omo caracterizamos las
frases en Espa~nol? Esta es una vieja pregunta que se propuso mucho tiempo an-
tes del uso generalizado de las computadoras: en el �area de investigaci�on de los
lenguajes naturales se ha propuesto a menudo la pregunta sobre lo que realmente
constituye un \idioma". La de�nici�on m�as simple a la que uno puede llegar dice
que el idioma Espa~nol iguala al conjunto de todas las oraciones gramaticalmente
correctas en Espa~nol, y que una oraci�on consiste en una secuencia de palabras en
Espa~nol. Esta terminolog��a ha sido llevada a las ciencias de la computaci�on: el
lenguaje de programaci�on Java puede ser visto como el conjunto de todos los pro-
gramas correctos en Java, mientras que un programa en Java puede ser visto como
una secuencia de s��mbolos de Java, tales como identi�cadores, palabras reservadas,
operadores espec���cos, etc.
Este cap��tulo presenta las nociones m�as importantes de este curso: el concepto de
lenguaje y de gram�atica. Un lenguaje es un conjunto, posiblemente in�nito, de
frases y las frases son secuencias de s��mbolos tomadas de un conjunto �nito (por
ejemplo secuencias de caracteres conocidas como cadenas). As�� como establecemos
que una oraci�on pertenece o no al idioma Espa~nol mediante la gram�atica Espa~nola
(recuerde que antes utilizamos la frase \gram�aticamente correcta"), contamos con
un formalismo gramatical para describir lenguajes arti�ciales.
Una diferencia con las gram�aticas para lenguajes naturales es que este formalis-
mo gramatical es completamente formal. Esta propiedad nos permite probar ma-
tem�aticamente que una frase pertenece a alg�un lenguaje, y a menudo tales pruebas
pueden ser constru��das autom�aticamente por una computadora en un proceso llama-
do an�alisis sint�actico.1 Note que �esta es bastante diferente de las gram�aticas para
lenguajes naturales, donde uno puede discrepar f�acilmente sobre si algo es correcto
en Espa~nol o no. Sin embargo, este enfoque completamente formal tambi�en conlleva
una desventaja; la expresividad de la clase de gram�aticas que vamos a describir en
este cap��tulo es bastante limitada, y hay muchos lenguajes que uno podr��a querer
1NT: A lo largo del texto se usa la expresi�on an�alisis sint�actico como traducci�on de parsing y
analizador sint�actico para traducir parser. En muchas ocasiones y cuando el contexto es claro se
usa solamente las palabras an�alisis o analizador respectivamente.
11
12 Gram�aticas Libres de Contexto
describir pero que no pueden ser descritos dadas las limitaciones del formalismo.
objetivos
El objetivo principal de este cap��tulo es introducir y mostrar la relaci�on entre los
principales conceptos para describir el problema de an�alisis sint�actico: los lenguajes
y frases, y las gram�aticas.
En particular despu�es de haber estudiado este cap��tulo:
� conocer�as los conceptos de lenguaje y frases ;
� sabr�as c�omo describir lenguajes por medio de gram�aticas libres de contexto;
� conocer�as la diferencia entre un s��mbolo terminal y un s��mbolo no terminal ;
� ser�as capaz de leer e interpretar la notaci�on BNF ;
� comprender�as el proceso de derivaci�on utilizado en la descripci�on de lenguajes;
� comprender�as el rol de los �arboles de an�alisis ;
� comprender�as la relaci�on entre las gram�aticas libres de contexto y los tipos de
datos;
� entender�as el formalismo EBNF ;
� entender�as los conceptos de sintaxis concreta y abstracta;
� ser�as capaz de convertir manualmente una gram�atica de notaci�on EBNF a una
de notaci�on BNF;
� ser�as capaz de estructurar una gram�atica libre de contexto simple en notaci�on
EBNF;
� ser�as capaz de veri�car si una simple gram�atica es ambigua o no;
� ser�as capaz de transformar una gram�atica, por ejemplo eliminando la recursi�on
a izquierda;
2.1 Lenguajes
El objetivo de esta secci�on es introducir los conceptos de lenguaje y frase.
En textos tradicionales de matem�aticas no es raro encontrar una de�nici�on de se-
cuencia que tiene la siguiente forma:
De�nici�on 1: Secuencia
Sea X un conjunto. El conjunto de secuencias sobre X , llamado X�, se de�ne comosecuencia
sigue:
� � es una secuencia, llamada secuencia vac��a, y
� si xs es una secuencia y x es un elemento de X , entonces x:xs es tambi�en una
secuencia, y
� nada m�as es una secuencia sobre X .
2
Hay dos cosas importantes que remarcar sobre esta de�nici�on del conjunto X�:
1. Es una instancia de un patr�on de de�nici�on muy com�un: es de�nida por in-
ducci�on, es decir, la de�nici�on del concepto re�ere al propio concepto.inducci�on
2. Corresponde casi exactamente a la de�nici�on del tipo [x] de listas de elementos
de un tipo x en Haskell; excepto para la cl�ausula �nal -ninguna otra cosa
2.1 Lenguajes 13
es una secuencia de elementos-X (en Haskell puedes de�nir listas in�nitas,
las secuencias siempre son �nitas). Puesto que este patr�on de de�nici�on es
muy com�un cuando se de�ne un tipo de dato recursivo; la �ultima parte de la
de�nici�on es siempre entendida impl��citamente: si no puede ser generada no
puede existir.
En lo que sigue, utilizaremos los tipos de datos y las funciones de Haskell para la
implementaci�on de X�.
Las funciones en X� tales como reverse, length y concatenaci�on (++ en Haskell) se
de�nen inductivamente y estas de�niciones a menudo siguen un patr�on de recursi�on
que es similar a la de�nici�on misma de X�. Recuerde tambi�en los foldrs que se han
utilizado en el curso de Programaci�on Funcional.
Un comentario �nal sobre las secuencias tiene que ver con la notaci�on: En Haskell
uno escribe:
[x1,x2, ... ,xn] para x1 : x2 : ... xn : �
abba para ['a','b','b','a']
Cuando se habla de lenguajes y gram�aticas tradicionales uno utiliza:
abba para a : b : b : a : []
xy para x ++ y
Entonces las letras del principio del alfabeto representan s��mbolos �unicos, y las letras
del �nal del alfabeto representan secuencias de s��mbolos.
Note que la diferencia entre elementos �unicos (como a) y secuencias (como aa) no
es expl��cita en esta notaci�on; sin embargo es tradicional permitir que los caracteres
del principio del alfabeto representen caracteres �unicos (a, b, c, . . . ) y que los
s��mbolos del �nal del alfabeto representen secuencias de s��mbolos (x, y, z). Como
una consecuencia ax deber��a ser interpretada como una secuencia que comienza con
un s��mbolo �unico llamado a y una cola llamada x.
Ahora nos moveremos de secuencias individuales a conjuntos de secuencias �nitas o
in�nitas. Podemos empezar con algo de terminolog��a:
De�nici�on 2: Alfabeto, Lenguaje, Frase
� Un alfabeto es un conjunto �nito de s��mbolos. alfabeto
� Un lenguaje es un subconjunto de T �, para alg�un alfabeto T . lenguaje
� Una frase es un elemento de un lenguaje. frase
2
Algunos ejemplos de alfabetos son:
� el alfabeto Romano tradicional: fa; b; c; : : : ; zg;
� el alfabeto binario f0; 1g ;
� el conjunto de palabras reservadas fif; then; elseg
� el conjunto de caracteres l = fa; b; c; d; e; i; k; l; m; n; m; o; p; r; s; t; u; w; xg;
� el conjunto de palabras en Espa~nol fcurso; practica; ejercicio; exameng.
Ejemplos de lenguajes son:
� T �, � (el conjunto vac��o), f�g y T son lenguajes sobre el alfabeto T ;
14 Gram�aticas Libres de Contexto
� el conjunto fcurso; practica; ejercicio; exameng es un lenguaje sobre el al-
fabeto de caracteres l y examen es una frase de �este.
La pregunta que surge ahora es c�omo especi�car un lenguaje. Puesto que un lenguaje
es un conjunto, inmediatamente podemos percibir tres enfoques:
� enumerar todos los elementos del conjunto expl��citamente;
� caracterizar los elementos del conjunto por medio de un predicado;
� de�nir qu�e elementos pertenecen al conjunto por medio de la inducci�on.
Acabamos de ver algunos ejemplos del primer y tercer enfoque. Ejemplos del segundo
enfoque son:
� Los n�umeros naturales pares f n j n 2 f0; 1; ::; 9g�; n mod 2 = 0 g;
� PAL, los pal��ndromes, secuencias que se leen igualmente de atr�as para adelante
que de adelante para atr�as, sobre el alfabeto fa; b; cg: f s j s 2 fa; b; cg�; s =
sR g, donde sR denota la secuencia s invertida.
Una de las diferencias fundamentales entre el enfoque inductivo y el predicativo para
de�nir un lenguaje es que el �ultimo enfoque es constructivo; es decir, nos proporciona
una forma de enumerar todos los elementos de un lenguaje. Si de�nimos un lenguaje
por medio de un predicado s�olo tenemos un medio para decidir si un elemento
pertenece o no a un lenguaje. Un famoso ejemplo de un lenguaje que es f�acilmente
de�nido en la forma predicativa, pero para el cual la prueba de pertenencia es muy
dif��cil, es el conjunto de los n�umeros primos.
Muy a menudo queremos probar que un lenguaje L, que se de�ne por medio de
una de�nici�on inductiva, tiene una propiedad espec���ca P . Si esta propiedad es de
la forma: P (L) = 8x 2 L : P (x), entonces lo que en realidad nosotros estamos
haciendo es veri�car que las dos de�niciones, una inductiva y una predicativa, de�nen
el mismo lenguaje.
Como los lenguajes son conjuntos, los operadores usuales como uni�on, intersecci�on
y diferencia pueden utilizarse para construir nuevos lenguajes a partir de lenguajes
existentes. El complemento del lenguaje L sobre el alfabeto T es L = f x j x 2
T �; x =2 Lg.
Adem�as de estos operadores de conjuntos, hay operadores m�as espec���cos, que se
aplican solamente a conjuntos de secuencias. Usaremos estos operadores princi-
palmente en el cap��tulo de lenguajes regulares, Cap��tulo 5. Note que [ denota el
conjunto uni�on, as��: f1; 2g [ f1; 3g= f1; 2; 3g.
De�nici�on 3: Operaciones de un lenguaje
Sean L y M lenguajes sobre el mismo alfabeto T , entonces:
L = T � � L complemento de L
LR = f sR j s 2 L g reverso de L
LM = f st j s 2 L; t 2M g concatenaci�on de L y M
L0 = f�g 0-�esima potencia de L
Ln = LL : : :L (n times) n-�esima potencia de L
L� = L0 [ L1 [ L2 [ : : : clausura estrella de L
L+ = L1 [ L2 [ : : : clausura positiva de L
2
2.2 Gram�aticas 15
Las siguientes ecuaciones se derivan inmediatamente de las de�niciones anteriores:
L� = f�g [ LL�
L+ = LL�
Ejercicio 2.1 . Sea L = fab; aa; baag. >Cu�ales de las siguientes cadenas est�an en L�?
abaabaaabaa; aaaabaaaa; baaaaabaaaab; baaaaabaa
/
Ejercicio 2.2 . >Cu�ales son los elementos de ��? /
Ejercicio 2.3 . Probar que para cualquier lenguaje:
1. � L = L� = �
2. f�g L = L f�g = L
/
Ejercicio 2.4 . >Podr��a usted justi�car la elecci�on de L0 = f�g?
Ayuda: Establecer una de�nici�on inductiva para las potencias de un lenguaje. /
Ejercicio 2.5 . En esta secci�on hemos de�nido dos operadores \estrella": uno para conjuntos ar-
bitrarios (De�nici�on 1) y otro para lenguajes (De�nici�on 3). >Hay alguna diferencia
entre estos dos operadores? /
2.2 Gram�aticas
El objetivo de esta secci�on es introducir el concepto de gram�aticas libres de contexto.
Puede que trabajar con conjuntos sea divertido, pero es complicado manipular con-
juntos, y probar las propiedades de conjuntos. Para estos prop�ositos introducimos
de�niciones sint�acticas de conjuntos, llamadas gram�aticas. Esta secci�on s�olo anali-
zar�a las llamadas gram�aticas libres de contexto, un tipo de gram�aticas que son con-
venientes para el procesamiento autom�atico, y que pueden describir una gran clase
de lenguajes. Pero la clase de lenguajes que pueden ser descritos por gram�aticas
libres de contexto es limitada.
En la secci�on anterior hemos de�nido PAL, el lenguaje de pal��ndromes, por medio
de un predicado. Aunque esta de�nici�on de�ne el lenguaje que queremos, es dif��cil
utilizarla en demostraciones y programas. Una observaci�on importante es el hecho
de que el conjunto de pal��ndromes se puede de�nir inductivamente como sigue:
De�nici�on 4: Pal��ndromes por inducci�on
� la cadena vac��a, �, es un pal��ndrome;
� las cadenas que consisten de un solo car�acter, a, b, and c, son pal��ndromes;
� si P es pal��ndrome; entonces las cadenas obtenida por la adici�on adelante y
atr�as del mismo car�acter, a, b, y c, son pal��ndromes tambi�en, esto es, las
16 Gram�aticas Libres de Contexto
cadenas
aPa
bPb
cPc
son pal��ndromes.
2
Las primeras dos partes de la de�nici�on cubren los casos b�asicos. La �ultima parte
de la de�nici�on cubre los casos inductivos. Todas las cadenas que pertenezcan al
lenguaje PAL de�nido inductivamente utilizando la de�nici�on de arriba se leen de
igual manera hacia adelante o hacia atr�as. Por consiguiente esta de�nici�on se dice
que es consistente. A la inversa, si una cadena consistente de as, bs, y cs se lee deconsistente
la misma manera hacia delante y hacia atr�as entonces pertenece al lenguaje PAL.
Por consiguiente esta de�nici�on se dice que es completa.completa
Encontrar una de�nici�on inductiva para un lenguaje que es descrito por un predicado
(como el utilizado para pal��ndromes) es frecuentemente una tarea no trivial. A
menudo es relativamente f�acil encontrar una de�nici�on consistente, pero tambi�en
es necesario estar seguro de que la de�nici�on es completa. Un m�etodo t��pico para
demostrar la consistencia y completitud de una de�nici�on inductiva es la inducci�on
matem�atica.
Ahora que contamos con una de�nici�on inductiva para pal��ndromes podemos dar
una representaci�on formal de esta de�nici�on inductiva.
Las de�niciones inductivas como la de arriba pueden ser representadas formalmente
haciendo uso de reglas de deducci�on que tienen el siguiente formato:
a1; a2; � � � ; an ` a or ` a
Este primer tipo de regla de deducci�on debe leerse como sigue:
si a1, a2, : : : y an son verdaderas,
entonces a es verdadera
El segundo tipo de regla deductiva, llamada axioma, tiene que leerse como sigue:
a es verdadera
Utilizando estas reglas de deducci�on podemos escribir la de�nici�on inductiva para
PAL como sigue:
` � 2 PAL'
` a 2 PAL'
` b 2 PAL'
` c 2 PAL'
P 2 PAL' ` aPa 2 PAL'
P 2 PAL' ` bPb 2 PAL'
P 2 PAL' ` cPc 2 PAL'
Aunque la de�nici�on de PAL' es completamente formal, es todav��a laborioso escri-
birla. Como en ciencias de la computaci�on utilizamos muchas de�niciones que siguen
2.2 Gram�aticas 17
estos patrones, introducimos un esquema para este patr�on, llamado gram�atica. Unagram�atica
gram�atica consiste en reglas de producci�on para la construcci�on de pal��ndromes. La
regla con la cual se construye la cadena vac��a es la siguiente:
P ! �
Esta regla corresponde al axioma que establece que la cadena vac��a � es un pal��ndrome.
Una regla de la forma s! �, donde s es un s��mbolo y � es una secuencia de s��mbolos,
es llamada una regla de producci�on, �o simplemente producci�on. Una regla de pro- regla
de pro-
duc-
ci�on
ducci�on puede ser considerada como una posible forma de reescribir el s��mbolo s.
El s��mbolo P a la izquierda de la echa es un s��mbolo que denota pal��ndromes.
Tal s��mbolo es un ejemplo de un s��mbolo no terminal , o simpli�cando no termi-s��mbolo
no ter-
minal
nal. Los s��mbolos no terminales son tambi�en llamados s��mbolos auxiliares, su �unico
prop�osito es denotar estructura, no son parte del alfabeto del lenguaje. Otras tres
reglas b�asicas de producci�on son las reglas para construir pal��ndromes consisten-
tes en un solo car�acter. Cada uno de estos elementos de cadenas a, b, y c es un
pal��ndrome, y da lugar a una producci�on:
P ! a
P ! b
P ! c
Estas reglas de producci�on corresponden a los axiomas que establecen que las cade-
nas de un elemento a, b, y c son pal��ndromes. Si una cadena � es un pal��ndrome,
entonces obtendremos un nuevo pal��ndrome por la adici�on adelante y atr�as de una
a, b, o c a ella, es decir, a�a, b�b, y c�c son tambi�en pal��ndromes. Para obtener
estos pal��ndromes utilizamos las siguientes producciones recursivas:
P ! aPa
P ! bPb
P ! cPc
Estas reglas de producci�on corresponden a las reglas de deducci�on que determinan
que, si P es un pal��ndrome, entonces uno puede deducir que aPa, bPb y cPc son
tambi�en pal��ndromes. La gram�atica que hab��amos presentado hasta ahora consiste
de tres componentes:
� el conjunto de terminales fa; b; cg; terminal
� el conjunto de no terminales fPg;
� y el conjunto de producciones (las siete producciones que hemos introducido
hasta ahora).
Tome en cuenta que la intersecci�on del conjunto de terminales y el conjunto de no
terminales est�a vac��a. Completamos la descripci�on de la gram�atica con la adici�on
de un cuarto componente: el s��mbolo inicial no terminal P . En este caso tenemos s��mbolo
inicialuna sola elecci�on para el s��mbolo inicial, pero una gram�atica puede tener muchos
s��mbolos no terminales.
Esto lleva a la siguiente gram�atica para PAL
P ! �
18 Gram�aticas Libres de Contexto
P ! a
P ! b
P ! c
P ! aPa
P ! bPb
P ! cPc
La de�nici�on del conjunto de terminales fa; b; cg, y el conjunto de no terminales
fPg, a menudo es impl��cita. Adem�as el s��mbolo inicial es impl��citamente de�nido
puesto que existe un solo no terminal.
Concluimos este ejemplo con la de�nici�on formal de una gram�atica libre de contexto.
De�nici�on 5: Gram�atica libre de contexto
Una gram�atica libre de contexto G es una tupla de 4 elementos (T;N;R;S) donde:gram�atica
libre
de con-
texto
� T es un conjunto �nito de s��mbolos terminales;
� N es un conjunto �nito de s��mbolos no terminales;
� R es un conjunto �nito de reglas de producci�on. Cada producci�on tiene la
forma A ! �; donde A es un no terminal y � es una secuencia de terminales
y no terminales;
� S es el s��mbolo inicial, S 2 N .
2
El adjetivo \libre de contexto" en la de�nici�on de arriba se debe a las reglas de
producci�on espec���cas que son consideradas: exactamente un no terminal en el lado
izquierdo. No todo lenguaje puede ser descrito mediante una gram�atica libre de
contexto. El ejemplo est�andar es fanbncn j n 2 IIN g. Volveremos a encontrar este
ejemplo posteriormente en el texto.
2.2.1 Convenciones de notaci�on
En la de�nici�on de la gram�atica para PAL hemos escrito cada producci�on en una
l��nea. Puesto que esto ocupa gran cantidad de espacio y ya que las reglas de pro-
ducci�on forman el coraz�on de cada gram�atica, introducimos el siguiente esquema.
En lugar de escribir:
S ! �
S ! �
unimos las dos producciones de S en una l��nea utilizando el s��mbolo j:
S ! � j �
de esta forma podemos reescribir cualquier cantidad de reglas de reescritura para
un no terminal, as�� la gram�atica para PAL puede tambi�en escribirse como sigue:
P ! � j a j b j c j aPa j bPb j cPc
La notaci�on que utilizamos para gram�aticas es conocida como BNF {Backus Naur
Form{, en honor a Backus y Naur, quienes fueron los primeros en utilizar estaBNF
2.3 El lenguaje de una gram�atica 19
notaci�on para la de�nici�on de gram�aticas.
Otra pr�actica est�andar de notaci�on tiene que ver con los nombres de producciones.
Algunas veces queremos dar nombres a las reglas de producci�on. Los nombres ser�an
escritos delante de la producci�on, como por ejemplo:
Alpha rule : S ! �
Beta rule : S ! �
Ejercicio 2.6 . Dar una gram�atica libre de contexto para el conjunto de frases sobre el alfabeto
X donde:
1. X = fag
2. X = fa; bg
/
Ejercicio 2.7 . Dar una gram�atica para pal��ndromes sobre el alfabeto fa; bg /
Ejercicio 2.8 . Dar una gram�atica para el lenguaje
L = f s sR j s 2 fa; bg� g
Este lenguaje es conocido como lenguaje de pal��ndromes espejo. /
Ejercicio 2.9 . Una secuencia de paridad es una secuencia que consiste de ceros y unos que
tiene una cantidad par de unos. Dar una gram�atica para secuencias de paridad. /
Ejercicio 2.10 . Dar una gram�atica para el lenguaje
L = fw j w 2 fa; bg� ^ nr(a; w) = nr(b; w)g
donde nr(c; w) es la cantidad de cs que aparecen en w. /
2.3 El lenguaje de una gram�atica
El objetivo de esta secci�on es describir la relaci�on entre gram�aticas y lenguajes:
mostrar como derivar frases de un lenguaje dada su gram�atica.
Hemos visto como obtener una gram�atica a partir de un lenguaje dado. Ahora
consideremos lo contrario: >C�omo obtener un lenguaje de una gram�atica dada?
Antes de responder a esta pregunta primero debemos indicar lo que se puede hacer
con una gram�atica. La respuesta es simple: con ella podemos derivar secuencias.
>C�omo construimos un pal��ndrome? Un pal��ndrome es una secuencia de terminales,
en nuestro caso caracteres a, b y c, que pueden ser derivados en cero o m�as pasos
de derivaci�on directa a partir del s��mbolo inicial P utilizando las producciones de la
gram�atica para pal��ndromes dadas anteriormente.
Por ejemplo, la secuencia bacab se puede derivar utilizando la gram�atica para
pal��ndromes como sigue:
20 Gram�aticas Libres de Contexto
P
)
bPb
)
baPab
)
bacab
Tal construcci�on se llama una derivaci�on. En el primer paso de la producci�on dederivaci�on
esta derivaci�on, se utiliza P ! bPb para reescribir P como bPb. En el segundo paso
de producci�on se utiliza P ! aPa para reescribir bPb como baPab. Finalmente en
el �ultimo paso de producci�on se utiliza P ! c para reescribir baPab como bacab.
Hacer una derivaci�on puede verse como una prueba constructiva de que la cadena
bacab es pal��ndrome.
Ahora describiremos los pasos de derivaci�on con mayor formalidad.
De�nici�on 6: Derivaci�on
Suponga que X ! � es una producci�on de una gram�atica, donde X es un s��mbolo
no terminal y � es una secuencia de s��mbolos (terminales o no terminales). Sea �X
una secuencia de s��mbolos (terminales o no terminales). Decimos que �X deriva
directamente la secuencia �� , que se obtiene de reemplazar el lado izquierdo Xderivaci�on
directa de la producci�on por el lado derecho correspondiente �. Escribimos �X ) ��
y tambi�en decimos que �X reescribe a �� en un paso. Una secuencia '1 derivaderivaci�on
una secuencia 'n, escrita '1�
) 'n, si existen secuencias '1; : : : ; 'n con n � 1 tal
que
8i; 1 � i < n : 'i ) 'i+1
Si n = 1 esta declaraci�on es evidentemente verdadera, y se concluye que podemos
derivar cada frase de s�� misma en cero pasos:
'�
) '
Una derivaci�on parcial es una derivaci�on de una secuencia � que todav��a contienederivaci�on
parcial no terminales. 2
Encontrar una derivaci�on '1�
) 'n es, en general, una tarea no trivial. Una deri-
vaci�on es s�olo una rama de todo un �arbol de b�usqueda que contiene muchas m�as
ramas. Cada rama representa una direcci�on (exitosa o no) en la que una posible
derivaci�on puede seguir adelante. Uno de los desaf��os m�as importante es disponer
las cosas de manera tal que se encuentre una derivaci�on de una manera e�ciente.
De la derivaci�on anterior se desprende que
P�
) bacab
Debido a que esta derivaci�on comienza con el s��mbolo inicial de la gram�atica y que
resulta en una secuencia que consiste s�olo de terminales (una cadena de terminales),
decimos que la cadena bacab pertenece al lenguaje generado por la gram�atica para
pal��ndromes. En general, de�nimos:
De�nici�on 7: Lenguaje de una gram�atica (o lenguaje generado por una gram�atica)
El lenguaje de una gram�atica G = (T;N;R;S), normalmente denotado por L(G),
2.3 El lenguaje de una gram�atica 21
se de�ne como
L(G) = f s j S�
) s ; s 2 T �g
2
Algunas veces tambi�en hablamos acerca de un lenguaje de un no terminal, que se
de�ne por
L(A) = f s j A�
) s ; s 2 T �g
para el no terminal A. El lenguaje de una gram�atica podr��a haber sido de�nido
como el lenguaje del s��mbolo inicial.
Note que diferentes gram�aticas pueden tener el mismo lenguaje. Por ejemplo, si
extendemos la gram�atica para PAL con la producci�on P ! bacab, obtendremos una
gram�atica con exactamente el mismo lenguaje que PAL. Dos gram�aticas que generan
el mismo lenguaje se dicen equivalentes. Por lo tanto para una gram�atica espec���ca
existe un lenguaje �unico, pero lo opuesto no es cierto, dado un lenguaje podemos
construir muchas gram�aticas que generan este lenguaje. En lenguaje matem�atico:
el mapeo (la correspondencia) entre una gram�atica y un lenguaje no es biyectivo.
De�nici�on 8: Lenguaje libre de contexto
Un lenguaje libre de contexto es un lenguaje generado por una gram�atica libre de lenguaje
libre
de con-
texto
contexto. 2
Todos los pal��ndromes pueden derivarse a partir del s��mbolo inicial P . Por lo tanto
el lenguaje de nuestra gram�atica para pal��ndromes es PAL, el conjunto de todos los
pal��ndromes sobre el alfabeto fa; b; cg y PAL es libre de contexto.
2.3.1 Algunos lenguajes b�asicos
Los d��gitos forman buena parte de la programaci�on y de otros lenguajes, como tam-
bi�en las letras. En esta subsecci�on de�niremos algunas gram�aticas que especi�can
algunos lenguajes b�asicos tales como d��gitos y letras. Estas gram�aticas ser�an utili-
zadas a menudo en secciones posteriores.
� El lenguaje de d��gitos simples es especi�cado por una gram�atica con 10 reglas
de producci�on para el no terminal Dig .
Dig ! 0 j 1 j 2 j 3 j 4 j 5 j 6 j 7 j 8 j 9
� Obtenemos secuencias de d��gitos por medio de la siguiente gram�atica:
Digs ! � j Dig Digs
� Los n�umeros naturales son secuencias de d��gitos que empiezan con un d��gito
que no es cero. Por lo tanto para especi�car los n�umeros naturales, primero
de�nimos el lenguaje de d��gitos sin cero.
Dig-0 ! 1 j 2 j 3 j 4 j 5 j 6 j 7 j 8 j 9
Ahora podemos de�nir los n�umeros naturales como sigue.
Nat ! 0 j Dig-0 Digs
22 Gram�aticas Libres de Contexto
� Los n�umeros enteros son n�umeros naturales precedidos por un signo. Si un
n�umero natural no est�a precedido por un signo, se supone que es un n�umero
positivo.
Sign ! + j -
Z ! Sign Nat j Nat
� Los lenguajes de las letras min�usculas y may�usculas se especi�can cada uno
con una gram�atica de 26 producciones:
ULetter ! a j b j : : : j z
CLetter ! A j B j : : : j Z
Por supuesto que en las de�niciones reales de estas gram�aticas tenemos que
escribir cada una de las 26 letras. Una letra es ahora o min�uscula o may�uscula.
Letter ! ULetter j CLetter
� Los nombres de las variables, funciones, tipos de dato, etc, son todos repre-
sentados por identi�cadores en los lenguajes de programaci�on. La siguiente
gram�atica para los identi�cadores podr��a ser utilizada en un lenguaje de pro-
gramaci�on:
Identi�er ! Letter SoS
SoS ! � j Letter SoS j Dig SoS
Un identi�cador empieza con una letra seguida de una secuencia de letras y
d��gitos. Tal vez queremos permitir m�as s��mbolos tales como por ejemplo la
l��nea de subrayado (_) y pesos ($) entonces, por supuesto, tendr��amos que
modi�car las gram�aticas.
� Los c�odigos postales holandeses consisten de cuatro d��gitos, en los que el primer
d��gito es diferente de cero, seguido por dos letras may�usculas. Por lo tanto
ZipCode ! Dig-0 Dig Dig Dig CLetter CLetter
Ejercicio 2.11 . Una cadena terminal siempre se deriva en uno o m�as pasos. >Por qu�e? /
Ejercicio 2.12 . >Qu�e lenguaje se genera con la gram�atica de una sola regla de producci�on:
S ! � ?
/
Ejercicio 2.13 . >Qu�e lenguaje genera la gram�atica con las siguientes reglas de producci�on?
S ! Aa
A ! B
B ! Aa
/
2.4 �Arboles de An�alisis 23
Ejercicio 2.14 . D�e una descripci�on sencilla del lenguaje generado por la gram�atica con las
producciones:
S ! aA
A ! bS
S ! �
/
Ejercicio 2.15 . >Es libre de contexto el lenguaje L de�nido en el ejercicio 2.1? /
2.4 �Arboles de An�alisis
El objetivo de esta secci�on es introducir los �arboles de an�alisis y mostrar c�omo �estos
se relacionan con las derivaciones. Adem�as, en esta secci�on se de�nen gram�aticas
(no) ambiguas.
Para cualquier derivaci�on parcial, es decir una derivaci�on que contiene no terminales
en su lado derecho, puede haber varias producciones de la gram�atica que pueden
usarse para extender la derivaci�on parcial y por lo tanto, se puede tener diferentes
derivaciones para la misma frase. Hay dos razones por las cuales las derivaciones
di�eren para una frase espec���ca:
� S�olo di�ere el orden en cual se eligen los pasos de derivaci�on. Todas estas
derivaciones se consideran equivalentes.
� Se han elegido diferentes pasos de derivaci�on. Se considera que tales deriva-
ciones son diferentes.
Este es un ejemplo sencillo. Considere la gram�atica SequenceOfS con producciones:
S ! SS
S ! s
Utilizando esta gram�atica podemos derivar la frase sss como sigue (hemos subrayado
el no terminal que se reemplaza):
S�
) SS�
) SSS�
) SsS�
) ssS�
) sss
S�
) SS�
) sS�
) sSS�
) sSs�
) sss
Estas derivaciones son las mismas excepto por el orden en que se toman los pasos
de derivaci�on. Sin embargo, la siguiente derivaci�on no utiliza los mismos pasos de
derivaci�on:
S�
) SS�
) SSS�
) sSS�
) ssS�
) sss
En esta derivaci�on la primera S se reescribe como SS en lugar de s.
El conjunto de todas las derivaciones equivalentes puede ser representado seleccio-
nando un, as�� llamado, elemento can�onico. Un buen candidato para tal elemento
can�onico es la derivaci�on a izquierda. En una derivaci�on a izquierda, s�olo se rees- derivaci�on
a iz-
quier-
da
24 Gram�aticas Libres de Contexto
cribe el no terminal m�as a la izquierda. Si existe una derivaci�on de una frase x que
utiliza las producciones de una gram�atica, entonces existe una derivaci�on a izquierda
de x. La derivaci�on a izquierda que corresponde a las dos derivaciones equivalentes
anteriores es:
S�
) SS�
) sS�
) sSS�
) ssS�
) sss
Existe otra forma conveniente para representar derivaciones equivalentes: todas tie-
nen el mismo �arbol de an�alisis sint�actico (o �arbol de derivaci�on). Un �arbol de an�alisis�arbol
de
an�alisis
es una representaci�on de una derivaci�on que hace abstracci�on del orden en que se
escogen los pasos de derivaci�on. El �arbol de an�alisis resultante de las dos primeras
derivaciones de la frase sss es de la siguiente forma.
S
�������
????
???
S S
�������
????
???
s S S
s s
La tercera derivaci�on de la frase sss resulta en un �arbol de derivaci�on diferente:
S
�������
????
???
S
�������
????
??? S
S S s
s s
Tomando otro ejemplo, todas las derivaciones de la cadena abba que utilizan las
producciones de la gram�atica
P ! �
P ! APA
P ! BPB
A ! a
B ! b
2.4 �Arboles de An�alisis 25
se representan con el siguiente �arbol de derivaci�on:
P
ooooooooooooooo
OOOO
OOOO
OOOO
OOO
A P
~~~~~~~
@@@@
@@@ A
a B P B a
b � b
Un �arbol de derivaci�on se puede ver como una interpretaci�on estructural de la frase
derivada. Note que podr��a haber m�as de una interpretaci�on estructural de una frase
con relaci�on a una gram�atica dada. Tales gram�aticas se llaman ambiguas.
De�nici�on 9: Gram�atica ambigua, gram�atica no ambigua
Una gram�atica es no ambigua si cada frase tiene una �unica derivaci�on a izquierda, no am-
biguao en t�erminos equivalentes, si cada frase tiene un �unico �arbol de derivaci�on. De otra
manera se llama ambigua. 2 ambigua
La gram�atica SequenceOfS para construir secuencias de ss es un ejemplo de una
gram�atica ambigua.
Generalmente es bastante dif��cil traducir lenguajes con gram�aticas ambiguas. Por lo
tanto encontrar�a que la mayor��a de las gram�aticas de lenguajes de programaci�on y
otros lenguajes que se utilizan en procesamiento de la informaci�on son no ambiguos.
Las gram�aticas han demostrado ser muy exitosas en la especi�caci�on de lenguajes
arti�ciales (tales como lenguajes de programaci�on). Sin embargo, han demostrado
ser menos exitosas en la especi�caci�on de lenguajes naturales (tales como el Ingl�es),
en parte por que es extremadamente dif��cil construir una gram�atica no ambigua
que especi�que una parte no trivial del lenguaje. La ambig�uedad de los lenguajes
naturales puede por lo tanto ser considerada como una ventaja para sus usuarios (por
ejemplo los pol��ticos), ciertamente es considerada una desventaja para traductores
de idiomas porque generalmente es imposible mantener un signi�cado ambiguo en
una traducci�on.
2.4.1 De gram�aticas libres de contexto a tipos de datos
Para cada gram�atica libre de contexto podemos de�nir un tipo de datos correspon-
diente en Haskell. Los valores de estos tipos de datos representan �arboles de an�alisis
de una gram�atica libre de contexto. Como ejemplo tomamos la gram�atica utilizada
al iniciar esta secci�on:
S ! SS
S ! s
Primero damos un nombre a cada una de las producciones de esta gram�atica:
Beside : S ! SS
Single : S ! s
y ahora interpretamos el s��mbolo inicial de la gram�atica S como un tipo de datos,
utilizando los nombres de las producciones como constructores:
26 Gram�aticas Libres de Contexto
data S = Beside S S
| Single Char
Observe que este tipo de datos es demasiado general: el tipo Char deber��a ser solo el
caracter s. Este tipo de dato se puede utilizar para representar �arboles de an�alisis de
las frases de S . Por ejemplo, el �arbol de an�alisis que corresponde a las dos primeras
derivaciones de la secuencia sss se representa por el siguiente valor del tipo de datos
S:
Beside (Single 's') (Beside (Single 's') (Single 's'))
La tercera derivaci�on de la frase sss produce el siguiente �arbol de an�alisis:
Beside (Beside (Single 's') (Single 's')) (Single 's')
Como el tipo de datos S es muy general, reconsideraremos la construcci�on de tipos
de dato para gram�aticas libres de contexto en la Secci�on 2.6.
Ejercicio 2.16 . Considere la gram�atica para pal��ndromes que construy�o en el ejercicio 2.7.
D�e �arboles de an�alisis para los pal��ndromes cPal1 = 00abaaba00 y cPal2 = 00baaab00.
De�na un tipo de datos Palc correspondiente a la gram�atica y represente los �arboles
de an�alisis para cPal1 y cPal2 como valores de Palc. /
2.5 Transformaciones de gram�aticas
El objetivo de esta secci�on es analizar las propiedades de las gram�aticas, las transfor-
maciones de gram�aticas y tambi�en mostrar c�omo se puede utilizar las transformacio-
nes de gram�aticas para obtener gram�aticas que satisfagan propiedades espec���cas.
Las gram�aticas satisfacen propiedades. Ejemplos de propiedades son:
� una gram�atica puede ser no ambigua, es decir, cada frase de su lenguaje tiene
un �unico �arbol de an�alisis;
� una gram�atica puede tener la propiedad por la cu�al s�olo el s��mbolo inicial puede
derivar la cadena vac��a; ning�un otro no terminal puede derivar la cadena vac��a;
� una gram�atica puede tener la propiedad por la cual cada producci�on tiene un
solo terminal, o dos no terminales en su lado derecho. Se dice que tal gram�atica
est�a en la forma normal de Chomsky.
>Entonces, por qu�e estamos interesados en estas propiedades? Algunas de estas
propiedades implican que es posible construir �arboles de an�alisis para frases de una
gram�atica de una �unica manera. Algunas propiedades implican que podemos cons-
truir estos �arboles de an�alisis muy r�apidamente. Otras propiedades se utilizan para
probar hechos acerca de las gram�aticas. Sin embargo, otras propiedades se utilizan
para calcular e�cazmente alguna informaci�on a partir de los �arboles de an�alisis de
una gram�atica.
Por ejemplo, suponga que tenemos un programa p que construye �arboles de an�alisis
para frases de gram�aticas en la forma normal de Chomsky, y que podemos probar
que toda gram�atica puede ser transformada en una gram�atica en la forma normal
2.5 Transformaciones de gram�aticas 27
de Chomsky (cuando decimos que una gram�atica G puede ser transformada en otra
gram�atica G0, signi�ca que existe alg�un procedimiento para obtener G0 a partir de G,trans-
for-
maci�on
de
gram�a-
ticas
y que G y G0 generan el mismo lenguaje). Entonces podemos utilizar este programa
p para construir �arboles de an�alisis para cualquier gram�atica.
Como a veces es conveniente tener una gram�atica que satisface una propiedad par-
ticular de un lenguaje, nos gustar��a poder transformar gram�aticas en otras gram�aticas
que generen el mismo lenguaje, pero que posiblemente satisfagan diferentes propie-
dades. Esta secci�on describe varias transformaciones de gram�aticas
� Eliminando producciones duplicadas.
� Sustituyendo el lado derecho por no terminales.
� Factorizaci�on a izquierda.
� Eliminando recursi�on a izquierda.
� Separador asociativo.
� Introducci�on de prioridades.
Hay muchas m�as transformaciones que las que describimos aqu��, mostraremos so-
lamente un peque~no pero �util conjunto de transformaciones de gram�aticas. En las
siguientes transformaciones asumimos que u, v, w, x, y, y z denotan secuencias de
terminales y no terminales es decir son elementos de (N [ T )�.
Eliminando producciones duplicadas
Esta transformaci�on de gram�aticas es una transformaci�on que puede ser aplicada
a cualquier gram�atica que tiene la forma adecuada. Si una gram�atica contiene dos
ocurrencias de la misma regla de producci�on, una de estas ocurrencias se puede
eliminar. Por ejemplo,
A ! u j u j v
puede ser transformada en:
A ! u j v
Sustituyendo el lado derecho por no terminales
Si un no terminal N ocurre en el lado derecho de una producci�on, la producci�on
puede reemplazarse por tantas producciones como las que existan para N , en las
que N est�a siendo reemplazado por sus lados derechos. Por ejemplo,
A ! uBv j z
B ! x j w
puede ser transformada en
A ! uxv j uwv j z
B ! x j w
Factorizaci�on a Izquierda
La factorizaci�on a izquierda de una gram�atica es una transformaci�on gramatical, factori-
zaci�on
a iz-
quier-
da
que es �util cuando dos producciones para el mismo no terminal comienzan con la
misma secuencia de s��mbolos (terminales y/o no terminales). Estas dos producciones
pueden ser reemplazadas por una sola producci�on que termina con un nuevo no
28 Gram�aticas Libres de Contexto
terminal, reemplazando la parte de la secuencia despu�es de la secuencia com�un
inicial. Se a~naden dos producciones para el nuevo no terminal: una para cada una
de las dos diferentes secuencias �nales de las dos producciones. Por ejemplo:
A ! xy j xz j v
donde x 2 (N [ T )�, y x 6= �, puede ser transformada en:
A ! xZ j v
Z ! y j z
donde Z es un nuevo no terminal.
Eliminando recursi�on a izquierda
Una producci�on recursiva a izquierda es una producci�on en la que el lado derechorecursi�on
a iz-
quier-
da
empieza con el no terminal del lado izquierdo. Por ejemplo, la producci�on
A ! Az
es recursiva a izquierda. Una gram�atica es recursiva a izquierda si podemos derivar
A�
) Az para alg�un no terminal A de la gram�atica. Las gram�aticas recursivas a la
izquierda son a veces indeseables. La siguiente transformaci�on elimina las produc-
ciones recursivas a la izquierda.
Para eliminar las producciones recursivas a la izquierda de un no terminal A, divida
las producciones de A en conjuntos de producciones recursivas a la izquierda y no
recursivos a la izquierda. Factorice A como sigue:
A ! Ax1 j Ax2 j : : : j Axn
A ! y1 j y2 j : : : j ym
con xi, yj 2 (N [ T )�, head yj 6= A, y 1 � i � n, 1 � j � m. Adicione un nuevo no
terminal Z, y reemplace las producciones de A por:
A ! yj j yjZ
Z ! xi j xiZ
Por ejemplo, la gram�atica SequenceOfS, vea la secci�on 2.4 con las producciones
S ! SS
S ! s
Es una gram�atica recursiva a la izquierda. El anterior procedimiento para eliminar
la recursi�on a izquierda devuelve las siguientes producciones:
S ! s j sZ
Z ! S j SZ
Este procedimiento para eliminar la recursi�on a izquierda solo funciona para recur-
si�on directa, es decir para producciones de la forma A ! Ax, . Para la recursi�on
indirecta, por ejemplo si las reglas de producci�on son A! Bx y B ! Ay, el proce-
dimiento es un poco m�as complicado.
2.5 Transformaciones de gram�aticas 29
Separador Asociativo
La siguiente transformaci�on de gram�aticas genera una lista de declaraciones, sepa-
radas por un punto y coma (;).
Decls ! Decls ; Decls
Decls ! Decl
Se omiten las producciones para Decl que generan una �unica declaraci�on. Esta
gram�atica es ambigua por la misma raz�on que SequenceOfS es ambigua. El operador
; es un separador asociativo en el lenguaje generado. Es decir: d1 ; (d2 ; d3) =
(d1 ; d2) ; d3 donde d1, d2, y d3 son declaraciones. Por tanto podemos utilizar
la siguiente gram�atica no ambigua para generar un lenguaje de declaraciones:
Decls ! Decl ; Decls
Decls ! Decl
Una gram�atica alternativa para el mismo lenguaje es
Decls ! Decls ; Decl
Decls ! Decl
Esta transformaci�on de gram�aticas s�olo puede ser aplicada porque el punto y coma
es asociativo en el lenguaje generado; no es una transformaci�on de gram�aticas que
puede ser aplicada ciegamente a cualquier gram�atica. La misma transformaci�on
puede aplicarse a gram�aticas con producciones de la forma:
A ! AaA
donde a es un operador asociativo en el lenguaje generado. Como un ejemplo puede
pensar en los n�umeros naturales con adici�on.
Introducci�on de prioridades
Otra forma de ambig�uedad surge a menudo en la parte que describe expresiones
de una gram�atica para un lenguaje de programaci�on. Por ejemplo, la siguiente
gram�atica genera expresiones aritm�eticas:
E ! E + E
E ! E * E
E ! (E)
E ! Digs
donde Digs genera una lista de d��gitos, vea la secci�on 2.3.1. Esta gram�atica es
ambigua: la frase 2+4*6 tiene dos �arboles de an�alisis: uno corresponde a (2+4)*6,
y otro corresponde a 2+(4*6). Si suponemos que * tiene mayor prioridad que +, la
�ultima expresi�on es la lectura deseada de la frase 2+4*6. Para obtener un �arbol de
an�alisis que respete esta prioridad transformamos la gram�atica como sigue:
E ! T
E ! E + T
T ! F
T ! T * F
F ! (E)
F ! Digs
30 Gram�aticas Libres de Contexto
Esta gram�atica genera el mismo lenguaje que la anterior gram�atica para expresiones,
pero respeta las prioridades de los operadores.
A menudo en la pr�actica se usan m�as de dos niveles de prioridad. Entonces, en
lugar de escribir una gran cantidad de reglas de producci�on formadas id�enticamente,
usamos no terminales parametrizados. Para 1 � i < n,
Ei ! Ei+1
Ei ! Ei OP i Ei+1
El operador OP i es un no terminal parametrizado que genera operadores de prio-
ridad i. Adem�as de las producciones de arriba, debe haber una producci�on para
expresiones de mayor prioridad, por ejemplo:
En ! (E1) j Digs
Una transformaci�on gramatical transforma esta gram�atica en otra gram�atica que ge-
nera el mismo lenguaje. Para cada una de las transformaciones de arriba deber��amos
demostrar que el lenguaje generado es el mismo. Como las demostraciones son de-
masiado complicadas a esta altura, las omitiremos. Las demostraciones se pueden
encontrar en cualquier libro de texto de construcci�on de compiladores [1].
Existen muchas m�as transformaciones de gram�aticas, pero las descritas en esta sec-
ci�on son su�cientes por el momento. Recuerde que cada vez que usemos \izquierda"
(recursi�on a izquierda, factorizaci�on a izquierda) podemos reemplazarla por la de-
recha, y obtener una transformaci�on dual de gram�aticas. Analizaremos un ejemplo
mayor de transformaci�on de gram�aticas despu�es de la siguiente secci�on.
Ejercicio 2.17 . El ejemplo est�andar de ambig�uedad en los lenguajes de programaci�on es el
else pendiente. Sea G la gram�atica que tiene el conjunto de s�imbolos terminales
f if; b; then; else ; a g y cuyas reglas de producci�on son:
S ! if b then S else S
S ! if b then S
S ! a
1 Dar dos �arboles de derivaci�on para la frase if b then if b then a else a.
2 Dar una gram�atica ambigua que genere el mismo lenguaje que G.
3 >C'omo evita Java el problema del else pendiente?
/
Ejercicio 2.18 . Una lista de bits (bit-list) es una lista no vac��a de bits separados por comas.
Una gram�atica para listas de bits est�a dada por
L ! B
L ! L ; L
B ! 0 j 1
Elimine la recursi�on a izquierda de esta gram�atica. /
2.6 Sintaxis concreta y abstracta 31
Ejercicio 2.19 . Considere la gram�atica con s��mbolo inicial S
S ! AB
A ! � j aaA
B ! � j Bb
1 >Qu�e lenguaje genera esta gram�atica?
2 Dar una gram�atica equivalente que no presente recursividad a izquierda.
/
2.6 Sintaxis concreta y abstracta
El objetivo de esta secci�on es introducir la sintaxis abstracta, y mostrar c�omo obtener
una sintaxis abstracta a partir de una sintaxis concreta.
Recuerde la gram�atica SequenceOfS para producir secuencias de ss:
Beside : S ! SS
Single : S ! s
Como se explic�o en la secci�on 2.4.1, se puede usar el el siguiente tipo de datos para
representar �arboles de an�alisis de frases del lenguaje de S .
data S = Beside S S
| Single Char
Por ejemplo, la secuencia sss puede ser representada por el �arbol de an�alisis:
Beside (Beside (Single 's') (Single 's')) (Single 's')
La funci�on s2string construye la frase que corresponde a un valor del tipo de datos
S:
s2string :: S -> String
s2string x = case x of
Beside l r -> s2string l ++ s2string r
Single x -> "s"
Como en todo �arbol de an�alisis para una frase del lenguaje de S , Single siempre
ser�a seguido por un caracter 's', no tenemos que incluir el tipo Char en la de�nici�on
del tipo de datos S. Re�namos a continuaci�on el tipo de datos para representar los
�arboles de an�alisis de SequenceOfS :
data SA = BesideA SA SA
| SingleA
Note que ahora ha desaparecido el tipo de datos Char que representa el s��mbolo
terminal. La secuencia sss est�a representada por el �arbol de an�alisis
BesideA (BesideA SingleA SingleA) SingleA
32 Gram�aticas Libres de Contexto
Una sintaxis concreta de un lenguaje describe c�omo son las frases de un lenguaje. As�� sintaxis
con-
creta
la sintaxis concreta del lenguaje de S est�a dada por la gram�atica SequenceOfS. Una
sintaxis abstracta de un lenguaje describe los �arboles de an�alisis de un lenguaje. Lossintaxis
abs-
tracta
�arboles de an�alisis son por lo tanto llamados a veces �arboles de sintaxis abstracta.
El tipo de datos SA es un ejemplo de una sintaxis abstracta para el lenguaje de
SequenceOfS. El adjetivo \abstracto" indica que los valores de la sintaxis abstracta
no necesitan tener toda la informaci�on acerca de frases espec���cas siempre que la
informaci�on sea recuperable. Por ejemplo, la funci�on sa2string toma un valor de
la sintaxis abstracta para SequenceOfS, y retorna la frase representada por el �arbol
de sintaxis abstracta.
sa2string :: SA -> String
sa2string x = case x of
BesideA l r -> sa2string l ++ sa2string r
SingleA -> "s"
Tal funci�on es a menudo llamada funci�on sem�antica. Una funci�on sem�antica esfunci�on
sem�antica una funci�on de�nida sobre una sintaxis abstracta de un lenguaje. Las funciones
sem�anticas son utilizadas para dar sem�antica (signi�cado) a los valores. Aqu��, el
signi�cado de una representaci�on m�as abstracta se expresa en una representaci�on
concreta. Utilizando las transformaciones gramaticales de eliminaci�on de recur-
sividad a izquierda y factorizaci�on a izquierda, la gram�atica SequenceOfS puede
transformarse en una gram�atica con las siguientes producciones:
S ! sS j �
Una sintaxis abstracta de esta gram�atica puede ser
data SA2 = Cons SA2 | Nil
De hecho, la �unica informaci�on importante sobre las secuencias de s es cuantas
ocurrencias de s existen. Por lo tanto sintaxis abstracta �nal para SequenceOfS es
data SA3 = Size Int
La secuencia sss est�a representada por el �arbol de an�alisis Size 3. El ejemplo
SequenceOfS muestra que uno puede elegir entre muchas sintaxis abstractas dife-
rentes para una gram�atica dada. La aplicaci�on determinar�a cu�al sintaxis abstracta
es la m�as conveniente.
Ejercicio 2.20 . Un tipo de datos en Hugs describe un conjunto de�nido inductivamente. El
siguiente tipo de datos representa una forma limitada de expresiones aritm�eticas.
data Expr = Add Expr Expr | Mul Expr Expr | Con Int
D�e una gram�atica que corresponde a este tipo de dato. /
Ejercicio 2.21 . Considere su respuesta al ejercicio 2.7 que describe la sintaxis concreta para
pal��ndromes sobre fa,bg.
1 De�ne un tipo de datos Pal que describa la sintaxis abstracta que corresponde
a su gram�atica. D�e a los dos pal��ndromes abstractos aPal1 y aPal2 sus co-
rrespondientes pal��ndromes concretos cPal1 = 00abaaba00 y cPal2 = 00baaab00.
2.6 Sintaxis concreta y abstracta 33
2 Escribe una funci�on (sem�antica) que transforme una representaci�on abstracta
de un pal��ndrome en una representaci�on concreta. Pruebe la funci�on con los
pal��ndromes abstractos aPal1 y aPal2.
3 Escribe una funci�on que cuente la cantidad de ocurrencias de as en un pal��ndrome.
Pruebe la funci�on con los pal��ndromes abstractos aPal1 y aPal2.
/
Ejercicio 2.22 . Considere su respuesta al ejercicio 2.8 que describe la sintaxis concreta para
pal��ndromes espejo.
1 De�ne un tipo de datos Mir que describe la sintaxis abstracta correspondiente
a su gram�atica. D�e los dos pal��ndromes espejo abstractos aMir1 y aMir2
que corresponden a los pal��ndromes espejo concretos cMir1 = 00abaaba00 and
cMir2 = 00abbbba00.
2 Escribe una funci�on (sem�antica) que transforme una representaci�on abstracta
de un pal��ndrome espejo en una representaci�on concreta. Pruebe la funci�on
con los pal��ndromes espejo aMir1 y aMir2.
3 Escribe una funci�on que transforme una representaci�on abstracta de un pal��ndrome
espejo en la representaci�on abstracta correspondiente de un pal��ndrome. Prue-
be la funci�on con los pal��ndromes espejo abstractos aMir1 y aMir2.
/
Ejercicio 2.23 . Considere su respuesta al ejercicio 2.9, el cual describe la sintaxis concreta
para secuencias de paridad.
1 De�ne tipos de datos que describan la sintaxis abstracta correspondiente a
esa gram�atica. D�e las dos secuencias con paridad abstracta aEven1 y aEven2
que corresponden a las secuencias con paridad concreta cEven1 = 000010100 y
cEven2 = 000101000
2 Escribe una funci�on (sem�antica) que transforme una representaci�on abstracta
de una secuencia de paridad en una representaci�on concreta. Pruebe la funci�on
con las secuencias de paridad abstractas aEven1 y aEven2.
3 Escribe una funci�on sem�antica que transforme una representaci�on abstracta de
una secuencia de paridad en su valor como numero binario. Pruebe la funci�on
con las secuencias de paridad abstracta aEven1 y aEven2.
/
Ejercicio 2.24 . Considere su respuesta al ejercicio 2.18, que describe la sintaxis concreta para
listas de bits en una forma sin recursi�on a izquierda.
1 De�ne un tipo de datos BitList que describa la sintaxis abstracta correspon-
diente a su gram�atica. D�e dos listas de bits abstractas aBitList1 y aBitList2
que corresponden a las listas de bits concretas cBitList1 = 000; 1; 000 y cBitList2 =000; 0; 100.
2 Escribe una funci�on que transforme una representaci�on abstracta de una lista
de bits en una representaci�on concreta. Veri�que la funci�on con las listas de
bits abstractas aBitList1 y aBitList2.
3 Escribe una funci�on que concatene dos representaciones abstractas de listas
de bits en una sola. Veri�que la funci�on con las listas de bits aBitList1 y
aBitList2.
/
34 Gram�aticas Libres de Contexto
2.7 Construcciones sobre Gram�aticas
Esta secci�on introduce algunas construcciones sobre gram�aticas que son �utiles cuan-
do se especi�can gram�aticas m�as grandes, por ejemplo, para lenguajes de progra-
maci�on. Adem�as, encontrar�as un ejemplo de una gram�atica m�as grande que ha sido
trasformada en varios pasos.
La notaci�on BNF, introducida en la secci�on 2.2.1, fue utilizada primero en los comien-
zos de los a~nos sesenta cuando fue de�nido el lenguaje de programaci�on ALGOL 60
y hasta ahora es la forma de de�nir lenguajes de programaci�on. Ver por ejemplo la
gram�atica del lenguaje Java. Usted puede objetar que la gram�atica de Java contiene
m�as detalles sint�acticos adicionales que las gram�aticas que hemos estado conside-
rando (lo mismo se puede a�rmar de la gram�atica de ALGOL 60): uno encuentra
no terminales con post �jos ?, + y *.
Se introduce la notaci�on BNF extendida, EBNF , porque la de�nici�on de un len-EBNF
guaje de programaci�on requiere muchos s��mbolos no terminales y la adici�on de no
terminales (super uos) para construcciones est�andar tales como:
� una o cero ocurrencias del s��mbolo no terminal P (P?),
� una o m�as ocurrencias del s��mbolo no terminal P (P+),
� y cero o m�as ocurrencias del s��mbolo no terminal P (P �),
disminuye la legibilidad de la gram�atica. En otros textos encontrar�a algunas veces
[P ] en lugar de P? , y fPg en lugar de P �. La misma notaci�on puede utilizarse para
lenguajes, gram�aticas y secuencias de s��mbolos terminales y no terminales en lugar
de s��mbolos no terminales individuales. Esta secci�on de�ne el signi�cado de estas
construcciones.
Introducimos gram�aticas como una alternativa para la descripci�on de lenguajes.
Puede que el dise~no de una gram�atica para un lenguaje espec���co no sea una tarea
trivial. Un m�etodo es descomponer el lenguaje y encontrar gram�aticas para cada
una de sus partes constituyentes.
De�nici�on 10: Operaciones sobre lenguajes
Suponga que tenemos gram�aticas para los lenguajes L y M , digamos que GL =
(T;NL; RL; SL) y GM = (T;NM; RM ; SM). Suponemos que los conjuntos NL y NM
son disjuntos. Entonces
� L [ M es generado por la gram�atica (T;N;R; S) entonces S es un nuevo
s��mbolo no terminal,
N = NL [ NM [ fSg y R = RL [ RM [ fS ! SL; S ! SM)
� LM es generado por la gram�atica (T;N;R; S) donde S es un nuevo no terminal,
N = NL [ NM y R = RL [ RM [ fS ! SL SMg
� L� es generado por la gram�atica (T;N;R; S) donde S es un nuevo no terminal,
N = NL [ fSg y R = RL [ fS ! �; S ! SL Sg.
� L+ es generado por la gram�atica (T;N;R;S) donde S es un nuevo no terminal,
N = NL [ fSg y R = RL [ fS ! SL; S ! SL Sg
2
Lo bueno acerca de las de�niciones anteriores es que las operaciones de la teor��a de
conjuntos a nivel de lenguajes (es decir conjuntos de frases) tienen una contraparte
2.7 Construcciones sobre Gram�aticas 35
directa a nivel de la descripci�on gramatical. La pregunta directa ahora es: >Puedo
tambi�en de�nir lenguajes como la diferencia entre dos lenguajes o como la inter-
secci�on de dos lenguajes? Desafortunadamente no existen operadores equivalentes
para componer gram�aticas que correspondan a tales operadores de intersecci�on y
diferencia.
Dos de las construcciones descritas arriba son lo su�cientemente importantes para
de�nirlas tambi�en como operaciones de gram�aticas. Mas a�un, a~nadimos una nueva
construcci�on para la elecci�on.
De�nici�on 11: Operaciones sobre gram�aticas
Sea G = (T;N;R;S) una gram�atica libre de contexto y S 0 un nuevo s��mbolo no
terminal. Entonces
G� = (T; N [ fS0g; R [ fS0! �; S 0 ! SS0g; S0) estrella G
G+ = (T; N [ fS0g; R [ fS0! S; S0! SS0g; S0) m�as G
G? = (T; N [ fS0g; R [ fS0! �; S 0 ! Sg; S0) opcional G
2
La de�nici�on de P?, P+, y P � es muy similar a las de�niciones de las operaciones
sobre gram�aticas.
De�nici�on 12: EBNF para secuencias
Sea P una secuencia de s��mbolos terminales y no terminales entonces
L(P �) = L(Z) with Z ! � j PZ
L(P+) = L(Z) with Z ! P j PZ
L(P?) = L(Z) with Z ! � j P
donde Z es un nuevo no terminal en cada de�nici�on. 2
Como el operador de concatenaci�on de secuencias es asociativo, los operadores + y
� pueden tambi�en de�nirse sim�etricamente:
L(P �) = L(Z) with Z ! � j ZP
L(P+) = L(Z) with Z ! P j ZP
Existen muchas variaciones posibles sobre este tema:
L(P �Q) = L(Z) with Z ! Q j PZ (2.1)
2.7.1 SL: un ejemplo
Para ilustrar EBNF y algunas de las transformaciones de gram�aticas, dadas en la
secci�on previa, damos un ejemplo extenso. La siguiente gram�atica genera expresiones
en un lenguaje de programaci�on muy peque~no, llamado SL.
Expr ! if Expr then Expr else Expr
Expr ! Expr where Decls
Expr ! AppExpr
AppExpr ! AppExpr Atomic j Atomic
36 Gram�aticas Libres de Contexto
Atomic ! Var j Number j Bool j (Expr)
Decls ! Decl
Decls ! Decls ; Decls
Decl ! Var = Expr
Los s��mbolos no terminales Var , Number , y Bool generan variables, expresiones
num�ericas, y expresiones booleanas, respectivamente. Observe que los par�entesis
alrededor de Expr en la producci�on para Atomic, y el punto y coma entre los dos
Decls en la segunda producci�on para Decls son tambi�en s��mbolos terminales. El
siguiente \programa" es una frase de este lenguaje:
if true then funny true else false where funny = 7
Es claro que este no es un lenguaje muy conveniente para escribir programas.
La gram�atica descrita arriba es ambigua (>por qu�e?), e introducimos prioridades
para resolver algunas de las ambiguedades. La aplicaci�on (de funciones) enlaza m�as
fuertemente que if, y ambos, aplicaci�on e if, enlazan m�as fuerte que where. Utili-
zando la introducci�on de prioridades de transformaci�on de gram�atica, obtenemos:
Expr ! Expr1
Expr ! Expr1 where Decls
Expr1 ! Expr2
Expr1 ! if Expr1 then Expr1 else Expr1
Expr2 ! Atomic
Expr2 ! Expr2 Atomic
donde Atomic, y Decls tienen las mismas producciones que antes.
El s��mbolo no terminal Expr2 es recursivo a izquierda. La eliminaci�on de la recur-
sividad a izquierda da las siguientes producciones para Expr2 :
Expr2 ! Atomic j Atomic Z
Z ! Atomic j Atomic Z
Como el nuevo s��mbolo no terminal Z tiene exactamente las mismas producci�ones
que Expr2 , estas producciones pueden ser reemplazadas por
Expr2 ! Atomic j Atomic Expr2
As�� Expr2 genera una secuencia no vac��a de Atomics. Utilizando la notaci�on +,
introducida en esta secci�on, podemos reemplazar Expr2 por Atomic+.
Otra fuente de ambig�uedad son las producciones de Decls. Decls genera una lista no
vac��a de declaraciones, y suponemos que el separador ; es asociativo. Por lo tanto,
podemos aplicar la transformaci�on de operador asociativo para obtener
Decls ! Decl j Decl ; Decls
o equivalentemente a (2.1),
Decls ! (Decl;)� Decl
2.8 An�alisis Sint�actico 37
que usando una regla omitida para ese operador estrella �, se puede transformar en
Decls ! Decl (;Decl)�
La �ultima transformaci�on gramatical que aplicamos es la factorizaci�on a la izquierda.
Esta transformaci�on se aplica a Expr , y da
Expr ! Expr1 Z
Z ! � j where Decls
Como el s��mbolo no terminal Z genera ya sea nada o una cl�ausula-where, podemos
reemplazar Z por una cl�ausula opcional where en la producci�on para Expr .
Expr ! Expr1 (where Decls)?
Despu�es de todas estas transformaciones gramaticales, obtenemos la siguiente gram�atica:
Expr ! Expr1 (where Decls)?
Expr1 ! Atomic+
Expr1 ! if Expr1 then Expr1 else Expr1
Atomic ! Var j Number j Bool j (Expr)
Decls ! Decl (;Decl)�
Ejercicio 2.25 . D�e la notaci�on EBNF para cada uno de los lenguajes b�asicos de�nidos en la
secci�on 2.3.1. /
Ejercicio 2.26 . >Qu�e lenguaje genera G? ? /
Ejercicio 2.27 . En caso de que de�namos lenguajes mediante predicados, es casi trivial de�nir
la intersecci�on y la diferencia de lenguajes. Muestre c�omo. /
Ejercicio 2.28 . Sea L1 = fanbncm j n;m 2 IINg y L2 = fa
nbmcm j n;m 2 IINg.
� D�e gram�aticas para L1 and L2.
� >Es L1\L2 libre de contexto, en otras palabras, puede usted dar una gram�atica
libre de contexto para este lenguaje?
/
2.8 An�alisis Sint�actico
Esta secci�on formula el problema del an�alisis sint�actico, y analiza algunos de los
futuros temas del curso. Ahora, �nalmente hemos alcanzado el punto donde podemos
formular el problema del an�alisis sint�actico.
De�nici�on 13: El problema del an�alisis sint�actico
Dada la gram�atica G y una cadena s el problema del an�alisis sint�actico responde a
la pregunta: si s 2 L(G) o no, la respuesta a esta pregunta puede ser un �arbol de
an�alisis o una derivaci�on. 2
38 Gram�aticas Libres de Contexto
Aunque en base a los problemas presentados hasta ahora, la intuici�on parece indicar
lo contrario, tal pregunta puede no ser tan f�acil de responder dada una gram�atica
arbitraria. Sin embargo, en la primera parte de este curso veremos como dada
una gram�atica con ciertas propiedades razonables, podemos f�acilmente construir
de forma manual analizadores sint�acticos. Al mismo tiempo se mostrar�a como el
proceso de an�alisis puede ser a menudo combinado con el algoritmo que en efecto
queremos ejecutar sobre el objeto reconocido (la funci�on sem�antica). Como tal �esta
provee una simple, aunque sorprendentemente e�ciente, introducci�on al �area de la
construcci�on de compiladores.
Un compilador para un lenguaje de programaci�on consiste de varias partes. Ejemplos
de tales partes son un analizador lexicogr�a�co, un analizador sint�actico, un veri�ca-
dor de tipos, y un generador de c�odigo. Normalmente, un an�alizador sint�actico es
precedido por un analizador lexicogr�a�co, el cual divide la frase de entrada en unaanalizador
lexico-
gr�a�co
lista de s��mbolos (tambi�en llamados tokens). Por ejemplo, dada la frase
if true then funny true else false where funny = 7
Un analizador lexicogr�a�co posiblemente retornar�a la siguiente lista de s��mbolos:
["if","true","then","funny","true","else","false"
,"where","funny","=","7"]
Entonces, un s��mbolo es una entidad sint�actica. Un analizador lexicogr�a�co normal-s��mbolo
mente ejecuta el primer paso hacia la sintaxis abstracta: descarta la informaci�on de
la organizaci�on del texto tal como el espaciado y los caracteres de corte de l��nea. En
este curso nos abocaremos a los analizadores sint�acticos, pero se utilizar�an algunos
de los conceptos de analizadores lexicogr�a�cos de vez en cuando.
En la segunda parte de este curso veremos gram�aticas m�as complicadas, las cuales
no siempre respetan las restricciones que acabamos de mencionar. Analizando la
gram�atica tambi�en podemos generar analizadores sint�acticos. Tales analizadores
generados estar�an de tal forma que ser�a claro que el escribirlos manualmente est�a
lejos de ser atractivo, y realmente imposible por una serie de motivos pr�acticos.
Uno de los problemas que no hemos mencionado todav��a en este cap��tulo bastante
formal es de��ndole m�as pr�actico. A menudo, la frase presentada al analizador no ser�a
una frase del lenguaje puesto que se cometieron errores al escribir la frase. Esto oca-
siona una nueva pregunta: >Cu�ales son los cambios m��nimos que deben hacerse a la
frase para convertirla en una frase del lenguaje? No es necesario mencionar que esta
es una pregunta importante que debe ser resuelta en la pr�actica; no estar��amos muy
contentos con un compilador que, con una entrada err�onea, responder��a solamente
\La entrada no pudo ser reconocida". Ac�a, uno de los aspectos m�as importantes
es el de�nir m�etricas para la decisi�on sobre el minimalidad de un cambio; normal-
mente los humanos cometen ciertos errores algunos m�as que otros: se puede olvidar
f�acilmente un punto y coma, pero la posibilidad de que se olvide el s��mbolo if es
mucho m�as remota. Es aqu�� donde la ingenier��a de la gram�atica comienza a jugar
su papel.
resumen
Comenzando por un ejemplo simple, el lenguaje de los pal��ndromes, hemos intro-
ducido el concepto de una gram�atica libre de contexto. Se introdujeron tambi�en
2.9 Ejercicios 39
conceptos asociados, tales como las derivaciones y los �arboles de an�alisis.
2.9 Ejercicios
Ejercicio 2.29 . >Existen lenguajes L tales como L� = L�
? /
Ejercicio 2.30 . D�e un lenguaje L tal que L = L� /
Ejercicio 2.31 . >Bajo qu�e circunstancias es L+ = L� � f�g? /
Ejercicio 2.32 . Sea L un lenguaje sobre el alfabeto fa; b; cg tal que L = LR. >L contiene solo
pal��ndromes? /
Ejercicio 2.33 . Considere la gram�atica con las siguientes producciones:
S ! AA
A ! AAA
A ! a
A ! bA
A ! Ab
1. >Qu�e cadenas terminales pueden ser producidas por derivaciones de cuatro o
menos pasos?
2. D�e por lo menos cuatro derivaciones distintas para la cadena babbab
3. Para cualquier m;n; p � 0, describe una derivaci�on para la cadena bmab
nab
p.
/
Ejercicio 2.34 . Considere la gram�atica con las siguientes producciones:
S ! aaB
A ! bBb
A ! �
B ! Aa
Muestre que la cadena aabbaabba no puede derivarse a partir de S. /
Ejercicio 2.35 . D�e una gram�atica para el lenguaje
L = f !c!R j ! 2 fa; bg�g
Este lenguaje es conocido como el lenguaje de pal��ndromes con centro marcado. D�e
una derivaci�on de la frase abcba. /
Ejercicio 2.36 . Describe el lenguaje generado por la gram�atica:
S ! �
S ! A
A ! aAb
A ! ab
>Puedes encontrar otra gram�atica (preferentemente m�as simple) para el mismo len-
guaje? /
40 Gram�aticas Libres de Contexto
Ejercicio 2.37 . Describe el lenguaje generado por las gram�aticas:
S ! �
S ! A
A ! Aa
A ! a
y
S ! �
S ! A
A ! AaA
A ! a
>Puede encontrar otra gram�atica (preferentemente m�as simple) para los mismos
lenguajes? /
Ejercicio 2.38 . Muestre que el lenguaje generado por las gram�aticas G1, G2 y G3 es el mismo.
G1 : G2 : G3 :
S ! �
S ! aS
S ! �
S ! Sa
S ! �
S ! a
S ! SS
/
Ejercicio 2.39 . Considere la siguiente propiedad de las gram�aticas:
1. el s��mbolo inicial es el �unico no terminal que puede tener una producci�on vac��a
(una producci�on de la forma X ! �),
2. el s��mbolo inicial no ocurre en cualquier alternativa.
Una gram�atica que tiene estas propiedades se llama no contrayente. La gram�atica
A ! aAb j � no tiene esta propiedad. D�e una gram�atica no contrayente que
describa ese mismo lenguaje. /
Ejercicio 2.40 . Describa el lenguaje L de la gram�atica A ! AaA j a. D�e una gram�atica
para L que no tenga producciones recursivas a izquierda. D�e una gram�atica para L
que no tenga producciones recursivas a derecha. /
Ejercicio 2.41 . Describa el lenguaje L de la gram�atica X ! a j Xb. D�e una gram�atica para
L que no tenga producciones recursivas a izquierda. D�e una gram�atica para L que
no tenga producciones recursivas a izquierda y sea no contrayente. /
Ejercicio 2.42 . Considere el lenguaje L de la gram�atica
S ! T j US
T ! aSa j Ua
U ! S j SUT
D�e una gram�atica paraL que use s�olo alternativas de longitud � 2. D�e una gram�atica
para L que use solo 2 no terminales. /
2.9 Ejercicios 41
Ejercicio 2.43 . D�e una gram�atica para el lenguaje de todas las secuencias de 0s y 1s que
comiencen con 1 y contengan exactamente un 0. /
Ejercicio 2.44 . D�e una gram�atica para el lenguaje que consiste de todas las secuencias no
vac��as de par�entesis:
f ( ; ) g
en la cual todos los par�entesis emparejen. ( ) ( ( ) ) ( ) es una frase del lenguaje, dar
un �arbol de derivaci�on para �esta. /
Ejercicio 2.45 . D�e una gram�atica para el lenguaje que consista de todas las secuencias no
vac��as de par�entesis y corchetes,
f ( ; ) ; [ ; ] g
en la cual los par�entesis y corchetes emparejen. [ ( ) ] ( ) es una frase del lenguaje. /
Ejercicio 2.46 . Este ejercicio muestra un ejemplo (atribuido a Noam Chomsky) de una frase
ambigua en Ingl�es. Considere la siguiente gram�atica como parte del idioma Ingl�es:
Sentence ! Subject Predicate.
Subject ! they
Predicate ! Verb NounPhrase
Predicate ! AuxVerb Verb Noun
Verb ! are
Verb ! ying
AuxVerb ! are
NounPhrase ! Adjective Noun
Adjective ! ying
Noun ! planes
D�e dos diferentes derivaciones m�as a la izquierda para la frase
they are ying planes.
/
Ejercicio 2.47 . Trate de encontrar algunas frases ambiguas en Espa~nol. Aqu�� hay algunas
frases ambiguas en holand�es publicadas en los peri�odicos:
Vliegen met hartafwijking niet gevaarlijk
Jessye Norman kan niet zingen
Alcohol is voor vrouwen schadelijker dan mannen
/
Ejercicio 2.48 . � >Es su gram�atica del ejercicio 2.45 no ambigua? Si no, encuentre una que
no sea ambigua. /
42 Gram�aticas Libres de Contexto
Ejercicio 2.49 . Este ejercicio trata de una gram�atica que utiliza s��mbolos terminales y no
terminales inusuales.
� ! �4
� !
! 3�
! �
� ! |
� ! �
encuentre una derivaci�on para la frase |3|4�. /
Ejercicio 2.50 . � Demuestre, usando inducci�on, que la gram�atica G para pal��ndromes de la
secci�on 2.2 genera realmente el lenguaje de pal��ndromes. /
Ejercicio 2.51 . �� Demuestre que el lenguaje generado por la gram�atica del ejercicio 2.33
contiene todas las cadenas sobre fa; bg con una cantidad par de as que sea mayor a
cero. /
Ejercicio 2.52 . Considere los n�umeros naturales en notaci�on unaria donde s�olo se utiliza el
s��mbolo I; as�� 4 se representa como IIII. Escribe el algoritmo que, dada una cadena
w de Is, determine si w es divisible o no por 7. /
Ejercicio 2.53 . Considere los n�umeros naturales en notaci�on binaria inversa; as�� 4 es repre-
sentado como 001. Escribe un algoritmo que, dada una cadena w de ceros y unos,
determine si w es divisible o no por 7. /
Ejercicio 2.54 . Sea w una cadena que consiste solamente de as y bs. Escribe un algoritmo que
determine si la cantidad de as en w es igual o no a la cantidad de bs en w. /
Cap��tulo 3
Combinadores para el an�alisis
sint�actico
introducci�on
Este cap��tulo es una introducci�on informal al desarrollo de analizadores sint�acticos
en un lenguaje funcional perezoso usando los `combinadores de an�alisis sint�actico'.
Los analizadores sint�acticos pueden escribirse usando un peque~no conjunto de fun-
ciones b�asicas de an�alisis sint�actico, y varias funciones que combinan analizadores
sint�acticos produciendo analizadores m�as complicados. Las funciones que combinan
analizadores sint�acticos se llaman combinadores de analizadores sint�acticos. Las fun-
ciones b�asicas de an�alisis sint�actico no combinan analizadores, y por consiguiente no
son combinadores en este sentido, pero normalmente se llaman tambi�en combinado-
res de analizadores sint�acticos.
Los combinadores de an�alisis sint�actico se usan para escribir analizadores que son
muy similares a la gram�atica de un lenguaje. As��, programar un analizador sint�actico
se reduce a traducir una gram�atica a un programa funcional, que a menudo es una
tarea f�acil.
Los combinadores de an�alisis sint�actico se construyen con elementos est�andar del
lenguaje funcional como funciones de alto orden, listas, y tipos de datos. Se usan
listas de comprensi�on en algunos lugares, pero no son esenciales, y podr��an reescri-
birse f�acilmente usando las funciones map, filter y concat. Las clases de tipo s�olo
se usan para la sobrecarga de los operadores de igualdad y aritm�eticos.
Empezaremos motivando la de�nici�on del tipo de las funciones de an'alisis sint�actico.
Usando ese tipo, podemos construir analizadores sint�acticos para el lenguaje de
gram�aticas (posiblemente ambiguas). Luego, introduciremos algunos analizadores
sint�acticos elementales que pueden usarse para el an�alisis de los s��mbolos terminales
de un lenguaje.
En la secci�on 3.3 se introducen los primeros combinadores de analizadores sint�acticos,
los cuales pueden usarse para combinar analizadores sint�acticos secuencial y alterna-
tivamente, y para calcular con las llamadas funciones sem�anticas durante el an�alisis.
Se usan funciones sem�anticas para dar signi�cado a las estructuras sint�acticas. Co-
mo ejemplo, construimos un analizador sint�actico para cadenas de emparejamiento
de par�entesis en la secci�on 3.3.1. Se calculan distintos valores sem�anticos para el
emparejamiento de par�entesis: un �arbol que describe la estructura, y un entero que
indica la profundidad del anidamiento.
43
44 Combinadores para el an�alisis sint�actico
En la secci�on 3.4 introducimos algunos combinadores de an�alisis sint�actico nuevos.
Estos no s�olo simpli�can el trabajo posterior, sino que sus de�niciones tambi�en son
buenos ejemplos del uso de los combinadores de an�alisis sint�actico. En la secci�on
3.5 se muestra una aplicaci�on real, donde se desarrolla un analizador sint�actico para
expresiones. Finalmente, este analizador sint�actico para expresiones se generaliza
a expresiones con un cantidad arbitraria de niveles de precedencia. Esto se hace
sin codi�car las prioridades de los operadores como enteros, evitando as�� el uso de
��ndices y puntos suspensivos.
No siempre es posible construir directamente un combinador de analizadores sint�acticos
para una gram�atica libre de contexto. Si la gram�atica es recursiva a izquierda, tiene
que ser transformada en una gram�atica no recursiva a izquierda antes de que po-
damos construir un combinador de analizadores sint�acticos. Otra limitaci�on de la
t�ecnica de construir combinadores de an�alisis sint�actico es que no es trivial escribir
analizadores sint�acticos para gram�aticas complejas que se desempe~nen e�cazmen-
te. Sin embargo, existen un buenos combinadores de an�alisis sint�actico para, por
ejemplo, Haskell [11, 12].
La mayor��a de las t�ecnicas introducidas en este cap��tulo han sido descritas por Burge
[4], Wadler [13] y Hutton [7]. Recientemente, se ha popularizado bastante el uso de
las llamadas m�onadas en conjunci�on con los combinadores de an�alisis sint�actico
[14, 8]. Sin embargo nosotros no las usaremos en este art��culo para mostrar que
no hay magia en el uso de combinadores de an�alisis sint�actico. Sin embargo los
alentamos a estudiar m�onadas en alg�un momento, porque forman una generalizaci�on
�util de las t�ecnicas descritas aqu��. Este cap��tulo es una versi�on revisada de [5].
objetivos
Este cap��tulo introduce los primeros programas para el an�alisis sint�actico en este
texto. Los analizadores sint�acticos est�an compuestos por analizadores sint�acticos
simples usando los combinadores de an�alisis sint�actico. Por tanto, los objetivos
principales de este cap��tulo son:
� Entender c�omo hacer el an�alisis sint�actico, es decir, c�omo reconocer estructura
en una secuencia de s��mbolos, por medio de combinadores de an�alisis sint�actico;
� Dada una gram�atica, ser capaz de construir un analizador sint�actico;
� Entender el concepto de funciones sem�anticas.
Los objetivos secundarios de este cap��tulo son:
� Desarrollar la capacidad de abstracci�on;
� Entender el concepto de lenguajes de dominio espec���co.
conocimiento previo requerido
Para entender este cap��tulo, debes poder formular el problema del an�alisis, y debes
entender el concepto de gram�aticas libres de contexto. Adem�as, debes estar familia-
rizado con conceptos de programaci�on funcional, tales como tipos, clases y funciones
de alto orden.
3.1 El tipo Parser 45
3.1 El tipo Parser
Los objetivos de esta secci�on son:
� Desarrollar un tipo Parser que se usa para dar un tipo a las funciones de
an�alisis sint�actico;
� Mostrar c�omo obtener este tipo por medio de varios pasos de abstracci�on.
El problema del an�alisis es (ver en la secci�on 2.8): Dada una gram�atica G y una
cadena s, determinar si s 2 L(G) o no. Si s 2 L(G) la respuesta a esta pregunta
puede ser un �arbol de an�alisis o una derivaci�on. Por ejemplo, en la secci�on 2.4 hemos
visto una gram�atica para secuencias de ss:
S ! SS j s
Un �arbol de an�alisis de una expresi�on es un valor del tipo de datos SA, que est�a
de�nido por
data SA = BesideA SA SA | SingleA
Un analizador sint�actico para expresiones puede implementarse como una funci�on
del siguiente tipo:
type Parser = String -> SA
Para analizar subestructuras, un analizador sint�actico puede llamar a otros anali-
zadores, o llamarse recursivamente. Estas llamadas no s�olo necesitan comunicar su
resultado, sino tambi�en la parte de la cadena de entrada que queda sin procesar.
Por ejemplo, cuando analizamos la cadena "sss", un analizador sint�actico prime-
ro construir�a �arbol de an�alisis para BesideA SingleA SingleA para "ss", y s�olo
entonces construir�a el �arbol de an�alisis completo:
BesideA (BesideA SingleA SingleA) SingleA
usando la parte no procesada "s" de la cadena de entrada. Como esto no se puede
hacer usando una variable global, la cadena de entrada sin procesar tiene que formar
parte del resultado del analizador sint�actico. Los dos resultados se pueden agrupar
en un tupla. Una mejor de�nici�on para el tipo Parser es:
type Parser = String -> (SA,String)
Cualquier analizador sint�actico del tipo Parser devuelve un SA y un String. Sin
embargo, para distintas gram�aticas queremos retornar distintos �arboles de an�alisis:
el tipo del �arbol que se devuelve depende de la gram�atica para la cual queremos
reconocer frases. Por consiguiente, es mejor abstraer del tipo SA, y convertir el tipo
Parser en un tipo polimorfo. El tipo Parser se parametriza con un tipo a, que
representa el tipo de �arboles de an�alisis.
type Parser a = String -> (a,String)
Por ejemplo, un analizador sint�actico que devuelva la estructura de tipo Oak (cual-
quiera que sea) ahora tiene el tipo Parser Oak. Un analizador sint�actico que analiza
46 Combinadores para el an�alisis sint�actico
secuencias de ss tiene el tipo Parser SA. Podr��amos tambi�en de�nir un analizador
que no retorne un valor del tipo SA, en cambio retorne la cantidad de eses de la
secuencia de entrada. Este analizador sint�actico tendr��a el tipo Parser Int. Otra
instancia de un analizador sint�actico es una funci�on de an�alisis que reconoce una
cadena de d��gitos, y devuelve el n�umero que representa como `�arbol de an�alisis'. En
este caso, la funci�on es tambi�en de tipo Parser Int. Finalmente, un reconocedor
que acepta o rechace frases de una gram�atica devuelve un valor booleano, y tendr�a
el tipo Parser Bool.
Hasta ahora, hemos supuesto que toda cadena puede ser analizada de una manera
�unica. En general, esto no es necesariamente as��: puede ser que una �unica cadena
pueda analizarse de varias formas, o que no exista una forma de analizar la cadena.
Por ejemplo, la cadena "sss" tiene los dos siguientes �arboles de derivaci�on:
BesideA (BesideA SingleA SingleA) SingleA
BesideA SingleA (BesideA SingleA SingleA)
Otro re�namiento de la de�nici�on del tipo es que en lugar de retornar un �arbol
de an�alisis (y su cadena restante asociada), podemos retornar una lista de �arboles.
Cada elemento del resultado consiste en un �arbol, emparejado con el resto de la
cadena que no fue procesada despu�es del an�alisis. Por consiguiente, la de�nici�on del
tipo de Parser es ahora:
type Parser a = String -> [(a,String)]
Si solamente existe un an�alisis, el resultado de la funci�on an�alisis ser�a una lista
unitaria. Si el an�alisis no es posible, el resultado ser�a una lista vac��a. En el caso de
una gram�atica ambigua, el resultado consiste en todos los posibles an�alisis.
Este m�etodo, descrito por Wadler [13], se llama lista de �exitos . Puede usarse enlista de
�exitos situaciones donde en otros lenguajes usar��an las llamadas t�ecnicas de rastreo con
retroceso.1 En el texto de Bird y Wadler se usa para resolver problemas combina-
torios como el problema de las ocho reinas [3]. Si se requiere s�olo una soluci�on en
lugar de todas las posibles soluciones, se puede tomar el head de la lista de �exitos.
Si s�olo se necesita el primer valor de la lista, gracias a la evaluaci�on perezosa no seevaluaci�on
pere-
zosa
calculan todos los elementos y entonces no habr�a p�erdida de e�ciencia. La evalua-
ci�on perezosa provee un m�etodo de rastreo con retroceso para encontrar la primera
soluci�on.
Los analizadores sint�acticos con el tipo descrito hasta ahora operan sobre cadenas,
es decir, listas de caracteres. Sin embargo, no existe raz�on para no permitir el
an�alisis de cadenas de elementos que sean distintos a caracteres. Se puede imaginar
una situaci�on en la que un preprocesador prepare una lista de s��mbolos (vea la
secci�on 2.8), la que se analiza posteriormente. Para tratar esta situaci�on, re�namos
el tipo Parser una vez m�as: dejamos que el tipo de los elementos de la cadena de
entrada sea un argumento del tipo del analizador sint�actico. Llam�andolo b, y como
antes el tipo del resultado a, el tipo de los Parser ahora est�a de�nido como:
type Parser b a = [b] -> [(a,[b])]
O si preferimos identi�cadores signi�cativos sobre la concisi�on:
1NT: en ingl�es \backtracking".
3.2 Analizadores sint�acticos Elementales 47
-- The type of parsers
type Parser symbol result = [symbol] -> [(result,[symbol])]
Listado 1: ParserType.hs
type Parser symbol result = [symbol] -> [(result,[symbol])]
Usaremos esta de�nici�on de tipo en el resto de este cap��tulo. Este tipo est�a de�-
nido en listado 1, la primera parte de nuestra librer��a de combinadores de an�alisis
sint�actico.
3.2 Analizadores sint�acticos Elementales
Los objetivos de esta secci�on son:
� Introducir algunos analizadores sint�acticos muy simples para realizar el an�alisis
de frases de gram�aticas con reglas de la forma:
A ! �
A ! a
A ! x
donde x es una secuencia de terminales;
� Mostrar como uno puede construir funciones �utiles a partir de funciones sim-
ples, trivialmente correctas por medio de la generalizaci�on y la parametrizaci�on
parcial.
Esta secci�on de�ne analizadores sint�acticos que solamente pueden usarse para anali-
zar secuencias �jas de s��mbolos terminales. Para una gram�atica con una producci�on
que contiene no terminales en su lado derecho necesitamos t�ecnicas que se introdu-
cir�an en la siguiente secci�on.
Empezaremos con una simple funci�on de an�alisis que s�olo reconoce el s��mbolo ter-
minal a. En este caso el tipo de los s��mbolos de la cadena de entrada es Char, y
como `�arbol de an�alisis' tambi�en usamos simplemente un Char:
symbola :: Parser Char Char
symbola [] = []
symbola (x:xs) | x == 'a' = [('a',xs)]
| otherwise = []
El m�etodo de lista de �exitos retribuye inmediatamente, porque ahora podemos de-
volver una lista vac��a si no es posible realizar el an�alisis (porque la entrada es vac��a,
o no empieza con una a).
De la misma forma, podemos escribir analizadores sint�acticos que reconozcan otros
s��mbolos. Como siempre, en lugar de construir muchas funciones estrechamente
48 Combinadores para el an�alisis sint�actico
-- Elementary parsers
symbol :: Eq s => s -> Parser s s
symbol a [] = []
symbol a (x:xs) | x == a = [(x,xs)]
| otherwise = []
satisfy :: (s -> Bool) -> Parser s s
satisfy p [] = []
satisfy p (x:xs) | p x = [(x,xs)]
| otherwise = []
token :: Eq s => [s] -> Parser s [s]
token k xs | k == take n xs = [(k,drop n xs)]
| otherwise = []
where n = length k
failp :: Parser s a
failp xs = []
succeed :: a -> Parser s a
succeed r xs = [(r,xs)]
-- Applications of elementary parsers
digit :: Parser Char Char
digit = satisfy isDigit
Listado 2: ParserType.hs
relacionadas, es mejor abstraer del s��mbolo a ser reconocido haci�endolo un par�ametro
extra de la funci�on. Adem�as, la funci�on puede operar sobre listas de caracteres, pero
tambi�en sobre listas de s��mbolos de otros tipos, de tal forma que se pueda usar en
otras aplicaciones adem�as de las orientadas al caracter. El �unico pre-requisito es que
la operaci�on de igualdad est�e de�nida para los s��mbolos analizados. En Hugs, esto
se indica con el predicado Eq en el tipo de la funci�on.
Usando estas generalizaciones, obtenemos la funci�on symbol que aparece en el lista-
do 2.
La funci�on symbol es una funci�on que, dado un s��mbolo, devuelve un analizador
sint�actico para ese s��mbolo. Adem�as de ser un analizador sint�actico es tambi�en una
funci�on. Por eso aparecen dos argumentos en la de�nici�on de symbol.
Ahora de�niremos algunos analizadores sint�acticos elementales que tradicionalmente
se hace en los analizadores lexicogr�a�cos (vea Secci�on 2.8). Por ejemplo, un anali-
zador sint�actico �util es aquel que reconoce una cadena �ja de s��mbolos, tales como
while o switch. Llamaremos a esta funci�on token, de�nida en el listado 2. Como en
el caso de la funci�on symbol hemos parametrizado esta funci�on con la cadena a ser
3.2 Analizadores sint�acticos Elementales 49
reconocida, convirti�endola efectivamente en una familia de funciones. Por supuesto,
esta funci�on no est�a restringida a cadenas de caracteres. Sin embargo, necesitamos
un test de igualdad en el tipo de valores en la cadena de entrada, el tipo de token
es:
token :: Eq s => [s] -> Parser s [s]
La funci�on token es una generalizaci�on de la funci�on symbol, en la que se reconoce
una lista de s��mbolos en lugar de un �unico s��mbolo. Observe que no podemos de�nir
symbol en t�erminos de token: las dos funciones tienen tipos incompatibles.
Otra generalizaci�on de symbol es una funci�on que puede, dependiendo de la entrada,
retornar diferentes resultados del an�alisis. En lugar de indicar un s��mbolo espec���co,
podemos parametrizar la funci�on con una condici�on que el s��mbolo debe cumplir.
As��, la funci�on satisfy tiene una funci�on s -> Bool como argumento. Mientras
symbol examina la igualdad de un valor espec���co, la funci�on satisfy prueba la
conformidad con un predicado. �Esta se de�ne en el listado 2. Esta funci�on generali-
zada es �util por ejemplo cuando queremos analizar d��gitos (caracteres entre '0' and
'9'):
digit :: Parser Char Char
digit = satisfy isDigit
donde la funci�on isDigit es el predicado est�andar que veri�ca si el caracter es o no
un d��gito:
isDigit :: Char -> Bool
isDigit x = '0' <= x && x <= '9'
En los libros sobre teor��a de gram�aticas una cadena vac��a se llama a menudo `epsilon'.
Siguiendo la tradici�on de�niremos una funci�on epsilon que `analiza' la cadena vac��a.
No consume ninguna entrada, y por lo tanto retorna siempre un �arbol de an�alisis
sint�actico vac��o y una entrada sin modi�car. Una tupla nula puede usarse como un
valor resultante: (), que es el �unico valor del tipo ().
epsilon :: Parser s ()
epsilon xs = [((),xs)]
Una variante m�as �util es la funci�on succeed, que tampoco consume la entrada,
pero siempre devuelve un valor �jo (o `�arbol de an�alisis', si se puede llamar �arbol
de an�alisis al resultado de procesar cero s��mbolos). Si de�nici�on se encuentra en el
listado 2.
El dual de la funci�on succeed es la funci�on fail, que falla al reconocer cualquier
s��mbolo de la cadena de entrada. Como la lista resultante de un analizador sint�actico
es una `lista de �exitos', y en caso de error no hay �exitos, la lista resultante deber��a
ser vac��a. Por consiguiente la funci�on fail siempre retorna una lista de �exitos vac��a.
La funci�on fail se de�ne en el listado 2. Note la diferencia con epsilon, que tiene
un elemento en su lista de �exitos (aunque es el vac��o).
No confundas fail con epsilon: hay una diferencia importante entre retornar una
soluci�on (que contiene la entrada sin cambiar como cadena `restante') y no retornar
una soluci�on.
Ejercicio 3.1 .De�ne una funci�on capital :: Parser Char Char que analiza letras may�usculas.
/
50 Combinadores para el an�alisis sint�actico
Ejercicio 3.2 . As�� como satisfy es una generalizaci�on de symbol, la funci�on symbol se puede
de�nir como instancia de satisfy. >C�omo se puede hacer esto? /
Ejercicio 3.3 . De�na la funci�on epsilon usando succeed. /
3.3 Combinadores de an�alisis sint�actico
Usando los analizadores sint�acticos elementales de la secci�on anterior, se pueden
construir analizadores sint�acticos para los s��mbolos terminales de una gram�atica.
M�as interesantes son los analizadores sint�acticos para s��mbolos no terminales. Es
conveniente construir estos analizadores sint�acticos parametrizando parcialmente
funciones de alto orden.
Los objetivos de esta secci�on son:
� Mostrar c�omo se pueden construir directamente analizadores sint�acticos para
las producciones de una gram�atica. El tipo de producciones para las que se
construir�an analizadores sint�acticos es:
A ! x j y
A ! x y
donde x, y 2 (N [ T )�;
� Mostrar como podemos construir un peque~no y poderoso lenguaje de combi-
nadores (lenguaje de dominio espec���co) para el prop�osito del an�alisis;
� Entender y usar el concepto de funci�on sem�antica en los analizadores sint�acticos.
Demos nuevamente una mirada a la gram�atica para expresiones, ver tambi�en sec-
ci�on 2.5:
E ! T+E j T
T ! F*T j F
F ! Digs j (E)
donde Digs es un no terminal que genera el lenguaje de secuencias de d��gitos, vea
secci�on 2.3.1. Una expresi�on puede ser analizada de acuerdo a cualquiera de las dos
reglas para E. Esto implica que queremos tener una forma de decir que un anali-
zador sint�actico consiste en varios analizadores sint�acticos alternativos. Adem�as, la
primera regla dice que para analizar una expresi�on, primero deber��amos analizar un
t�ermino, luego un s��mbolo + y luego una expresi�on. Esto implica que queremos te-
ner una manera de decir que un analizador sint�actico consiste en varios analizadores
sint�acticos que se aplican secuencialmente.
De esta manera, las operaciones importantes sobre analizadores sint�acticos son com-
posici�on alternativa y secuencial: un elemento m�as complejo, puede consistir de un
elemento sencillo seguido de otro elemento (composici�on secuencial), o por la elec-
ci�on entre dos elementos (composici�on alternativa). Estas operaciones correspon-
den directamente a sus contrapartes gramaticales. Desarrollaremos dos funciones
para esto, que por conveniencia notacional se de�nen como operadores: <*> para
3.3 Combinadores de an�alisis sint�actico 51
composici�on secuencial, y <|> para composici�on alternativa. Los nombres de estos
operadores se eligieron de tal forma que se recuerden f�acilmente: <*> `multiplica'
dos constructores y <|> puede pronunciarse como `o'. Sin embargo tenga cuidado
de no confundir el operador <|> con el constructor prede�nido de Hugs |, que se
usa para distinguir casos en la de�nici�on de una funci�on.
Para estos operadores, see de�nen prioridades minimizando as�� el uso de par�entesis
en situaciones pr�acticas:
infixl 6 <*>
infixr 4 <|>
Ambos operadores toman dos analizadores sint�acticos como argumento, y devuelven
un analizador sint�actico como resultado. Combinando nuevamente este resultado
con otros analizadores sint�acticos, se podr��a construir analizadores sint�acticos m�as
complicados.
En las de�niciones del listado 3, las funciones operan sobre analizadores sint�acticos
p y q. Adem�as de los argumentos p y q, la funci�on opera sobre una cadena que puede
pensarse como la cadena que es analizada por el analizador sint�actico resultado de
la combinaci�on de p y q.
Empezamos con la de�nici�on del operador <*>. Para la composici�on secuencial,
primero se debe aplicar p a la entrada. Luego, q se aplica a la cadena del resto
del resultado. El primer analizador sint�actico, p, devuelve una lista de �exitos, cada
elemento contiene un valor y una cadena restante. El segundo analizador sint�actico
q, debe aplicarse al resto de la cadena retornando un segundo valor. Por consiguien-
te, usamos una comprensi�on de listas, en la cual se aplica el segundo analizador
sint�actico de todas las formas posibles al resto de la cadena del primer analizador
sint�actico:
(p <*> q) xs = [ (combine r1 r2, zs)
| (r1,ys) <- p xs
, (r2,zs) <- q ys
]
La cadena restante del analizador sint�actico para la composici�on secuencial de p y
q es lo que el segundo analizador sint�actico q devuelve como cadena restante.
Ahora, >c�omo deber��an combinarse los resultados de los dos an�alisis? Por supues-
to, podr��amos parametrizar todo con un operador que describe c�omo combinar las
partes (como se hace en la funci�on zipWith). Sin embargo, hemos elegido un en-
foque diferente que explota muy bien la habilidad de los lenguajes funcionales para
manipular funciones. La funci�on combine debe combinar los resultados de los dos
�arboles de an�alisis reconocidos por p y q. Anteriormente, hemos interpretado la
palabra `�arbol' liberalmente: los valores simples, como caracteres, tambi�en pueden
ser usados como `�arboles de an�alisis'. Aceptaremos tambi�en funciones como �arboles
de an�alisis. Es decir, el tipo de resultado de un analizador sint�actico puede ser el
tipo de una funci�on.
Si el primer analizador sint�actico que se combina con <*> devolviera una funci�on
de tipo b -> a, y el segundo analizador sint�actico un valor de tipo b, la elecci�on
directa para la funci�on combine ser��a la aplicaci�on de funci�on. �Este es exactamente
52 Combinadores para el an�alisis sint�actico
-- Parser combinators
(<|>) :: Parser s a -> Parser s a -> Parser s a
(p <|> q) xs = p xs ++ q xs
(<*>) :: Parser s (b -> a) -> Parser s b -> Parser s a
(p <*> q) xs = [(f x,zs)
|(f ,ys) <- p xs
,( x,zs) <- q ys
]
(<$>) :: (a -> b) -> Parser s a -> Parser s b
(f <$> p) xs = [(f y,ys)
|( y,ys) <- p xs
]
-- Applications of parser combinators
newdigit :: Parser Char Int
newdigit = f <$> digit
where f c = ord c - ord '0'
Listado 3: ParserCombinators.hs
3.3 Combinadores de an�alisis sint�actico 53
el enfoque que se usa en la de�nici�on de <*> en el listado 3. El primer analiza-
dor sint�actico devuelve una funci�on, el segundo analizador sint�actico un valor, y la
combinaci�on del analizador sint�actico devuelve el valor que se obtiene aplicando la
funci�on al valor.
Adem�as de la `composici�on secuencial necesitamos un combinador de analizadores
sint�acticos para representar una `elecci�on'. Para esto, tenemos el operador del combi-
nador de analizadores <|>. Gracias al m�etodo de la lista de �exitos, p1 y p2 devuelven
listas de los posibles an�alisis. Para obtener todos los an�alisis cuando aplicamos p1
y p2, s�olo necesitamos concatenar estas dos listas.
La combinaci�on de analizadores sint�acticos usando los combinadores de an�alisis per-
mite construir nuevos analizadores sint�acticos. Los combinadores m�as importantes
de an�alisis son <*> y <|>. El combinador de analizadores sint�acticos <,> del ejercicio
que se encuentra m�as adelante es s�olo una variaci�on de <*>.
Algunas veces, no estamos totalmente satisfechos con el valor resultante de un ana-
lizador sint�actico. El analizador puede trabajar bien en el sentido de que consume
adecuadamente los s��mbolos de la entrada (dejando los s��mbolos no utilizados como
cadena restante en las tuplas en la lista de �exitos), pero el valor resultante puede
necesitar alg�un proceso posterior. Por ejemplo, un analizador sint�actico que reco-
noce un d��gito se de�ne usando la funci�on satisfy: digit = satisfy isDigit.
En algunas aplicaciones, podemos necesitar que un analizador sint�actico reconozca
caracteres de un d��gito, pero devuelva el resultado como un entero, en lugar de un
caracter, En este tipo de casos podemos usar un nuevo combinador de analizadores:
<$>. �Este toma una funci�on y un analizador sint�actico como par�ametro y da como
resultado un analizador sint�actico que reconoce la misma cadena que el analizador
sint�actico original pero procesa posteriormente el resultado usando la funci�on. La
de�nici�on de <$> se da en el listado 3. Es un operador entre�jo:
infixl 7 <$>
Usando este combinador de analizadores sint�acticos de procesamiento posterior, po-
demos modi�car el analizador sint�actico digit que se de�ni�o anteriormente:
newdigit :: Parser Char Int
newdigit = f <$> digit
where f c = ord c - ord '0'
La funci�on auxiliar f determina el n�umero ordinal de un car�acter d��gito, usando el
combinador de analizadores sint�acticos <$> que se aplica a la parte del resultado del
analizador digit.
En la pr�actica, el operador <$> se usa para construir un determinado valor durante
el an�alisis (en caso de analizar un programa de computadora este valor puede ser el
c�odigo generado, o una lista de todas las variables con sus tipos, etc.). En general:
usando <$> podemos adicionar funciones sem�anticas a los analizadores sint�acticos .
Un analizador sint�actico para la gram�atica SequenceOfS que retorna el �arbol de
sintaxis abstracta de la entrada, es decir un valor del tipo SA, vea secci�on 2.6, se
de�ne como sigue:
sequenceOfS :: Parser Char SA
sequenceOfS = BesideA <$> sequenceOfS <*> sequenceOfS
<|> const SingleA <$> symbol 's'
54 Combinadores para el an�alisis sint�actico
<Pero si tratas de ejecutar esta funci�on obtendr�as un desbordamiento de pila! Si apli-
cas sequenceOfS a una cadena, la primera cosa que hace es llamarse a s�� misma con
la misma cadena que . . . . El problema es que la gram�atica subyacente es recursiva
a la izquierda y no puedes usar combinadores de analizador sint�actico para analizar
frases producidas por gram�aticas recursivas a la izquierda. En la secci�on 2.5 hemos
mostrado como eliminar la recursi�on a la izquierda en la gram�atica SequenceOfS.
Se ha usado tal gram�atica resultante para obtener el siguiente analizador sint�actico:
sequenceOfS' :: Parser Char SA
sequenceOfS' =
BesideA <$> (const SingleA <$> symbol 's') <*> parseZ
<|> const SingleA <$> symbol 's'
where parseZ = BesideA <$> sequenceOfS' <*> parseZ
<|> sequenceOfS'
Ejercicio 3.4 . Demostrar que para todo f :: a -> b
f <$> succeed a = succeed (f a)
A continuaci�on usaremos frecuentemente esta regla para las funciones constantes f,
es decir f = na -> c donde c es un valor que no contiene a a. /
Ejercicio 3.5 . Considerar el analizador sint�actico list <$> symbol 0a0, donde list a as = a:as.
Dar su tipo y muestre sus resultados con entradas [] y x:xs. /
Ejercicio 3.6 . Considere el analizador sint�actico list <$> symbol 0a0 <*> p. D�e el tipo y
muestre sus resultados con las entradas [] and x:xs. /
Ejercicio 3.7 . De�na un analizador sint�actico para booleanos. /
Ejercicio 3.8 . De�na analizadores sint�acticos para cada lenguaje b�asico de�nido en la secci�on
2.3.1. /
Ejercicio 3.9 . Considere la gram�atica para pal��ndromes que construy�o en el ejercicio 2.7.
1. D�e el tipo de datos PAL2 que corresponde a esta gram�atica.
2. De�na un analizador sint�actico palin2 que devuelva �arboles de an�alisis para
pal��ndromes. Pruebe su funci�on con los pal�indromes cPal1 = 00abaaba00 y
cPal2 = 00abaaba00. Compare estos resultados con su respuesta al ejercicio
2.16.
3. De�na un analizador sint�actico palina que cuente el n�umero de as que ocurren
en un pal��ndrome.
/
Ejercicio 3.10 . Considere la gram�atica para una parte del idioma ingl�es que se d�a en el ejercicio
2.46.
1. D�e el tipo de datos English que corresponde a esta gram�atica.
2. De�na un analizador sint�actico english que devuelva �arboles de an�alisis para
el lenguaje English. Veri�que el funcionamiento con la frase they are flying planes.
Compare este resultado con su respuesta en el ejercicio 2.46.
/
3.3 Combinadores de an�alisis sint�actico 55
Ejercicio 3.11 . Cuando de�nimos la prioridad del operador <|>, usando la palabra clave
infixr tambi�en especi�camos que el operador se asocia a la derecha. >Por qu�e
esta es una mejor elecci�on que la asociaci�on a la izquierda? /
Ejercicio 3.12 . De�na un combinador de analizadores sint�acticos <,> que combine dos ana-
lizadores. El valor devuelto por el analizador sint�actico combinado deber��a ser un
par que contenga los resultados de ambos componentes. >Cu�al es el tipo de este
combinador? /
Ejercicio 3.13 . El t�ermino `combinador de analizadores sint�acticos' de hecho no es una des-
cripci�on adecuada para <$>. >Puede pensar en una mejor palabra? /
Ejercicio 3.14 . Compare el tipo de <$> con el tipo de la funci�on est�andar map. >Puede describir
sus observaciones en una frase pegadiza y f�acil de recordar? /
Ejercicio 3.15 . De�na <*> en t�erminos de <,> y <$>. M�as dif��cil, de�na <,> en t�erminos de
<*> y <$>. /
Ejercicio 3.16 . Si examina las de�niciones de <*> y <$> en el listado 3, puede observar que
<$> es en esencia un caso especial de <*>. >Puede de�nir <$> en t�erminos de <*>?
/
3.3.1 Ejemplo: emparejamiento de par�entesis
Usar combinadores para construir un analizador para el cual se tiene una gram�atica
es a menudo bastante directo. Por ejemplo, considere la gram�atica que escribi�o en
el ejercicio 2.44:
S ! ( S ) S j �
Esta gram�atica puede traducirse directamente a un analizador sint�actico, usando
los combinadores de an�alisis sint�actico <*> y <|>. Usamos <*> cuando los s��mbolos
est�an pr�oximos el uno del otro, y <|> cuando en una producci�on aparece j (o cuando
hay m�as de una producci�on para un no terminal)
parens :: Parser Char ???
parens = symbol '(' <*> parens <*> symbol ')' <*> parens
<|> epsilon
Sin embargo, esta funci�on no est�a escrita correctamente: los analizadores sint�acticos
en la primera alternativa no pueden componerse usando <*>, como por ejemplo
symbol 0(0 no es un analizador sint�actico que devuelve una funci�on.
Pero podemos procesar posteriormente el analizador symbol 0(0 para que este ana-
lizador sint�actico s�� devuelva una funci�on en lugar de un caracter. Entonces, >qu�e
funci�on usar��amos? Esto depende del tipo de valor que queremos como resultado del
analizador sint�actico. Un buen resultado ser��a una descripci�on en forma de �arbol
de los par�entesis que se analizan. Para este prop�osito introducimos una sintaxis
abstracta, vea la secci�on 2.6, para la gram�atica de par�entesis. Una primer sintaxis
abstracta se de�ne con el tipo de dato Parentheses1.
data Parentheses1 = Match1 Char Parentheses1 Char Parentheses1
| Empty1
56 Combinadores para el an�alisis sint�actico
data Parentheses = Match Parentheses Parentheses
| Empty
deriving Show
open = symbol '('
close = symbol ')'
parens :: Parser Char Parentheses
parens = f <$> open <*> parens <*> close <*> parens
<|> succeed Empty
where f a b c d = Match b d
nesting :: Parser Char Int
nesting = f <$> open <*> nesting <*> close <*> nesting
<|> succeed 0
where f a b c d = max (1+b) d
Listado 4: ParseParentheses.hs
Por ejemplo, la frase \()()" se representa por
Match1 '(' Empty1 ')' (Match1 '(' Empty1 ')')
Suponga que queremos calcular la cantidad de par�entesis de una frase. La cantidad
de par�entesis se calcula con la funci�on nrofpars, que se de�ne por inducci�on sobre
el tipo de dato Parentheses1.
nrofpars :: Parentheses1 -> Int
nrofpars (Match1 cl pl cr pr) = nrofpars pl + nrofpars pr + 2
nrofpars Empty1 = 0
Como los valores cl y cr en el caso de Match1 no se usan (y nunca deber��an usarse)
por las funciones de�nidas sobre Parentheses1, usamos el siguiente tipo de dato
para la sintaxis abstracta para la gram�atica de par�entesis.
data Parentheses = Match Parentheses Parentheses
| Empty
Ahora podemos a~nadir `funciones sem�anticas' para el analizador sint�actico. As��,
conseguimos la de�nici�on de parens del listado 4.
Al variar la funci�on usada antes de <$> (la `funci�on sem�antica'), podemos retornar
otras cosas adem�as de �arboles de an�alisis. Como ejemplo, construimos un analizador
que calcula la profundidad de anidamiento de los par�entesis, vea la funci�on nesting
de�nida en el listado 4.
Una sesi�on en la cual se usa nesting puede verse as��:
? nesting "()(())()"
[(2,[]), (2,"()"), (1,"(())()"), (0,"()(())()")]
? nesting "())"
[(1,")"), (0,"())")]
3.4 M�as combinadores de an�alisis sint�actico 57
Como se puede ver, cuando hay un error de sintaxis en el par�ametro, no hay solucio-
nes con la cadena restante vac��a. Es bastante sencillo veri�car si una cadena dada
pertenece al lenguaje analizado por un dado analizador sint�actico.
Ejercicio 3.17 . >Cu�al es el tipo de la funci�on f en la l��nea 8 del listado 4? >De qu�e tipo es el
analizador sint�actico open? Usando el tipo de <$>, >cu�al es el tipo de f <$> open?
>Puede f <$> open ser usado como lado izquierdo de <*> parens? >De qu�e tipo
es el resultado? /
Ejercicio 3.18 . >Cu�al es la manera conveniente para asociar <*>? >Existe? /
Ejercicio 3.19 . Escriba una funci�on test que determine si una cadena dada pertenece o no al
lenguaje analizado por un dado analizador sint�actico. /
3.4 M�as combinadores de an�alisis sint�actico
En principio se puede construir analizadores sint�acticos para cualquier lenguaje li-
bre de contexto usando los combinadores <*> y <|>, pero en la pr�actica es m�as
f�acil tener disponibles algunos combinadores de analizadores sint�acticos m�as. En los
formalismos tradicionales de gram�atica se usan s��mbolos adicionales para describir,
por ejemplo, construcciones opcionales o repetitivas. Considere por ejemplo el for-
malismo BNF, en el que originalmente s�olo la composici�on secuencial y alternativa
pueden usarse (denotada por la yuxtaposici�on y barras verticales, respectivamente),
pero que fue despu�es extendido a EBNF para permitir tambi�en repetici�on, denotado
por un asterisco. El objetivo de esta secci�on es mostrar c�omo se puede extender el
conjunto de combinadores de an�alisis sint�actico.
3.4.1 Combinadores de an�alisis sint�actico para EBNF
Es muy f�acil hacer nuevos combinadores de analizadores sint�acticos para EBNF. Co-
mo un primer ejemplo consideraremos la repetici�on. Dado un analizador sint�actico
para una construcci�on, many construye un analizador sint�actico para cero o m�as
ocurrencias de la construcci�on:
many :: Parser s a -> Parser s [a]
many p = list <$> p <*> many p
<|> succeed []
As��, many P implementa la expresi�on EBNF P �. La funci�on auxiliar list toma un
elemento y una lista, y los combina en una simple lista:
list x xs = x:xs
La funci�on list es s�olo (:) y pod��amos as�� haberla escrito en la de�nici�on de many,
pero entonces la de�nici�on podr��a haber parecido demasiado misteriosa a primera
vista.
El orden en que se dan las alternativas s�olo in uencia el orden en el cual las soluciones
se ponen en la lista de �exitos.
Por ejemplo el combinador many puede usarse en el an�alisis de un n�umero natural:
58 Combinadores para el an�alisis sint�actico
-- EBNF parser combinators
option :: Parser s a -> a -> Parser s a
option p d = p <|> succeed d
many :: Parser s a -> Parser s [a]
many p = list <$> p <*> many p <|> succeed []
many1 :: Parser s a -> Parser s [a]
many1 p = list <$> p <*> many p
pack :: Parser s a -> Parser s b -> Parser s c -> Parser s b
pack p r q = (\x y z -> y) <$> p <*> r <*> q
listOf :: Parser s a -> Parser s b -> Parser s [a]
listOf p s = list <$> p <*> many ((\x y -> y) <$> s <*> p)
-- Auxiliary functions
first :: Parser s b -> Parser s b
first p xs | null r = []
| otherwise = [head r]
where r = p xs
greedy, greedy1 :: Parser s b -> Parser s [b]
greedy = first . many
greedy1 = first . many1
list x xs = x:xs
Listado 5: EBNF.hs
3.4 M�as combinadores de an�alisis sint�actico 59
natural :: Parser Char Int
natural = foldl f 0 <$> many newdigit
where f a b = a*10 + b
El analizador sint�actico natural, de�nido de esta forma, tambi�en acepta la entrada
vac��a como un n�umero. Si esto no es lo deseado, es mejor utilizar el combinador de
analizadores sint�acticos many1, el cual acepta una o m�as ocurrencias de una cons-
trucci�on, y corresponde a la expresi�on EBNF P+, vea la secci�on 2.7. El combinador
many1 est�a de�nido en el listado 5.
Otro combinador del EBNF es el combinador de opci�on P?, toma como par�ametro
un analizador sint�actico, y devuelve un analizador sint�actico que reconoce la misma
construcci�on, pero que tambi�en es exitoso si esa construcci�on no est�a presente en la
cadena de entrada. La de�nici�on se expone en el listado 5. Este combinador tiene
un par�ametro adicional: el valor que deber��a ser usado como resultado en caso de
que la construcci�on no est�e presente. Este es en cierto modo un valor prede�nido.2
Por medio del uso de las funciones option y many, se han introducido una gran
cantidad de posibilidades de rastreo con retroceso. Esto no es siempre ventajoso.
Por ejemplo, si de�nimos un analizador sint�actico para identi�cadores por medio de
identifier = many1 (satisfy isAlpha)
Tambi�en se puede reconocer una palabra simple como dos identi�cadores. Debido
al orden de las alternativas en la de�nici�on de many (succeed [] aparece como la
segunda alternativa), se intenta primero un an�alisis greedy (acumula tantas letras
como sea posible en el identi�cador), pero si el an�alisis fracasa en alguna parte de
la frase, tambi�en se intentan los an�alisis menos greedy del identi�cador { en vano.
Usted dar�a una mejor de�nici�on de identifier en el ejercicio 3.26.
En situaciones donde desde la manera en que la gram�atica es construida podemos
predecir que no tiene sentido intentar resultados no greedy de many, podemos de�-
nir un transformador de analizador sint�actico first, que transforma un analizador
sint�actico en un analizador sint�actico que solamente devuelve la primera posibilidad.
Esto se realiza tomando el primer elemento de la lista de �exitos.
first :: Parser a b -> Parser a b
first p xs | null r = []
| otherwise = [head r]
where r = p xs
Usando esta funci�on, podemos crear una versi�on especial \todo o nada" de many:
greedy = first . many
greedy1 = first . many1
Si componemos la funci�on first con el combinador de analizadores sint�acticos
option:
obligatory = first . option
obtenemos un analizador sint�actico que debe aceptar una construcci�on si �esta est�a
presente, pero la cual no debe fallar si no lo est�a.
2NT: en ingl�es \by default".
60 Combinadores para el an�alisis sint�actico
3.4.2 Separadores
Los combinadores many, many1 y option son cl�asicos en las construcciones de com-
piladores, {hay notaciones para ella en EBNF (*,+ y ? respectivamente){, pero no
hay necesidad de dejarlo as��. Por ejemplo, muchas construcciones de lenguajes se en-
cierran frecuentemente entre dos s��mbolos sin signi�cado, m�as comunmente en alg�un
tipo de par�entesis. Para esto dise~namos un combinador de analizadores sint�acticos
pack. Dado un analizador sint�actico que tome un s��mbolo de apertura, un cuerpo,
y un s��mbolo de terminaci�on, �este construye un analizador sint�actico para encerrar
el cuerpo, como se de�ne en el listado 5. Casos especiales de este combinador son:
parenthesised p = pack (symbol '(') p (symbol ')')
bracketed p = pack (symbol '[') p (symbol ']')
compound p = pack (token "begin") p (token "end")
Otra construcci�on que ocurre frecuentemente es la repetici�on de cierta construc-
ci�on, donde los elementos se separan mediante alg�un s��mbolo. Se puede pensar en
una lista de argumentos (expresiones separadas por comas), o sentencias compues-
tas (sentencias separadas por punto y coma). Para los �arboles de derivaci�on, los
separadores no son importantes. Dado un analizador sint�actico para los items y
un analizador sint�actico para los separadores, la funci�on listOf abajo genera un
analizador sint�actico para una lista (posiblemente vac��a), :
listOf :: Parser s a -> Parser s b -> Parser s [a]
listOf p s = list <$> p <*> many ((\x y -> y) <$> s <*> p )
Algunas instancias �utiles son:
commaList, semicList :: Parser Char a -> Parser Char [a]
commaList p = listOf p (symbol ',')
semicList p = listOf p (symbol ';')
Una variante algo m�as complicada de la funci�on listOf es el caso donde los sepa-
radores llevan un signi�cado en s�� mismos. Por ejemplo, en expresiones aritm�eticas,
donde los operadores que separan las subexpresiones tienen que ser parte de un
�arbol de an�alisis. Para este caso desarrollaremos las funciones chainr y chainl.
Estas funciones esperan que el analizador sint�actico para los separadores devuelva
una funci�on (!); esa funci�on es usada por chain para combinar �arboles de an�alisis
para los items. En el caso de chainr el operador es aplicado de derecha a izquierda,
en el caso de chainl es aplicado de izquierda a derecha. Las funciones chainr y
chainl est�an de�nidas en el listado 6.
Las de�niciones se ven bastante complicadas, pero cuando se ve la gram�atica sub-
yacente son bastante directas. Suponga que aplicamos el operador � (� es un
operador variable, denota un operador arbitrario asociativo a la derecha) de derecha
a izquierda, as��
e1 � e2 � e3 � e4=
e1 � (e2 � (e3 � e4))=
((e1�) � (e2�) � (e3�)) e4
3.4 M�as combinadores de an�alisis sint�actico 61
-- Chain expression combinators
chainr :: Parser s a -> Parser s (a -> a -> a) -> Parser s a
chainr pe po = h <$> many (j <$> pe <*> po) <*> pe
where j x op = (x `op`)
h fs x = foldr ($) x fs
chainl :: Parser s a -> Parser s (a -> a -> a) -> Parser s a
chainl pe po = h <$> pe <*> many (j <$> po <*> pe)
where j op x = (`op` x)
h x fs = foldl (flip ($)) x fs
Listado 6: Chains.hs
Seguidamente, podemos analizar tales expresiones analizando muchos pares expre-
si�on-operador, convirti�endolos en funciones, y aplicando todas esas funciones a la
ultima expresi�on. Esto se realiza mediante la funci�on chainr, vea listado 6. Si el
operador � es aplicado de izquierda a derecha, entonces
e1 � e2 � e3 � e4=
((e1 � e2)� e3)� e4=
((�e4) � (�e3) � (�e2)) e1
As��, tal expresi�on puede ser analizada primero mediante el an�alisis de una expresi�on
simple (e1), luego analizando muchos pares operador-expresi�on, convirti�endolos en
funciones, y aplicando todas esas funciones a la primera expresi�on. Esto es lo que
hace la funci�on chainl, ver el listado 6.
Las funciones chainl y chainr pueden hacerse m�as e�cientes evitando la cons-
trucci�on de una lista intermedia de funciones. Las de�niciones resultantes pueden
encontrarse en [5].
Observe que las funciones chainl y chainr son muy similares, la �unica diferencia
es que `todo se invierte': la funci�on j de chainr toma un valor y un operador, y
retorna la funci�on obtenida aplicando a izquierda el operador, la funci�on j de chainl
toma un operador y un valor, y retorna la funci�on obtenida aplicando a derecha el
operador a este valor. Tales funciones se denominan duales . dual
Ejercicio 3.20 . > Cu�al es el valor de
many (symbol 'a') xs
para xs 2 [] ,['a'] ,['b'] ,['a','b'] ,['a','a','b']? /
Ejercicio 3.21 . Considere la aplicaci�on del analizador sint�actico many (symbol 'a') a la ca-
dena "aaa". >En qu�e orden los cuatro posibles analizadores sint�acticos aparecen en
la lista de �exitos? /
Ejercicio 3.22 . Usando los combinadores de an�alisis sint�actico option, many y many1 de�na
analizadores sint�acticos para cada uno de los lenguajes b�asicos de�nidos en 2.3.1. /
62 Combinadores para el an�alisis sint�actico
Ejercicio 3.23 . Como otra variaci�on del tema `repetici�on', de�na un combinador de anali-
zadores sint�acticos psequence que transforme una lista de parsers de alg�un tipo
en un parser que devuelve una lista de elementos de este tipo. Tambi�en de�na un
combinador choice que itere el operador <|>. /
Ejercicio 3.24 . Como una aplicaci�on de psequence, de�na la funci�on token que fue descutida
en la secci�on 3.2. /
Ejercicio 3.25 . Analice cuidadosamente la funci�on sem�antica en la de�nici�on de chainl en el
listado 6. /
Ejercicio 3.26 . En lenguajes de programaci�on reales, los identi�cadores tienen reglas m�as
exibles: el primer s��mbolo puede ser una letra, pero el s��mbolo que sigue (si hay
alguno) puede ser una letra, d��gito o un `underscore' ( ). De�na de una forma m�as
realista el analizador sint�actico identifier. /
3.5 Expresiones Aritm�eticas
El objetivo de esta secci�on es usar combinadores de an�alisis sint�actico en una aplica-
ci�on concreta. Desarrollaremos un analizador sint�actico para expresiones aritm�eticas,
que tiene la siguiente sintaxis concreta:
E ! E + E j E - E j E * E j E / E j (E) j Digs
Adem�as de estas producciones, tambi�en tenemos producciones para identi�cadores
y aplicaciones de funciones:
E ! Identi�er j Identi�er (LoA)
LoA ! � j E(,E)�
El �arbol de an�alisis para esta gram�atica es del tipo Expr:
data Expr = Con Int
| Var String
| Fun String [Expr]
| Expr :+: Expr
| Expr :-: Expr
| Expr :*: Expr
| Expr :/: Expr
Casi se puede reconocer la estructura de un analizador sint�actico en esta de�nici�on
de tipo, pero para contar las prioridades de los operadores, usaremos una gram�atica
con tres no terminales, `expresi�on' , `term' y `factor' : una expresi�on est�a compuesta
de t�erminos separados por + o �; un t�ermino est�a compuesto de factores separados
por � o =, y un factor es una constante, variable, llamada a una funci�on o una
expresi�on entre par�entesis.
Esta gram�atica est�a representada por las funciones del listado 7. El primer analiza-
dor, fact, reconoce factores.
fact :: Parser Char Expr
3.5 Expresiones Aritm�eticas 63
-- Type definition for parse tree
data Expr = Con Int
| Var String
| Fun String [Expr]
| Expr :+: Expr
| Expr :-: Expr
| Expr :*: Expr
| Expr :/: Expr
-------------------------------------------------------------
-- Parser for expressions with two priorities
fact :: Parser Char Expr
fact = Con <$> integer
<|> Var <$> identifier
<|> Fun <$> identifier <*> parenthesised (commaList expr)
<|> parenthesised expr
term :: Parser Char Expr
term = chainr fact
( const (:*:) <$> symbol '*'
<|> const (:/:) <$> symbol '/'
)
expr :: Parser Char Expr
expr = chainr term
( const (:+:) <$> symbol '+'
<|> const (:-:) <$> symbol '-'
)
Listado 7: ExpressionParser.hs
64 Combinadores para el an�alisis sint�actico
fact = Con <$> integer
<|> Var <$> identifier
<|> Fun <$> identifier <*> parenthesised (commaList expr)
<|> parenthesised expr
La primera alternativa es un analizador sint�actico para n�umeros enteros que es
post-procesado por la `funci�on sem�antica' Con. La segunda y tercer alternativas
son una variable o llamada a una funci�on, dependiendo de la presencia de una lista
de par�ametros. En ausencia del �ultimo se aplica la funci�on Var, caso contrario la
funci�on Fun. Para la cuarta alternativa no existe una funci�on sem�antica porque el
signi�cado de una expresi�on entre par�entesis es el mismo que el de una expresi�on sin
par�entesis.
Para la de�nici�on de term como una lista de factores separados por operadores
multiplicativos, usamos la funci�on chainr. Recuerde que la funci�on chainr repeti-
damente reconoce su primer argumento (fact), separado por su segundo par�ametro
(� o =). Los �arboles de derivaci�on para los factores individuales se unen mediante
las funciones constructoras que aparecen antes de <$>.
La funci�on expr es an�aloga a term, s�olo con operadores aditivos en lugar de opera-
dores multiplicativos, y con terminos en lugar de factores.
Este ejemplo muestra claramente el poder de este m�etodo. No se necesita un for-
malismo separado para gram�aticas; las reglas de producci�on de la gram�atica se
combinan usando funciones de alto orden. Tampoco hay necesidad de un generador
de analizadores sint�acticos separado (como `yacc'); las funciones pueden verse tanto
como descripci�on de la gram�atica o como un analizador sint�actico ejecutable.
Ejercicio 3.27 . Modi�que las funciones del listado 7, de tal forma que se analice + como ope-
rador asociativo a derecha; pero se analice � como operador asociativo a izquierda.
/
3.6 Expresiones generalizadas
Esta secci�on generaliza el analizador sint�actico de la secci�on anterior con respecto a
las prioridades. Se pueden analizar expresiones aritm�eticas, en las que los operadores
tienen m�as de dos niveles de prioridad, escribiendo m�as funciones auxiliares entre
term y expr. Se usa la funci�on chainr en cada de�nici�on, y su primer par�ametro
es la funci�on de un nivel de prioridad menor.
Si hay nueve niveles de prioridad obtenemos nueve copias de casi el mismo texto.
Esto no es como deber��a ser. Las funciones que se parecen una a otra son una
indicaci�on de que deber��amos escribir una funci�on generalizada, donde se describe
las diferencias usando par�ametros extra. Por consiguiente, revisamos nuevamente
las diferencias en las de�niciones de term y expr. Estas son:
� Los operadores y los constructores del �arbol asociados que se usan en el segundo
par�ametro de chainr
� El analizador sint�actico que se usa como primer par�ametro de chainr
La funci�on generalizada tomar�a estas dos diferencias como argumentos extras: el
primero en forma de una lista de pares, el segundo en forma de una funci�on de
an�alisis sint�actico:
3.7 Exercises 65
type Op a = (Char,a -> a -> a)
gen :: [Op a] -> Parser Char a -> Parser Char a
gen ops p = chainr p (choice (map f ops))
where f (s,c) = const c <$> symbol s
Si adem�as de�nimos en forma sucinta:
multis = [ ('*',(:*:)), ('/',(:/:)) ]
addis = [ ('+',(:+:)), ('-',(:-:)) ]
entonces expr y term pueden de�nirse como parametrizaciones parciales de gen:
expr = gen addis term
term = gen multis fact
Expandiendo la de�nici�on de term en expr obtenemos:
expr = addis `gen` (multis `gen` fact)
la cual un programador funcional experimentado inmediatamente reconoce como
una aplicaci�on de foldr:
expr = foldr gen fact [addis, multis]
A partir de esta de�nici�on, una generalizaci�on para m�as niveles de prioridad es
simplemente cuesti�on de extender la lista de listas de operadores.
La formulaci�on muy compacta de este analizador sint�actico para expresiones con
una cantidad arbitraria de niveles de prioridad es posible ya que los combinadores
de an�alisis sint�actico se pueden usar en conjunci�on con los mecanismos existentes
para generalizaci�on y parametrizaci�on parcial de Haskell.
Contrariamente a los enfoques convencionales, no se necesita codi�car expl��citamente
con enteros los niveles de prioridad. Lo �unico que importa es la posici�on relativa de
un operador en la lista de `lista de operadores con la misma prioridad'. Tambi�en la
inserci�on de nuevos niveles de prioridad es muy f�acil. Las de�niciones se resumen
en el listado 8.
resumen
Este cap��tulo muestra c�omo construir analizadores sint�acticos a partir de combina-
dores simples. Se muestra como una peque~na librer��a de combinadores puede ser
una herramienta poderosa en la construcci�on de analizadores sint�acticos.
3.7 Exercises
Ejercicio 3.28 . Demostrar las siguientes leyes:
1. h <$> (f <$> p) = (h.f) <$> p
2. h <$> (p <|> q) = (h <$> p) <|> (h <$> q)
66 Combinadores para el an�alisis sint�actico
-- Parser for expressions with arbitrary many priorities
type Op a = (Char,a -> a -> a)
fact' :: Parser Char Expr
fact' = Con <$> integer
<|> Var <$> identifier
<|> Fun <$> identifier <*> parenthesised (commaList expr')
<|> parenthesised expr'
gen :: [Op a] -> Parser Char a -> Parser Char a
gen ops p = chainr p (choice (map f ops))
where f (s,c) = const c <$> symbol s
expr' :: Parser Char Expr
expr' = foldr gen fact' [addis, multis]
multis = [ ('*',(:*:)), ('/',(:/:)) ]
addis = [ ('+',(:+:)), ('-',(:-:)) ]
Listado 8: GExpressionParser.hs
3. h <$> (p <*> q) = ((h.) <$> p) <*> q
/
Ejercicio 3.29 . Considerar la respuesta al ejercicio 3.9. De�nir un combinador de an�alisis pPal
que transforme una representaci�on concreta de un pal��ndrome en una abstracta.
Probar la funci�on con los pal��ndromes concretos cPal1 y cPal2. /
Ejercicio 3.30 . Considerar la respuesta al ejercicio 2.22. De�nir un combinador de an�alisis
pMir que transforme una representaci�on concreta de un pal��ndrome espejo en una
abstracta. Probar la funci�on con los pal��ndromes espejo concretos cMir1 ycMir2.
/
Ejercicio 3.31 . Considerar la respuesta al ejercicio 2.24. De�nir un combinador de an�alisis
pBitList que transforme una representaci�on concreta de una lista de bits en una
abstracta. Probar la funci�on con las listas de bits concretas cBitList1 ycBitList2.
/
Ejercicio 3.32 . De�nir un analizador para n�umeros de punto �jo, es decir n�umeros como 12.34
y -123.456. Tambi�en se aceptan enteros. Observar que la parte que sigue al punto
decimal parece un entero, <pero tiene una sem�antica distinta! /
Ejercicio 3.33 . De�nir un analizador para n�umeros de punto otante, que son n�umeros de
punto �jo seguidos por opcionalmente una E y un exponente (entero positivo o
negativo). /
Ejercicio 3.34 . De�nir un analizador para asignaciones de Java que consiste de una variable,
un signo =, una expresi�on y un punto y coma. /
3.7 Exercises 67
Ejercicio 3.35 . De�nir un analizador para sentencias de Java (simpli�cadas). /
Ejercicio 3.36 . Esbozar la construcci�on de un analizador sint�actico para programas en Java.
/
Cap��tulo 4
Gram�aticas y dise~no de analizadores
sint�acticos
En los anteriores cap��tulos hemos introducido muchos conceptos relacionados con
gram�aticas y analizadores sint�acticos. El objetivo de este cap��tulo es revisar es-
tos conceptos, y mostrar c�omo se usan en el dise~no de gram�aticas y analizadores
sint�acticos.
El dise~no de una gram�atica y un analizador sint�actico para un lenguaje consiste en
varios pasos. Se tiene que:
1. Dar una gram�atica para el lenguaje para el cual se quiere tener un analizador
sint�actico;
2. Analizar esta gram�atica para averiguar si tiene o no algunas propiedades de-
seables;
3. Posiblemente transformar la gram�atica para obtener algunas de estas propie-
dades deseables;
4. Decidir el tipo del analizador sint�actico Parser a b, es decir, decidir sobre
ambos, el tipo de la entrada a del analizador sint�actico (que puede ser el
tipo resultante de un analizador lexicogr�a�co), y el tipo del resultado b del
analizador sint�actico;
5. Construir un analizador sint�actico b�asico;
6. A~nadir funciones sem�anticas;
7. Veri�car si ha obtenido o no lo que se esperaba.
En el resto de esta secci�on describiremos y ejempli�caremos cada uno de estos pasos
en detalle.
Como ejemplo inmediato construiremos una gram�atica y un analizador sint�actico
para esquemas de viaje por un dia, de la siguiente forma:
Groningen 8:37 9:44 Zwolle 9:49 10:15 Utrecht 10:21 11:05 Den Haag
Podr��amos querer hacer varias cosas con tal esquema, por ejemplo:
1. Calcular el tiempo neto del viaje, es decir, el tiempo de viaje menos el tiempo
de espera (2 horas y 17 minutos en el ejemplo de arriba);
69
70 Gram�aticas y dise~no de analizadores sint�acticos
2. Calcular el tiempo total que uno espera en las estaciones intermedias (11 mi-
nutos).
Este cap��tulo de�ne funciones para llevar a cabo estos c�alculos.
4.1 Paso 1: Una gram�atica para el lenguaje
El punto inicial para el dise~no de un analizador sint�actico para un lenguaje es de-
�nir la gram�atica que describa este lenguaje con tanta precisi'on como sea posible.
Es importante que se convenza a s�� mismo del hecho que la gram�atica que usted
proporciona realmente genera el lenguaje deseado, ya que la gram�atica ser�a la base
para las transformaciones gramaticales, las cuales podr��an convertir a la gram�atica
en un conjunto de producciones incomprensibles.
Para el lenguaje de esquema de viajes, podemos tener varias gram�aticas. La siguiente
gram�atica enfatiza el hecho de que un viaje consiste en cero o m�as llegadas y salidas.
TS ! TS Departure Arrival TS j Station
Station ! Identi�er
Departure ! Time
Arrival ! Time
Time ! Nat:Nat
Donde Identi�er y Nat han sido de�nidos en la secci�on 2.3.1. De manera que un
esquema de viajes es una secuencia de tiempo de salidas y llegadas, separadas por
estaciones. Tome en cuenta que una simple estaci�on tambi�en es un esquema de
viajes con esta gram�atica.
Otra gram�atica enfatiza el cambio de estaciones:
TS ! Station Departure (Arrival Station Departure)� Arrival Station
j Station
As��, cada esquema de viaje empieza y termina en una estaci�on, y entre ambos hay
una lista de estaciones intermedias.
4.2 Paso 2: Analizando la gram�atica
Para analizar e�cientemente las frases de un lenguaje, queremos tener gram�aticas
no ambiguas, es decir, factorizadas a izquierda y sin recursividad a izquierda. De-
pendiendo del analizador sint�actico que queremos obtener, podr��amos desear otras
propiedades para la gram�atica. De esta manera, un primer paso en el dise~no de
un analizador sint�actico es analizar la gram�atica, y determinar aquellas propiedades
que se satisfacen o no. Todav��a no hemos desarrollado herramientas para el an�alisis
de gram�aticas (lo haremos en el cap��tulo sobre an�alisis sint�actico LL(1)), pero para
algunas gram�aticas es f�acil detectar algunas propiedades.
La primera gram�atica del ejemplo es recursiva a izquierda y derecha: la primera pro-
ducci�on paraTS comienza y termina con TS . Adem�as, la secuencia Departure Arrival
es un separador asociativo del lenguaje generado.
4.3 Paso 3: Transformando la gram�atica 71
Estas propiedades pueden usarse para transformar la gram�atica. Como no nos im-
porta la recursividad a derecha, no haremos uso del hecho que la gram�atica es
recursiva a derecha. Las otras propiedades se usar�an en las transformaciones de
gram�aticas en la siguiente subsecci�on.
4.3 Paso 3: Transformando la gram�atica
Como la secuencia Departure Arrival es un separador asociativo del lenguaje gene-
rado, las producciones para TS pueden transformarse en:
TS ! Station j Station Departure Arrival TS (4.1)
As�� hemos eliminado la recursi�on a izquierda en la gram�atica. Ambas producciones
para TS empiezan con el no terminal Station y de esta manera TS puede factorizarse
a izquierda. Las producciones resultantes son:
TS ! Station Z
Z ! � j Departure Arrival TS
Tambi�en podemos aplicar la equivalencia (2.1) a las dos producciones para TS de
(4.1) y obtener la producci�on:
TS ! (Station Departure Arrival)�Station (4.2)
Entonces, >qu�e producciones tomamos para TS? Esto depende de qu�e queremos
hacer con las frases analizadas. Mostraremos varias opciones en la siguiente secci�on.
4.4 Paso 4: Decidiendo sobre los tipos
Queremos escribir un analizador sint�actico para esquemas de viaje, es decir, quere-
mos escribir una funci�on ts del tipo:
ts :: Parser ? ?
Los signos de interrogaci�on deber��an reemplazarse por el tipo de la entrada y el tipo
del resultado respectivamente. Para el tipo de la entrada podemos elegir, entre por
lo menos, dos posibilidades: caracteres (de tipo Char) o s��mbolos (de tipo Token).
Los tipos de los s��mbolos pueden elegirse como sigue:
data Token = Station_Token Station | Time_Token Time
type Station = String
type Time = (Int,Int)
Construiremos un analizador sint�actico para ambos tipos de entrada en la siguiente
subsecci�on. As��, ts tiene uno de los dos tipos siguientes.
ts :: Parser Char ?
ts :: Parser Token ?
72 Gram�aticas y dise~no de analizadores sint�acticos
Para el tipo del resultado tenemos muchas opciones. Si s�olo queremos calcular el
tiempo total de viaje, Int es su�ciente como tipo del resultado. Si queremos calcular
el tiempo total de viaje, el tiempo total de espera, y una buena versi�on impresa del
esquema de viajes, podemos hacer varias cosas:
� De�nir tres analizadores sint�acticos, con Int (tiempo total de viaje), Int (tiem-
po total de espera), y String (buena versi�on impresa) como tipo del resultado
respectivamente;
� De�nir un analizador sint�actico simple con el triple (Int,Int,String) como
el tipo del resultado;
� De�nir una sintaxis abstracta para esquemas de viaje, digamos un tipo de
datos TS, y de�nir tres funciones en TS que calculen los resultados deseados.
La primera alternativa analiza tres veces la entrada, y es bastante ine�ciente com-
parada con las otras alternativas. La segunda alternativa es dif��cil de extender si
queremos calcular algo extra, pero en algunos casos puede ser m�as e�ciente que la
tercera alternativa. La tercera alternativa necesita una sintaxis abstracta. Hay va-
rias maneras de de�nir una sintaxis abstracta para esquemas de viaje. La primera
sintaxis abstracta corresponde a la de�nici�on (4.1) de la gram�atica TS .
data TS1 = Single1 Station
| Cons1 Station Time Time TS1
Station y Time se han de�nido anteriormente. Una segunda sintaxis abstracta
corresponde a la gram�atica para esquemas de viaje de�nida en (4.2).
type TS2 = ([(Station,Time,Time)],Station)
De esta manera un esquema de viaje es un par: el primer componente del mismo
es una lista de triples que consisten de una estaci�on de salida, un tiempo de salida,
y un tiempo de llegada; y el segundo componente del par es la �ultima estaci�on de
llegada. Una tercera sintaxis abstracta corresponde a la segunda gram�atica de�nida
en la secci�on 4.1:
data TS3 = Single3 Station
| Cons3 (Station,Time,[(Time,Station,Time)],Time,Station)
>Qu�e sintaxis abstracta deber��amos tomar? Nuevamente, esto depende de lo que
queremos hacer con la sintaxis abstracta. Como TS2 y TS1 combinan tiempos de
salida y llegada en una tupla, es conveniente usarlos para calcular tiempos de viaje.
TS3 es �util cuando queremos calcular tiempos de espera puesto que combina tiempos
de salida y llegada en un constructor. A menudo queremos representar exactamente
las producciones de una gram�atica en una sintaxis abstracta; as��, si usamos (4.1) para
la gram�atica de esquemas de viaje, usamos TS1 para la sintaxis abstracta. Observe
que TS1 es un tipo de datos, mientras que TS2 es un tipo. TS1 no puede de�nirse
como un tipo por las dos producciones alternativas de TS. TS2 puede ser de�nido
como un tipo de datos a~nadiendo un constructor. Entre tipos y tipos de datos, cada
uno tiene sus ventajas y desventajas; la aplicaci�on determina cu�al usar. El tipo
del resultado de la funci�on de an�alisis ts puede ser uno de los tipos mencionados
anteriormente (Int, etc.), o uno de TS1, TS2, TS3.
4.5 Paso 5: Construyendo el analizador sint�actico b�asico 73
4.5 Paso 5: Construyendo el analizador sint�actico b�asico
Convertir una gram�atica en un analizador sint�actico es un proceso mec�anico que
consiste en un conjunto de reglas de reemplazo sencillas. Los lenguajes de progra-
maci�on funcional ofrecen alguna exibilidad extra que usamos algunas veces, pero
normalmente escribir un analizador sint�actico es una simple traducci�on. Usamos las
siguientes reglas de reemplazo.
! =
j <|>
(espacio) <*>
+ many1
� many
? option
terminal x symbol x
inicio de una secuencia de s��mbolos undefined<$>
Observe que cada secuencia de s��mbolos empieza con undefined<$>. El undefined<$>
tiene que reemplazarse por una funci�on sem�antica adecuada en el Paso 6, pero po-
ner undefined<$> ac�a asegura la correctitud del tipo del analizador sint�actico. Por
supuesto que la ejecuci�on del analizador resultar�a en un error.
Construimos un analizador sint�actico b�asico para cada uno de los tipos de entrada
Char y Token.
4.5.1 Analizadores sint�acticos b�asicos de cadenas
Aplicando estas reglas a la gram�atica (4.2) para esquemas de viaje, obtenemos el
siguiente analizador sint�actico b�asico.
station :: Parser Char Station
station = undefined <$> identifier
time :: Parser Char Time
time = undefined <$> natural <*> symbol ':' <*> natural
departure, arrival :: Parser Char Time
departure = undefined <$> time
arrival = undefined <$> time
tsstring :: Parser Char ?
tsstring = undefined <$>
many (undefined <$>
spaces
<*> station
<*> spaces
<*> departure
<*> spaces
<*> arrival
)
<*> spaces
74 Gram�aticas y dise~no de analizadores sint�acticos
<*> station
spaces :: Parser Char String
spaces = undefined <$> many (symbol ' ')
La �unica cosa que queda por hacer es agregar pegamento sem�antico a las funciones.
El pegamento sem�antico tambi�en determina el tipo de funci�on tsstring, que se
denota por ? por el momento. Para los otros analizadores sint�acticos b�asicos hemos
elegido algunos tipos de resultado razonables. Las funciones sem�anticas se de�nen
en el siguiente y �ultimo paso.
4.5.2 Analizadores sint�acticos b�asicos de s��mbolos
Para obtener un analizador sint�actico b�asico de s��mbolos, primero escribimos un
analizador lexicogr�a�co que produce una lista de s��mbolos a partir de una cadena.
scanner :: String -> [Token]
scanner = map mkToken . words
mkToken :: String -> Token
mkToken xs = if isDigit (head xs)
then Time_Token (mkTime xs)
else Station_Token (mkStation xs)
parse_result :: [(a,b)] -> a
parse_result xs
| null xs = error "parse_result: could not parse the input"
| otherwise = fst (head xs)
mkTime :: String -> Time
mkTime = parse_result . time
mkStation :: String -> Station
mkStation = parse_result . station
Este es un analizador lexicogr�a�co b�asico con muchos mensajes de error b�asicos, pero
es su�ciente por ahora. La composici�on del analizador lexicogr�a�co con la funci�on
tstoken1, de�nida abajo, proporciona el analizador sint�actico �nal.
tstoken1 :: Parser Token ?
tstoken1 = undefined <$>
many (undefined <$>
tstation
<*> tdeparture
<*> tarrival
)
<*> tstation
tstation :: Parser Token Station
tstation (Station_Token s:xs) = [(s,xs)]
4.6 Paso 6: A~nadiendo funciones Sem�anticas 75
tstation _ = []
tdeparture, tarrival :: Parser Token Time
tdeparture (Time_Token (h,m):xs) = [((h,m),xs)]
tdeparture _ = []
tarrival (Time_Token (h,m):xs) = [((h,m),xs)]
tarrival _ = []
donde nuevamente falta de�nir las funciones sem�anticas. Observe que las funciones
tdeparture y tarrival son la misma funci�on. Se incluyen ambos para re ejar su
presencia en la gram�atica.
Otro analizador sint�actico b�asico de s��mbolos se basa en la segunda gram�atica de la
secci�on 4.1.
tstoken2 :: Parser Token Int
tstoken2 = undefined <$>
tstation
<*> tdeparture
<*> many (undefined <$>
tarrival
<*> tstation
<*> tdeparture
)
<*> tarrival
<*> tstation
<|> undefined <$> tstation
4.6 Paso 6: A~nadiendo funciones Sem�anticas
Una vez que tenemos las funciones b�asicas de an�alisis sint�actico, necesitamos a~nadir
el pegamento sem�antico: las funciones que toman los resultados de los elementos en
el lado derecho de una producci�on, y los convierten en el resultado del lado izquierdo.
La regla b�asica es: <deje que los tipos hagan el trabajo!
Primero a~nadimos funciones sem�anticas a las funciones b�asicas del an�alisis sint�actico
station, time, departure, arrival, y spaces. Como la funci�on identifier
ya devuelve una cadena, podemos tomar la funci�on identidad id en reemplazo de
undefined en la funci�on station. Como id <$> es la funci�on identidad, �esta puede
omitirse. Para obtener un valor del tipo Time a partir de un entero, un car�acter, y
un entero, tenemos que combinar los dos enteros en un par. De esta manera usamos
la funci�on:
\x y z -> (x,z)
para reemplazar a undefined en time. Ahora, como la funci�on time devuelve un
valor del tipo Time, podemos tomar la funci�on identidad para reemplazar undefined
en departure y arrival, y entonces reemplazamos id <$> time time por s�olo time.
Finalmente, el resultado de many es una cadena, as�� para reemplazar undefined en
spaces podemos tomar tambi�en la funci�on identidad.
76 Gram�aticas y dise~no de analizadores sint�acticos
La primer funci�on sem�antica para el analizador sint�actico b�asico tsstring de�nida
en la secci�on 4.5.1 devuelve un �arbol de sintaxis abstracta del tipo TS2. As��, el
primer undefined en tsstring deber��a devolver un par de: una lista de cosas del
tipo correcto (el primer componente del tipo TS2) y un Station. Como many retorna
una lista de cosas, podemos construir un tal par por medio de la funci�on
\x y z -> (x,z)
con tal de que many retorne un valor del tipo deseado: [(Station,Time,Time)].
Observe que esta funci�on sem�antica b�asicamente s�olo devuelve el valor entregado
por el analizador sint�actico spaces: no estamos interesados en los espacios entre los
componentes de nuestro esquema de viajes. El analizador sint�actico many retorna
un valor del tipo correcto si reemplazamos la segunda aparici�on de undefined en
tsstring mediante la funci�on:
\u v w x y z -> (v,x,z)
Nuevamente se prescinde del resultado devuelto por spaces. Esto completa un
analizador sint�actico para el esquema de viajes.
Las funciones sem�anticas que de�nimos a continuaci�on calculan el tiempo neto de
viaje. Para calcular el tiempo neto de viaje, tenemos que calcular el tiempo de viaje
de cada viaje desde una estaci�on a otra, y sumar los tiempos de viaje de todos estos
viajes. Obtenemos el tiempo de viaje de un viaje simple si reemplazamos la segunda
ocurrencia de undefined por:
\u v w (xh,xm) y (zh,zm) -> (zh-xh)*60 + zm-xm
y la funci�on del preludio1 de Haskell sum se puede usar para sumar estos tiempos;
as��, para la primera ocurrencia de undefined tomamos:
\x y z -> sum x
De�nimos el conjunto �nal de funciones sem�anticas para calcular el tiempo total de
espera. Como la segunda gram�atica de la secci�on 4.1 combina tiempos de llegada
y salida, usamos un analizador sint�actico basado en esta gram�atica: el analiza-
dor sint�actico b�asico tstoken2. Tenemos que dar de�niciones de tres funciones
sem�anticas undefined. Si un viaje consiste en una �unica estaci�on, no existe tiempo
de espera; as��, la �ultima ocurrencia de undefined es la funci�on const 0. La segunda
ocurrencia de la funci�on undefined calcula el tiempo de espera para una estaci�on
intermedia:
\(uh,um) v (wh,wm) -> (wh-uh)*60 + wm-um
Finalmente la primera ocurrencia de undefined suma la lista de los tiempos de
espera obtenidos por medio de la funci�on que reemplaza la segunda ocurrencia de
undefined:
\s t x y z -> sum x
1NT: en ingl�es \prelude".
4.7 Paso 7: >Consigui�o lo que esperaba? 77
4.7 Paso 7: >Consigui�o lo que esperaba?
En este �ultimo paso se prueba el analizador sint�actico (o analizadores sint�acticos)
para ver si ha obtenido o no lo que esperaba, y si ha cometido o no errores en el
proceso mencionado anteriormente.
resumen
Este cap��tulo describe los diferentes pasos que deben considerarse en el dise~no de
una gram�atica y un lenguaje.
4.8 Ejercicios
Ejercicio 4.1 . Escribir un parser para literales de punto otante de Java. La gram�atica EBNF
para literales de punto otante es:
Digits ! Digits Digit j Digit
FloatLiteral ! IntPart .FractPart? ExponentPart? FloatSuÆx?
j .FractPart ExponentPart? FloatSuÆx?
j IntPart ExponentPart FloatSuÆx?
j IntPart ExponentPart? FloatSuÆx
IntPart ! Digits
FractPart ! Digits
ExponentPart ! ExponentIndicator SignedInteger
SignedInteger ! Sign? Digits
ExponentIndicator ! e j E
Sign ! + j -
FloatSuÆx ! f j F j d j D
/
Ejercicio 4.2 . Escribir un evaluador para los literales de punto otante de Java (se puede
ignorar el oatSuÆx). /
Ejercicio 4.3 . Dependiendo de la de�nici�on de las funciones sem�anticas, los analizadores
sint�acticos construidos sobre una sintaxis abstracta (�ja) tienen la misma estruc-
tura. Dar este esquema de an�alisis sint�actico para los literales de punto otante de
Java. /
Cap��tulo 5
Lenguajes regulares
introducci�on
La primera fase de un compilador toma un programa de entrada, y divide la en-
trada en una lista de s��mbolos terminales: palabras clave, identi�cadores, n�umeros,
signos de puntuaci�on, etc. Las expresiones regulares se usan para la descripci�on de
s��mbolos terminales. Una gram�atica regular es un tipo particular de gram�atica libre
de contexto que puede usarse para describir expresiones regulares. Los aut�omatas
de estado �nito pueden usarse para reconocer frases de gram�aticas regulares. Este
cap��tulo trata de todos estos conceptos, y se organiza de la siguiente manera. La
secci�on 5.1 presenta a los aut�omatas de estado �nito. Los aut�omatas de estado �ni-
to aparecen en dos versiones: determin��sticos y no determin��sticos. La secci�on 5.1.4
muestra que un aut�omata de estado �nito no determin��stico puede ser transformado
en un aut�omata de estado �nito determin��stico, as�� no tienes que preocuparte de que
si un aut�omata es determin��stico o no. La secci�on 5.2 presenta gram�aticas regula-
res (gram�aticas libres de contexto de una forma particular), y lenguajes regulares.
Adem�as, muestra su equivalencia con aut�omatas de estado �nito. La secci�on 5.3
introduce expresiones regulares como descripciones �nitas de lenguajes regulares y
muestra que tales expresiones son otra herramienta de equivalencia para lenguajes
regulares. Finalmente, la secci�on 5.4 proporciona algunas de las demostraciones de
los resultados de las secciones previas.
objetivos
Despu�es de haber estudiado este cap��tulo conocer�a que
� los lenguajes regulares son un subconjunto de los lenguajes libres de contexto;
� no siempre es posible proporcionar una gram�atica regular para una gram�atica
libre de contexto;
� gram�aticas regulares, aut�omatas de estado �nito y expresiones regulares son
tres herramientas diferentes para lenguajes regulares;
� gram�aticas regulares, aut�omatas de estado �nito y expresiones regulares tienen
el mismo poder expresivo
� Los aut�omatas de estado �nito aparecen en dos versiones: determin��sticos y
no determin��sticos, pero uno siempre puede ser reemplazado por el otro.
79
80 Lenguajes regulares
5.1 Aut�omatas de estado �nito
El enfoque cl�asico para reconocer sentencias de un lenguaje regular utiliza aut�omatas
de estado �nito. Un aut�omata de estado �nito puede verse como una computadora
digital sencilla que tiene solamente una cantidad �nita de estados, sin almacena-
miento temporal, un archivo de entrada de solamente lectura, y una unidad de
control que registra las transiciones de estado. Un medio m�as bien limitado, pero
una herramienta �util en muchos subproblemas pr�acticos.
5.1.1 Aut�omatas de estado �nito determin��sticos
Existen dos tipos de aut�omatas de estado �nito: determin��sticos y no determin��sticos.
Comenzamos con una descripci�on de aut�omatas de estado �nito determin��sticos, la
forma m�as simple de aut�omatas.
De�nici�on 1: Aut�omatas de estado �nito determin��sticos, AFD
Un aut�omata de estado �nito (AFD) es una qu��ntupla (X;Q; d; S; F ) dondeaut�omata
de es-
tados
�nitos
deter-
min��stico
� X es el alfabeto de entrada,
� Q es un conjunto �nito de estados,
� d::Q! X ! Q es la funci�on de transici�on de estado,
� S 2 Q es el estado inicial.
� F � Q es el conjunto de estados aceptadores.
2
por ejemplo, considere el AFD M0 = (X;Q; d; S;F ) con
X = fa; b; cg
Q = fS;A;B; Cg
F = fCg
Donde la funci�on de transici�on de estados d est�a de�nida por
d S a = C
d S b = A
d S c = S
d A a = B
d B c = C
Para los seres humanos, un aut�omata de estado �nito es m�as comprensible en una
representaci�on gr�a�ca. La siguiente representaci�on es la acostumbrada: los estados
se representan como los nodos de un grafo; los estados aceptadores tienen un doble
c��rculo, los estados iniciales se mencionan o indican expl��citamente. La funci�on de
transici�on es representada por los v�ertices: siempre que d Qi x es un estado Qj ,
entonces existe una echa etiquetada x de Qi a Qj . El alfabeto de entrada est�a
impl��cito en las etiquetas. Para el aut�omata M0 de arriba, la representaci�on gr�a�ca
5.1 Aut�omatas de estado �nito 81
es:
S?>=<89:;EDGFc
@A// a //
b
��
C?>=<89:;/.-,()*+
A?>=<89:; a // B?>=<89:;
c
OO
Observe que d es una funci�on parcial: por ejemplo d B a no est�a de�nida. Podemos
convertir a d en una funci�on total introduciendo un nuevo estado `sink', el estado
resultado para todas las transiciones no de�nidas. Por ejemplo en el aut�omata de
arriba podemos introducir un estado sink D con d D x = D para todos los terminales
x, y d E x = D para todos los estados E y terminales x para los que d E x = D es
inde�nida. El estado sink y las transiciones de/para se omiten casi siempre.
La acci�on de un AFD en una cadena de entrada se describe como sigue: dada una
secuencia w de s��mbolos de entrada, w puede 'procesarse' s��mbolo a s��mbolo (de
izquierda a derecha) y | dependiendo del s��mbolo de entrada espec���co | el AFD
(inicialmente en el estado inicial) se mueve al estado como determina la funci�on
de transici�on de estado. El aut�omata se bloquea si no es posible m�as movimiento.
Cuando la entrada completa ha sido procesada y el AFD est�a en uno de sus estados
aceptadores, entonces decimos que w es aceptada por el aut�omata. aceptaci�on
Para ilustrar la acci�on de un AFD, mostraremos que la frase bac es aceptada por
M0. Hacemos esto grabando las con�guraciones sucesivas, es decir pares de estado
actual y valor de entrada restante.
(S; bac)
7!
(A; ac)
7!
(B; c)
7!
(C; �)
Debido a que el comportamiento determin��stico de un AFD la de�nici�on de acepta-
ci�on por un AFD es relativamente f�acil. Informalmente, una secuencia w 2 X� es
aceptada por un AFD (X;Q; d; S; F ), si cuando empezamos el AFD en S,terminamos
en un estado aceptador luego de procesar w. Esta descripci�on operacional de acep-
taci�on es formalizada en el predicado dfa accept . El predicado ser�a derivado de
forma descendente,1 es decir formulamos el predicado en t�erminos de subcomponen-
tes (\m�as chicas") y despu�es damos soluciones a los subcomponentes.
Suponga que dfa es una funci�on que re eja el comportamiento del AFD, es decir una
funci�on que dada una funci�on de transici�on, un estado inicial y una cadena, retorna
el �unico estado que es alcanzado despu�es de procesar la cadena comenzando en el
estado inicial. Entonces el predicado dfa accept se de�ne por:
dfa accept w (d; S; F ) = (dfa d S w) 2 F
1NT: en ingl�es \top-down".
82 Lenguajes regulares
Queda construir una de�nici�on de la funci�on dfa que toma una funci�on de transici�on,
un estado inicial, y una lista de s��mbolos de entrada, y re eja el comportamiento de
un AFD. La de�nici�on de dfa es directa
dfa d q � = q
dfa d q (ax) = dfa d (d q a) x
Sigue que la funci�on dfa puede escribirse como un foldl :
dfa d q = foldl d q:
De�nici�on 2: Aceptaci�on por un AFD
La secuencia w 2 X� es aceptada por un AFD (X;Q; d; S;F ) si
dfa accept w (d; S; F )
donde
dfa accept w (d; qs; fs) = dfa d qs w 2 fs
dfa d qs = foldl d qs
2
Usando el predicado dfa accept , el lenguaje de un AFD se de�ne como sigue.
De�nici�on 3: Lenguaje de un AFD
Para el AFD M = (X;Q; d; S;F ), el lenguaje de M , Ldfa M , se de�ne por
Ldfa M = fw 2 X� j dfa accept w (d; S; F )g
2
5.1.2 Aut�omatas de estado �nito no determin��sticos
Esta subsecci�on introduce aut�omatas de estado �nito no determin��sticos y de�ne su
sem�antica, es decir el lenguaje de un aut�omata de estado �nito no determin��stico.
La funci�on de transici�on de un AFD retorna un estado, que implica que para todo
s��mbolo no terminal x y para todos los estados t puede haber s�olo un v�ertice que
empieza en t etiquetado con x. Algunas veces es conveniente tener dos o m�as v�ertices
etiquetados con el mismo s��mbolo terminal a partir de un estado. En estos casos
uno puede utilizar un aut�omata de estados �nitos no determin��stico. Los aut�omatas
de estado �nito no determin��sticos se de�nen como sigue.
De�nici�on 4: Aut�omata de estado �nito no determin��stico, AFN
Un aut�omata de estado �nito no determin��stico(AFN) es una qu��ntupla (X;Q; d;Q0; F ),aut�omata
de es-
tado
�nito
no
deter-
min��stico
donde
� X es el alfabeto de entrada,
� Q es un conjunto �nito de estados,
� d::Q! X ! fQg es la funci�on de transici�on de estados,
� Q0 � Q es el conjunto de estados iniciales,
5.1 Aut�omatas de estado �nito 83
� F � Q es el conjunto de estados aceptadores.
2
Un AFN di�ere de un AFD en que puede haber m�as de un estado inicial y que puede
haber m�as que un posible movimiento para cada estado y s��mbolo de entrada. Este
es un ejemplo de un AFN:
S?>=<89:;EDGFc
@A// a //
b
��
C?>=<89:;/.-,()*+
A?>=<89:; a //
BC
ED
a
oo
B?>=<89:;
c
OO
Observe que este AFN es muy similar al AFD in la anterior secci�on: la �unica dife-
rencia es que hay dos echas salientes etiquetadas con a, a partir del estado A. As��
el AFD se convierte en un AFN.
Formalmente, este AFN se de�ne como M1 = (X;Q; d;Q0; F ) con
X = fa; b; cg
Q = fS;A;B; Cg
Q0 = fSg
F = fCg
donde la funci�on de transici�on de estados d est�a de�nida por
d S a = fCg
d S b = fAg
d S c = fSg
d A a = fS;Bg
d B c = fCg
Nuevamente d es una funci�on parcial, que puede hacerse total a~nadiendo d D x = f g
para todos los estados D y todos los s��mbolos terminales x para los cuales d D x no
est�a de�nido.
Como un AFN puede hacer una elecci�on arbitraria (no determin��stica) para uno
de sus posibles movimientos, tenemos que tener cuidado en de�nir el signi�cado de
cuando una secuencia es aceptada por un AFN. Informalmente, la secuencia w 2 X�
es aceptada por el AFN (X;Q; d;Q0; F ), si es posible, cuando iniciamos el AFN en el
estado a partir de Q0, para terminar en un estado aceptador despu�es de procesar w.�Esta descripci�on operacional de aceptaci�on se formaliza en el predicado nfa accept .
Suponga que tenemos una funci�on, digamos nfa, que re eja el comportamiento del
AFN. Esa es una funci�on que dada una funci�on de transici�on, un conjunto de estados
iniciales y una cadena, retorna todos los posibles estados que pueden alcanzarse des-
pu�es de procesar la cadena empezando en alg�un estado inicial. Entonces el predicado
nfa accept puede expresarse como
nfa accept w (d;Q0; F ) = nfa d Q0 w \ F 6= �
84 Lenguajes regulares
Ahora nos queda encontrar una funci�on nfa d qs del tipo [X ]! fQg que re eje el
comportamiento del AFN. Para listas de longitud 1 tal funci�on, llamada deltas , se
de�ne por
deltas d qs a = fr j q 2 qs; r 2 d q ag
El comportamiento del AFN sobre secuencias-X de longitud arbitraria es consecuen-
cia de este comportamiento de \un paso":
nfa d qs � = qs
nfa d qs (ax) = nfa d (deltas d qs a) x
Sigue que nfa puede escribirse como un foldl.
nfa d qs = foldl (deltas d) qs
Esto concluye la de�nici�on del predicado nfa accept . En resumen hemos derivado
De�nici�on 5: Aceptaci�on por un AFN
La secuencia w 2 X� es aceptada por un AFN (X;Q; d;Q0; F ) si
nfa accept w (d;Q0; F )
donde
nfa accept w (d; qs; fs) = nfa d qs w \ fs 6= �
nfa d qs = foldl (deltas d) qs
deltas d qs a = fr j q 2 qs; r 2 d q ag
2
Usando el predicado nfa accept , el lenguaje de un AFN se de�ne por
De�nici�on 6: Lenguaje de un AFN
Sea el AFN M = (X;Q; d;Q0; F ), el lenguaje de M , Lnfa M , se de�ne por
Lnfa M = fw 2 X� j nfa accept w (d;Q0; F )g
2
Observe que determinar si una lista es o no un elemento del lenguaje de un aut�omata
de estado �nito no determin��stico es computacionalmente caro. Esto es debido al
hecho de que todas las posibles transiciones tienen que intentarse para determinar
si un aut�omata puede o no terminar en un estado aceptador despu�es de leer la
entrada. Determinar si una lista es o no un elemento de un lenguaje de un aut�omata
de estado �nito determin��stico puede ser hecho en un tiempo lineal en la longitud
de la lista de entrada, as��, desde el punto de vista computacional son preferibles los
aut�omatas de estados �nitos determin��sticos. Afortunadamente, para cada aut�omata
de estado �nito no determin��stico existe un aut�omata de estado �nito determin��stico
que acepta el mismo lenguaje. Mostraremos como construir un AFD de un AFN en
la subsecci�on 5.1.4.
5.1 Aut�omatas de estado �nito 85
5.1.3 Implementaci�on
Esta secci�on describe como implementar m�aquinas de estados �nitos. Empezamos
con la implementaci�on de AFDs. Dado un AFD M = (X;Q; d; S;F ), de�nimos dos
tipos de datos:
data StateM = ... deriving Eq
data SymbolM = ...
donde los estados de M (los elementos del conjunto Q) son los constructores de
StateM, y los s��mbolos de M (los elementos del conjunto X) son los constructores
de SymbolM. Adem�as, de�nimos tres valores (uno de los cuales es una funci�on):
start :: StateM
delta :: SymbolM -> StateM -> StateM
finals :: [StateM]
Observe que los primeros dos argumentos de delta han cambiado de lugar: esto se
ha hecho para poder aplicar despu�es la `evaluaci�on parcial'. La funci�on de transici�on
extendida dfa y la funci�on de aceptaci�on dfaAccept se de�nen ahora por:
dfa :: [SymbolM] -> StateM
dfa = foldl (flip delta) start
dfaAccept :: [SymbolM] -> Bool
dfaAccept xs = elem (dfa xs) finals
Dada una lista de s��mbolos [x1,c2,...,xn], el c�alculo de dfa [x1,x2,...,xn] usa
los siguientes estados intermedios:
start, delta x1 start, delta x2 (delta x1 start),...
Esta lista de estados est�a determinada �unicamente por la entrada [x1,x2,..xn] y
el estado inicial.
Como queremos usar los mismos nombres de funciones para diferentes aut�omatas,
introducimos la siguiente clase:
class Eq a => DFA a b where
start :: a
delta :: b -> a -> a
finals :: [a]
dfa :: [b] -> a
dfa = foldl (flip delta) start
dfaAccept :: [b] -> Bool
dfaAccept xs = elem (dfa xs) finals
Observe que las funciones dfa y dfaAccept est�an de�nidas una �unica vez y para
todas las instancias de la clase DFA. Como ejemplo, damos la implementaci�on del
ejemplo AFD (aqu�� llamado MEX) presentado en la subsecci�on anterior.
86 Lenguajes regulares
data StateMEX = A | B | C | S deriving Eq
data SymbolMEX = SA | SB | SC
De esta manera el estado A est�a representado por A, y el s��mbolo a est�a representado
por SA, e igualmente para los otros estados y s��mbolos. El aut�omata se hace una
instancia de la clase DFA como sigue:
instance DFA StateMEX SymbolMEX where
start = S
delta x S = case x of SA -> C
SB -> A
SC -> S
delta SA A = B
delta SC B = C
finals = [C]
Podemos mejorar el desempe~no del aut�omata (funci�on) dfa por medio de la evalua-
ci�on parcial . La idea principal de la evaluaci�on parcial es reemplazar c�alculos que seevaluaci�on
parcial ejecutan a menudo en tiempo de ejecuci�on por un �unico c�alculo simple que se ejecuta
solamente una vez en tiempo de compilaci�on. Un ejemplo simple es el reemplazo de
la expresi�on if True then f1 else f2 por la expresi�on f1. La evaluaci�on parcial
se aplica a aut�omatas �nitos de la siguiente manera.
dfa [x1,x2,...,xn]
=
foldl (flip delta) start [x1,x2,...,xn]
=
foldl (flip delta) (delta x1 start) [x2,...,xn]
=
case x1 of
SA -> foldl (flip delta) (delta SA start) [x2,...,xn]
SB -> foldl (flip delta) (delta SB start) [x2,...,xn]
SC -> foldl (flip delta) (delta SC start) [x2,...,xn]
Todas estas igualdades son pasos de transformaci�on simples para programas fun-
cionales. Observe que el primer argumento de foldl siempre es flip delta, y el
segundo argumento es uno de los cuatro estados S, A, B, o C (el resultado de delta).
Puesto que hay s�olo un n�umero �nito de estados (cuatro, para precisar), podemos
de�nir una funci�on de transici�on para cada estado:
dfaS, dfaA, dfaB, dfaC :: [Symbol] -> State
Cada una de estas funciones es una expresi�on case sobre los posibles s��mbolos de
entrada.
dfaS [] = S
dfaS (x:xs) = case x of SA -> dfaC xs
SB -> dfaA xs
5.1 Aut�omatas de estado �nito 87
SC -> dfaS xs
dfaA [] = A
dfaA (x:xs) = case x of SA -> dfaB xs
dfaB [] = B
dfaB (x:xs) = case x of SC -> dfaC xs
dfaC [] = C
Con esta de�nici�on del aut�omata �nito el n�umero de pasos requeridos para calcular
el valor de dfaS xs para alguna lista de s��mbolos xs se reduce considerablemente.
La implementaci�on de AFNs es similar a la implementaci�on de AFDs. La �unica
diferencia es que la transici�on y las funciones de aceptaci�on tienen que cuidar ahora
de los conjuntos (listas) de estados. Usaremos la siguiente clase, en la que usamos
algunos nombres que tambi�en aparecen en la clase DFA. Es un problema si estas dos
clases est�an en el mismo m�odulo.
class Eq a => NFA a b where
start :: [a]
delta :: b -> a -> [a]
finals :: [a]
nfa :: [b] -> [a]
nfa = foldl (flip deltas) start
deltas :: b -> [a] -> [a]
deltas a = union . map (delta a)
nfaAccept :: [b] -> Bool
nfaAccept xs = intersect (nfa xs) finals /= []
Las funciones de union e intersect se implementan como sigue:
union :: Eq a => [[a]] -> [a]
union = nub . concat
nub :: Eq a => [a] -> [a]
nub = foldr (\x xs -> x:filter (/=x) xs) []
intersect :: Eq a => [a] -> [a] -> [a]
intersect xs ys = intersect' (nub xs)
where intersect' =
foldr (\x xs -> if x `elem` ys then x:xs else xs) []
5.1.4 Construcci�on de un AFD a partir de un AFN
>Es posible expresar m�as lenguajes por medio de aut�omatas �nitos no determin��sticos
que por medio de aut�omatas �nitos determin��sticos? Para cada aut�omata no deter-
min��stico es posible dar un aut�omata de estado �nito determin��stico tal que ambos
88 Lenguajes regulares
aut�omatas acepten el mismo lenguaje, entonces la respuesta a la pregunta ante-
rior es no. Antes de hacer una prueba formal de esta a�rmaci�on, ilustraremos la
construcci�on de un AFD a partir de un AFN en un ejemplo.
Considere el aut�omata de estado �nito no determin��stico correspondiente al ejemplo
de la subsecci�on 5.1.2.
S?>=<89:;EDGFc
@A// a //
b
��
C?>=<89:;/.-,()*+
A?>=<89:; a //
BC
ED
a
oo
B?>=<89:;
c
OO
El no determinismo de este aut�omata aparece en el estado A: los dos arcos salientes
de A tienen como etiqueta una a. Suponga que a~nadimos un nuevo estado D, con
un arco de A a D etiquetado a, y eliminamos los arcos etiquetados a de A a S y
de A a B. Como D es una uni�on de los estados S y B tenemos que unir los arcos
salientes de S y B en arcos salientes de D. Obtenemos el siguiente aut�omata:
S?>=<89:;EDGFc
@A// a //
b
��
C?>=<89:;/.-,()*+
A?>=<89:; a // D?>=<89:;BC@Ab
OO
c
__????????????????
a;c
??����������������
B?>=<89:;
c
OO
Omitimos la prueba de que el lenguaje del aut�omata resultante es el mismo que el
lenguaje del anterior aut�omata. Aunque hay s�olo un arco saliente de A etiquetado
con a, este aut�omata es todav��a no determin��stico: hay dos salientes etiquetadas con
c de D. Aplicamos el mismo procedimiento de arriba. A~nadimos un nuevo estado
E y un arco etiquetado c de D a E, y eliminamos los dos arcos salientes etiquetados
c de D. Como E es una uni�on de los estados C y S tenemos que unir los arcos
salientes de C y S en arcos salientes de E. Obtenemos el siguiente aut�omata:
S?>=<89:;EDGFc
@A// a //
b
��
C?>=<89:;/.-,()*+
A?>=<89:; a // D?>=<89:;BC@Ab
OO
a
=={{{{{{{{{{{{{{{{{{c // E?>=<89:;/.-,()*+
a
FF
c
eeKKKKKKKKKKKKKKKKKKKKKKK
EDBC b@AOOB?>=<89:;
c
OO
Nuevamente, no probamos que el lenguaje de este aut�omata es igual al lenguaje del
anterior aut�omata a~nadiendo el estado E al conjunto de estados aceptadores que
5.1 Aut�omatas de estado �nito 89
hasta ahora consist��a solamente en el estado C. El estado E se a~nade al conjunto
de estados aceptadores porque es la uni�on de un conjunto de estados entre los que
al menos uno pertenece al conjunto de estados aceptadores. Observe que en este
aut�omata para cada estado, todos los arcos salientes se etiquetan diferentemente, es
decir este aut�omata es determin��stico. El AFD construido a partir del AFN arriba
es una qu��ntupla (X;Q; d; S;F ) con
X = fa; b; cg
Q = fS;A;B; C;D;Eg
F = fC;Eg
donde la funci�on de transici�on d se de�ne por
d S a = C
d S b = A
d S c = S
d A a = D
d B c = C
d D a = C
d D b = A
d D c = E
d E a = C
d E b = A
d E c = S
Esta construcci�on se llama la `construcci�on subconjunto'. En general, la construc-
ci�on funciona como sigue. Suponga que M = (X;Q; d;Q0; F ) es un aut�omata
de estado �nito no determin��stico. Entonces el aut�omata de estado �nito M 0 =
(X 0; Q0; d0; Q00; F 0) cuyos componentes se de�nen abajo, acepta el mismo lenguaje.
X 0 = X
Q0 = subs Q
donde subs retorna todos los subconjuntos de un conjunto. Por ejemplo,
subs fA;Bg = ff g; fAg; fA;Bg; fBgg
Para otros componentes de M 0 de�nimos
d0 q a = ft j t 2 d r a; r 2 qg
Q00 = Q0
F 0 = fp j p \ F 6= �; p 2 Q0g
La demostraci�on del siguiente teorema se presenta en la secci�on 5.4.
Teorema 7: AFD � AFN
Para todo aut�omata �nito no determin��stico M existe un aut�omata de estado �ni-
to M 0 tal que
Lnfa M = Ldfa M 0
90 Lenguajes regulares
2
El teorema 7 nos permite cambiar libremente entre AFNs y AFDs. Equipados con
este conocimiento continuamos la exploraci�on de lenguajes regulares en la siguiente
secci�on. Pero primero mostramos que la transformaci�on de un AFN a un AFD es
una instancia de evaluaci�on parcial.
5.1.5 Evaluaci�on parcial de los AFNs
Dado un aut�omata de estado �nito no determin��stico podemos obtener un aut�omata
de estado �nito determin��stico no solo por medio de la construcci�on de arriba, sino
tambi�en por medio de evaluaci�on parcial.
Tal como para la funci�on dfa, podemos calcular como sigue con la funci�on nfa.
nfa [x1,x2,...,xn]
=
foldl (flip deltas) start [x1,x2,...,xn]
=
foldl (flip deltas) (deltas x1 start) [x2,...,xn]
=
case x1 of
SA -> foldl (flip deltas) (deltas SA start) [x2,...,xn]
SB -> foldl (flip deltas) (deltas SB start) [x2,...,xn]
SC -> foldl (flip deltas) (deltas SC start) [x2,...,xn]
Observe que el primer argumento de foldl es siempre flip deltas, y el segundo
argumento es uno de seis conjuntos de estados [S], [A], [B], [C], [B,S], [C,S]
(el posible resultado de deltas) Como s�olo hay una cantidad �nita de resultados
deltas (seis, para ser precisos), podemos de�nir una funci�on de transici�on para cada
estado:
nfaS, nfaA, nfaB, nfaC, nfaBS, nfaCS :: [Symbol] -> [State]
Por ejemplo,
nfaA [] = A
nfaA (x:xs) = case x of
SA -> nfaBS xs
_ -> error "no transition possible"
Cada una de estas funciones es una expresi�on case sobre los posibles s��mbolos de
entrada. Al evaluar parcialmente la funci�on nfa hemos obtenido una funci�on que es
la implementaci�on de un aut�omata de estado �nito determin��stico correspondiente
al aut�omata de estado �nito no determin��stico.
5.2 Gram�aticas Regulares
Esta secci�on de�ne las gram�aticas regulares, un tipo especial de gram�aticas libres
de contexto. La subsecci�on 5.2.1 da la correspondencia entre aut�omatas de estado
�nito no determin��sticos y gram�aticas regulares.
5.2 Gram�aticas Regulares 91
De�nici�on 8: Gram�atica Regular
Una gram�atica regular G es una gram�atica libre de contexto (T;N;R; S) en la cual gram�atica
regulartodas las reglas de producci�on en R son de la forma
A ! xB
A ! x
Con x 2 T � y A;B 2 N . As��, en cada regla hay al menos un no terminal, y si hay
un no terminal presente, �este est�a al �nal. 2
Las gram�aticas regulares como se de�nen aqu�� se llaman algunas veces gram�aticas
regulares a derecha. Existe una de�nici�on sim�etrica para gram�aticas regulares a
izquierda.
De�nici�on 9: Lenguaje Regular
Un lenguaje regular es un lenguaje generado por una gram�atica regular. 2 lenguaje
regularA partir de �esta de�nici�on est�a claro que todo lenguaje regular es libre de contexto.
La pregunta ahora es: >es todo lenguaje libre de contexto tambi�en regular? La
respuesta es: No. Hay lenguajes libres de contexto que no son regulares, un ejemplo
de tales lenguajes es fanbn j n 2 IINg. Para entender esto, se tiene que saber
c�omo probar que un lenguaje no es regular. Debido a su sutileza, pospondremos
este tipo de demostraciones hasta el cap��tulo 9. Hasta aqu�� es su�ciente saber que
los lenguajes regulares forman un subconjunto apropiado de los lenguajes libres de
contexto y que ganaremos de su especialidad en el proceso de reconocimiento.
Una primera similitud entre lenguajes regulares y lenguajes libres de contexto es que
ambos son cerrados bajo la uni�on, concatenaci�on y la estrella de Kleene.
Teorema 10:
Sean L y M lenguajes regulares, entonces
L [M es regular
LM es regular
L� es regular
2
Demostraci�on: Sean GL = (T;NL; RL; SL) y GM = (T;NM; RM ; SM) gram�aticas
regulares para L y M respectivamente, entonces
� Para gram�aticas regulares, siguiendo la bien conocida construcci�on de uni�on
para gram�aticas libres de contexto llegamos a obtener una gram�atica regular;
� Obtenemos una gram�atica regular para LM si reemplazamos, en GL, toda
producci�on de la forma T ! x y T ! � por T ! xSM y T ! SM , respectiva-
mente;
� como L� = f�g [ LL�, es consecuencia de los puntos anteriores que existe una
gram�atica regular para L�.
2
Adem�as de estas propiedades de clausura, los lenguajes regulares son tambi�en ce-
rrados bajo la intersecci�on y complemento. Vea los ejercicios. Esto es remarca-
ble ya que los lenguajes libres de contexto no son cerrados bajo estas operacio-
92 Lenguajes regulares
nes. Recordemos el lenguaje L = L1 \ L2 donde L1 = fanbncm j n;m 2 IINg y
L2 = fanbmcm j n;m 2 IINg.
En cuanto a los lenguajes libres de contexto, puede existir m�as de una gram�atica re-
gular para un lenguaje regular dado y es posible transformar entre s�� estas gram�aticas
regulares. Concluimos esta secci�on con una transformaci�on de gram�aticas:
Teorema 11:
Para toda gram�atica regular G existe una gram�atica regular G0 con s��mbolo inicial
S0 tal que
L G = L G0
y tal que G0 no tiene producciones de la forma U ! V y , and W ! �, con W 6= S0.
En otras palabras: toda gram�atica regular puede transformarse en una forma en la
que cada producci�on tiene una cadena terminal no vac��a en su lado derecho (con
una posible excepci�on para S ! �). 2
Omitimos la demostraci�on de esta transformaci�on, s�olo describimos brevemente la
construcci�on de tal gram�atica regular, e ilustramos la construcci�on con un ejemplo.
Dada una gram�atica regular G, se puede obtener una gram�atica regular que genera
el mismo lenguaje pero no tiene producciones de la forma U ! V y W ! � para
todo U , V , y todo W 6= S como sigue. Primero, considere todos los pares Y , Z de
los no terminales de G tal que Y�
) Z. Eliminar todas las producciones U ! V
de G, y a~nadir producciones Y ! z a la gram�atica, con Z ! z una producci�on
de la gram�atica original, donde z no es un s��mbolo no terminal �unico. Finalmente,
eliminar todas las producciones de la forma W ! � por W 6= S, y para cada
producci�on U ! xW a~nada la producci�on U ! x. El siguiente ejemplo muestra esta
construcci�on.
Considere la siguiente gram�atica regular G.
S ! aA
S ! bB
S ! A
S ! C
A ! bB
A ! S
A ! �
B ! bB
B ! �
C ! c
La gram�atica G0 de la forma deseada se construye en 3 pasos.
Paso 1
Sea G0 igual a G.
Paso 2
Considere todos los pares de no terminales Y y Z. Si Y�
) Z, a~nada las producciones
Y ! z a G0, con Z ! z una producci�on de la gram�atica original, y donde z es un
no terminal que no es �unico. Adem�as, eliminar todas las producciones de la forma
5.2 Gram�aticas Regulares 93
U ! V de G0. En el ejemplo eliminamos las producciones S ! A, S ! C, A! S,
y a~nadimos las producciones S ! bB y S ! � ya que S�
) A, la producci�on S ! c
ya que S�
) C, las producciones A ! aA y A ! bB ya que A�
) S, y �nalmente
la producci�on A ! c ya que A�
) C. Obtenemos la gram�atica con las siguientes
producciones:
S ! aA
S ! bB
S ! c
S ! �
A ! bB
A ! aA
A ! c
A ! �
B ! bB
B ! �
C ! c
Esta gram�atica genera el mismo lenguaje que G, y no tiene producciones de la
forma U ! V . A�un contin�ua la eliminaci�on de producciones de la forma W ! �
para W 6= S.
Paso 3
Elimine todas las producciones de la forma W ! � para W 6= S, y para cada
producci�on U ! xW a~nada la producci�on U ! x. Aplicando esta transformaci�on a
la gram�atica anterior obtenemos la siguiente gram�atica:
S ! aA
S ! bB
S ! a
S ! b
S ! c
S ! �
A ! bB
A ! aA
A ! a
A ! b
A ! c
B ! bB
B ! b
C ! c
Cada producci�on en esta gram�atica est�a en una de las formas deseadas: U ! x o
U ! xV , y el lenguaje que obtenemos de la gram�atica G0 es el mismo que el lenguaje
de la gram�atica G.
94 Lenguajes regulares
5.2.1 Equivalencia entre gram�aticas regulares y aut�omatas �nitos
En la secci�on previa, presentamos a los aut�omatas de estados �nitos. Aqu�� mostra-
mos que las gram�aticas regulares y los aut�omatas de estado �nito no determin��sticos
son los dos lados de una misma moneda.
Probaremos la equivalencia utilizando el teorema 7. La equivalencia consiste de
dos partes formuladas posteriormente en los teoremas 12 y 13. La base para am-
bos teoremas es la correspondencia directa entre una producci�on A ! xB y una
transici�on
A?>=<89:; x // B?>=<89:;
Teorema 12: La gram�atica regular de un AFN
Para todo AFN M existe una gram�atica regular G tal que
Lnfa M = L(G)
2
Demostraci�on: S�olo bosquejaremos la construcci�on, la prueba formal puede encon-
trarse en la bibliograf��a. Sea (X;Q; d; S; F ) un AFD para el AFN M . Construimos
la gram�atica G = (X;Q;R; S) donde
� los terminales de la gram�atica son los del alfabeto de entrada del aut�omata;
� los no terminales de la gram�atica son los estados del aut�omata;
� el estado inicial de la gram�atica es el estado inicial del aut�omata;
� Las producciones de la gram�atica corresponden a las transiciones del aut�omata:
una regla A! xB para cada transici�on
A?>=<89:; x // B?>=<89:;y, una regla A! � para cada estado terminal A.
Formalmente:
R = fA! xB j A;B 2 Q; x 2 X; d A x = Bg
[ fA! � j A 2 Fg
2
Teorema 13: El AFN de una gram�atica regular
Para toda gram�atica regular G existe un aut�omata de estado �nito no determin��stico
M tal que
L(G) = Lnfa M
2
Demostraci�on: Nuevamente, s�olo bosquejaremos la construcci�on, la prueba formal
puede encontrarse en la bibliograf��a. La construcci�on consiste de dos pasos: primero
damos una transformaci�on directa de una gram�atica regular a un aut�omata y luego
transformamos al aut�omata en la forma adecuada.
De una gram�atica a un aut�omata: Sea G = (T;N;R; S) una gram�atica regular sin
producciones de la forma U ! V y W ! � para W 6= S.
Construimos el AFN M = (X;Q; d; fSg; F ) donde
5.3 Expresiones regulares 95
� El alfabeto de entrada del aut�omata son las cadenas terminales no vac��as (!)
que ocurren en las reglas de la gram�atica:
X = fx 2 T+ j A;B 2 N; A! xB 2 Rg
[ fx 2 T+ j A;B 2 N; A! x 2 Rg
� Los estados del aut�omata son los no terminales de la gram�atica extendida con
un nuevo estado Nf .
Q = N [ fNfg
� Las transiciones del aut�omata corresponden a las producciones de la gram�atica:
para cada regla A! xB obtenemos una transici�on
A?>=<89:; x // B?>=<89:;Para cada regla A! x con x no vac��a, obtenemos una transici�on
A?>=<89:; x // NfGFED@ABC
Formalmente: Para todo A 2 N y x 2 X :
d A x = fB j B 2 N; A! xB 2 Rg
[ fNf j A! x 2 Rg
� Los estados �nales del aut�omata son Nf y posiblemente S, si S ! � es una
producci�on de la gram�atica.
F = fNfg [ fS j S ! � 2 Rg
Lnfa M = L(G), debido a la correspondencia directa entre los pasos de derivaci�on
en G y las transiciones de M .
Transformar el aut�omata a una forma adecuada: Existe una peque~na imperfecci�on
en el aut�omata dado anteriormente: la gram�atica y el aut�omata tienen distintos
alfabetos. Esta de�ciencia ser�a remediada por una transformaci�on del aut�omata
que devuelve un aut�omata equivalente con transiciones etiquetadas por elementos
de T (en lugar de T �). La tranformaci�on es relativamente f�acil y se representa en el
diagrama de abajo. Para eliminar la transici�on d q x = q0 donde x = x1x2 : : :xk+1con k > 0 y xi 2 T para todo i, a~nadimos nuevos estados (no �nales) p1; : : : ; pk a
los existentes y nuevas transiciones d q x1 = p1; d p1 x2 = p2; : : : ; d pk xk+1 = q0.
b
qx1x2:::xk+1
b
q0
b
qx1
b
p1x2
b
p2
b
pkxk+1
b
q0
- - - p p p p p p p p p p p p p p p p p p -
Es intuitivamente claro que el aut�omata resultante es equivalente al original. Lleve
a cabo esta transformaci�on para toda transici�on de M d q x = q0 con jxj > 1, para
obtener un aut�omata para G con el mismo alfabeto T de entrada. 2
5.3 Expresiones regulares
Las expresiones regulares son una forma cl�asica y conveniente para describir, por
ejemplo, la estructura de palabras terminales. Esta secci�on de�ne expresiones re-
gulares, el lenguaje de una expresi�on regular, y muestra que expresiones regulares
96 Lenguajes regulares
y gram�aticas regulares son los dos lados de la misma moneda. No discutiremos
la implementaci�on (tipos de datos y funciones de emparejamiento) de expresiones
regulares; las implementaciones pueden encontrarse en la literatura, vea [9, 6].
De�nici�on 14: RET , expresiones regulares sobre el alfabeto T
El conjunto RET de las expresiones regulares sobre el alfabeto T se de�ne inducti-expresi�on
regular vamente como sigue: si R y S son expresiones regulares,
� 2 RET
� 2 RET
a 2 RET
R+ S 2 RET
RS 2 RET
R� 2 RET
son tambi�en expresiones regulares, donde a 2 T . El operador + es asociativo, con-
mutativo, e idempotente; el operador de concatenaci�on, escrito como yuxtaposici�on,
es asociativo y su unidad es �. Formalmente, para toda expresi�on regular R, S y V ,
R+ (S + U) = (R+ S) + U
R+ S = S + R
R+ R = R
R (S U) = (RS)U
R � = R (= � R)
2
Adem�as, el operador asterisco �, enlaza con m�as fuerza que la concatenaci�on, y la
concatenaci�on enlaza con m�as fuerza que +. Ejemplos de expresiones regulares son:
(bc)�+�
� + b(��)
El lenguaje (o la \sem�antica") de una expresi�on regular sobre T es un conjunto
de secuencias-T de�nidas composicionalmente sobre la estructura de las expresiones
regulares. Se de�ne como sigue:
De�nici�on 15: Lenguaje de una expresi�on regular
La funci�on Lre: Lre :: RET ! fT�g devuelve el lenguaje de una expresi�on regular.
Se de�ne inductivamente por:
Lre � = �
Lre � = f�g
Lre b = fbg
Lre (x+ y) = (Lre x) [ (Lre y)
Lre (xy) = (Lre x) (Lre y)
Lre (x�) = (Lre x)�
5.3 Expresiones regulares 97
2
Ya que [ es asociativa, conmutativa, e idempotente, la concatenaci�on de conjuntos
es asociativa con unidad f�g, y la funci�on Lre est�a bien de�nida. Observe que el
lenguaje Lre b� es el conjunto consistente de cero, una o m�as concatenaciones de
b, es decir, Lre b� = (fbg)�. Como ejemplo de lenguaje de una expresi�on regular,
calculamos el lenguaje para la expresi�on regular (�+ bc)d.
Lre ((�+ bc)d)
=
(Lre (� + bc)) (Lre d)
=
(Lre � [ Lre bc)fdg=
(f�g [ (Lre b)(Lre c))fdg
=f�; bcg fdg
=
fd; bcdg
Las expresiones regulares se usan para describir los s��mbolos de un lenguaje. Por
ejemplo, la lista
if p then e1 else e2
contiene seis s��mbolos, tres de los cuales son identi�cadores. Un identi�cador es un
elemento en el lenguaje de la expresi�on regular
letter(letter + digit)�
donde
letter = a+ b + : : :+ z+
A+ B + : : :+ Z
digit = 0+ 1+ : : :+ 9
vea la subsecci�on 2.3.1.
Al iniciar esta secci�on dijimos que las expresiones regulares y las gram�aticas regulares
son formalismos equivalentes. Probaremos despu�es esta a�rmaci�on, pero primero
ilustraremos en un ejemplo la construcci�on de una gram�atica regular a partir de una
expresi�on regular.
Considere la siguiente expresi�on regular.
R = a�+ � + (a+ b)�
Queremos una gram�atica regular G tal que Lre R = L G y otra vez tomamos un
enfoque descendente.
Suponga que el s��mbolo no terminal A genera el lenguaje Lre a�, el no terminal
B genera el lenguaje Lre �, y el no terminal C genera el lenguaje Lre (a+ b)�.
98 Lenguajes regulares
Suponga adem�as que las producciones para A, B, y C satisfacen las condiciones
impuestas sobre gram�aticas regulares. Luego obtenemos una gram�atica regular G
con L G = Lre R de�niendo
S ! A
S ! B
S ! C
Donde S es el s��mbolo inicial de G. Resta construir producciones para los no termi-
nales A, B, y C.
� El no terminal A con producciones
A ! aA
A ! �
genera el lenguaje Lre a�.
� Como Lre � = f�g, el no terminal B con producciones
B ! �
genera el lenguaje f�g.
� El no terminal C con producciones
C ! aC
C ! bC
C ! �
genera el lenguaje Lre (a+ b)�.
Para un ejemplo especi�co no es dif��cil construir una gram�atica regular a partir de
una expresi�on regular. Ahora proporcionamos el resultado general.
Teorema 16: La gram�atica regular de una expresi�on regular
Para toda expresi�on regular R existe una gram�atica regular G tal que
Lre R = L G
2
La demostraci�on de este teorema se da en la secci�on 5.4. La obtenci�on de una
expresi�on regular que genere el mismo lenguaje que una gram�atica regular se hace
por medio de un aut�omata. Dada una gram�atica regular G, podemos usar el teorema
de las secciones previas para obtener un AFD D tal que
L G = Ldfa D
De manera que si podemos obtener una expresi�on regular para un AFD D, hemos
encontrado una expresi�on regular para una gram�atica regular. Para obtener una
5.4 Pruebas 99
expresi�on regular para un AFD D, interpretamos cada estado de D como una ex-
presi�on regular de�nida como la suma de la concatenaci�on de los s��mbolos terminales
salientes con el estado resultante. Para nuestro ejemplo de AFD obtenemos:
S = aC + bA+ cS
A = aB
B = cC
C = �
En general tenemos que :
Teorema 17: La expresi�on regular de una gram�atica regular
Para toda gram�atica regular G existe una expresi�on regular R tal que
L G = Lre R
2
La prueba de este teorema se presenta en la secci�on 5.4.
5.4 Pruebas
Esta secci�on contiene las demostraciones de algunos teoremas dados en este cap��tulo.
Demostraci�on: del Teorema 7.
Suponga que M = (X;Q; d;Q(); F ) es un aut�omata de estado �nito no deter-
min��stico. De�nimos el aut�omata de estado �nito M 0 = (X 0; Q0; d0; Q()0; F 0) como
sigue.
X 0 = X
Q0 = subs Q
Donde subs retorna todos los subconjuntos de un conjunto. Por ejemplo,
subs fA;Bg = ff g; fAg; fA;Bg; fBgg
Para los otros componentes de M 0 de�nimos
d0 q a = ft j t 2 d r a; r 2 qg
Q00 = Q0
F 0 = fp j p \ F 6= �; p 2 Q0g
Tenemos
Lnfa M
= de�nici�on de H
fw j w 2 [X ]; nfa accept w (d;Q0; F )g
= de�nici�on de nfa accept
fw j w 2 [X ]; (nfa d Q0 w \ F ) 6= �g
= de�nici�on de F 0
100 Lenguajes regulares
fw j w 2 [X ]; nfa d Q0 w 2 F 0g
= suponer nfa d Q0 w = dfa d0 Q00 w
fw j w 2 [X ]; dfa d0 Q00 w 2 F 0g
= de�nici�on de dfa accept
fw j w 2 [X ]; dfa accept w (d0; Q00; F 0)g
= de�nici�on de Ldfa
Ldfa M 0
Seguidamente Lnfa M = Ldfa M 0 proporciona
nfa d Q0 w = dfa d0 Q00 w (5.1)
Probamos esta ecuaci�on como sigue.
nfa d Q0 w
= de�nici�on de nfa
foldl (step d) Q0 w
= Q0 = Q00 ; suponer step d = d0
foldl d0 Q00 w
= de�nici�on de dfa
dfa d0 Q00 w
De manera que si
step d = d0
la ecuaci�on (5.1) es v�alida. Esta igualdad proviene del siguiente c�alculo.
d0 q a
= de�nici�on de d'
ft j t 2 d r a; r 2 qg
= de�nici�on de step
step d q a
2
Demostraci�on: del Teorema 16.
La prueba de este teorema es por inducci�on sobre la estructura de las expresiones
regulares. Para los tres casos base argumentamos lo siguiente.
La gram�atica regular sin producciones genera el lenguaje Lre �. La gram�atica
regular con la producci�on S ! � genera el lenguaje Lre �. La gram�atica regular con
producci�on S ! b genera el lenguaje Lre b.
Para los otros tres casos, la hip�otesis de inducci�on es que existe una gram�atica
regular con s��mbolo inicial S1 que genera el lenguaje Lre x, y una gram�atica regular
con s��mbolo inicial S2 que genera el lenguaje Lre y.
Obtenemos una gram�atica regular con s��mbolo inicial S que genera el lenguaje
Lre (x+ y) de�niendo
S ! S1
5.4 Pruebas 101
S ! S2
Obtenemos una gram�atica regular con s��mbolo inicial S que genera el lenguaje
Lre (xy) remplazando, en la gram�atica regular que genera el lenguaje Lre x, to-
da producci�on de la forma T ! a y T ! � por T ! aS2 y T ! S2, respectivamente.
Obtenemos una gram�atica regular con s��mbolo inicial S que genera el lenguaje Lre x�
remplazando en la gram�atica regular que genera el lenguaje Lre x toda producci�on
de la forma T ! a y T ! � por T ! aS y T ! S, y a~nadiendo las producciones
S ! S1 y S ! �, donde S1 es el s��mbolo inicial de la gram�atica regular que genera
el lenguaje Lre x. 2
Demostraci�on: del Teorema 17.
En las secciones 5.1.4 y 5.2.1 hemos mostrado que existe un AFDD = (X;Q; d; S;F )
tal que
L G = Ldfa D
Por lo tanto, si podemos mostrar que si existe una expresi�on regular R tal que
Ldfa D = Lre R
Entonces el teorema es v�alido. Sea D = (X;Q; d; S; F ) un AFD tal que L G =
Ldfa D. De�nimos una expresi�on regular R tal que
Lre R = Ldfa D
Para todo estado q 2 Q de�nimos una expresi�on regular �q, y dejamos que R sea �S.
Obtenemos la de�nici�on de �q combinando todos los pares c y C tal que d q c = C.
�q = if q =2 F
then foldl (+) � [c �C j d q c = C]
else � + foldl (+) � [c �C j d q c = C]
Probamos que Ldfa D = Lre �S.
Ldfa D
= de�nici�on de Ldfa
fw j w 2 [X ]; dfa accept w (d; S; F )g
= de�nici�on de dfa accept
fw j w 2 [X ]; (dfa d S w) 2 Fg
= de�nici�on de dfa
fw j w 2 [X ]; (foldl d S w) 2 Fg
= suponemos
fw j w 2 Lre �Sg
= igualdad para comprensi�on de conjuntos
Lre �S
102 Lenguajes regulares
Queda probar la suposici�on del c�alculo anterior: para w 2 [X ],
(foldl d S w) 2 F � w 2 Lre �S
Probamos una generalizaci�on de esta ecuaci�on, es decir, para un q arbitrario,
(foldl d q w) 2 F � w 2 Lre �q
Esta ecuaci�on se demuestra por inducci�on sobre la longitud de w. Calculamos como
sigue para el caso base w = �.
(foldl d q �) 2 F
� de�nici�on de foldl
q 2 F
� de�nici�on de �q, E abrevia la expresi�on fold
�q = � +E
� de�nici�on de Lre, de�nici�on de �q
� 2 Lre �q
La hip�otesis de inducci�on es que para todas las listas w con jwj � n tenemos
(foldl d q w) 2 F � w 2 Lre �q. Suponga que ax es una lista con longitud n+1.
(foldl d q (ax)) 2 F
� de�nici�on de foldl
(foldl d (d q a) x) 2 F
� hip�otesis de inducci�on
x 2 Lre ( �d q a)
� de�nici�on de �q, D es determin��stico
ax 2 Lre �q
2
resumen
Este cap��tulo trata sobre m�etodos para el reconocimiento de frases de lenguajes re-
gulares, e introduce varios conceptos relacionados a la descripci�on y reconocimiento
de lenguajes regulares. Los lenguajes regulares se usan para describir lenguajes sen-
cillos como `el lenguaje de identi�cadores' y `el lenguaje de las palabras clave', y las
expresiones regulares son convenientes para la descripci�on de lenguajes regulares.
La traducci�on directa de una expresi�on regular en un reconocedor para el lenguaje
de esa expresi�on regular resulta en un reconocedor que es a menudo muy ine�cien-
te. Por medio de aut�omatas de estado �nito (no) determin��sticos construimos un
reconocedor que requiere tiempo lineal en la longitud de la lista de entrada para
reconocer una lista de entrada.
5.5 Ejercicios
Ejercicio 5.1 . Dada una gram�atica regular G para el lenguaje L, construir una gram�atica
regular para L�. /
5.5 Ejercicios 103
Ejercicio 5.2 . Transformar la gram�atica que tiene las siguientes reglas de producci�on en una
gram�atica sin producciones de la forma U ! V and W ! � con W 6= S.
S ! aA
S ! A
A ! aS
A ! B
B ! C
B ! �
C ! cC
C ! a
/
Ejercicio 5.3 . Supongamos que la funci�on de transici�on de estados d de la de�nici�on de un
aut�omata �nito no determin��stico tiene el siguiente tipo:
d : fQg ! X ! fQg
La funci�on d toma un conjunto de estados V y un elemento a, y devuelve el conjunto
de estaod que son alcanzables a partir de V con un arco etiquetado a. De�nimos la
funci�on ndfsa con tipo
(fQg ! X ! fQg)! fQg ! [X ]! fQg
que dada una funci�on d, un conjunto de estados iniciales y una lista de entrada,
devuelve el conjunto de estados en los cuales termina el aut�omata �nito no deter-
min��stico, luego de leer la lista de entrada. /
Ejercicio 5.4 . Demostrar la inversa del Teorema 7: mostrar que para todo aut�omata �nito
determin��stico M existe un aut�omata �nito no determin��stico M 0 tal que
Lda M = Lna M 0
/
Ejercicio 5.5 . Los lenguajes regulares son cerrados bajo complemento. Demostrar esta a�r-
maci�on. Ayuda: construir un aut�omata �nito para L a partir de un aut�omata para
el lenguaje regular L. /
Ejercicio 5.6 . Los lenguajes regulares son cerrados bajo intersecci�on.
1 Demostrar esta a�rmaci�on usando el resultado del ejercicio anterior.
2 Una demostraci�on directa de esta a�rmaci�on es la siguiente:
Sean M1 = (X;Q1; d1; S1; F1) y M2 = (X;Q2; d2; S2; F2) DFAs para los len-
guajes regulares L1 y L2 respectivamente. De�nimos el aut�omata (producto)
M = (X;Q1 � Q2; d; (S1; S2); F1 � F2) con d (q1; q2) x = (d1 q1 x; d2 q2 x)
Ahora demostrar que Ldfa M = Ldfa M1 \ Ldfa M2.
/
Ejercicio 5.7 . De�nir aut�omatas �nitos no determin��sticos que acepten lenguajes iguales a los
lenguajes de las siguientes gram�aticas regulares.
104 Lenguajes regulares
1.
8>>>>><>>>>>:
S ! (A
S ! �
S ! )A
A ! )
A ! (
2.
8>>>>>>>>>><>>>>>>>>>>:
S ! 0A
S ! 0B
S ! 1A
A ! 1
A ! 0
B ! 0
B ! �
/
Ejercicio 5.8 . Describir el lenguaje de las siguientes expresiones regulares.
1. �+ b(��)
2. (bc)�+�
3. a(b�) + c�
/
Ejercicio 5.9 . Demostrar que si R, S, y T son expresiones regulares arbitrarias entonces valen
las siguientes equivalencias.
Lre (R(S + T )) = Lre (RS + RT )
Lre ((R+ S)T ) = Lre (RT + ST )
/
Ejercicio 5.10 . Dar expresiones regulares S y R tales que
Lre (RS) = Lre (SR)
Lre (RS) 6= Lre (SR)
/
Ejercicio 5.11 . Dar expresiones regulares V y W , con Lre V 6= Lre W , tal que para todas las
expresiones regulares R y S con S 6= �
Lre (R(S + V )) = Lre (R(S +W ))
V y W pueden expresarse en t�erminos de R y S. /
Ejercicio 5.12 . Dar una expresi�on regular para el lenguaje que consiste de todas las listas
de ceros y unos tales que el segmento 01 no ocurre en ninguna parte de la lista.
Ejemplos de frases de este lenguaje son 1110 y 000. /
Ejercicio 5.13 . Dar gram�aticas regulares que generan el lenguaje de las siguientes expresiones
regulares.
1. ((a+ bb)�+ c)�
5.5 Ejercicios 105
2. a�+ b�+ ab
/
Ejercicio 5.14 . Dar expresiones regulares cuyo lenguaje sea el mismo que el lenguaje generado
por las siguientes gram�aticas regulares.
1.
8>>>>>>>>>>>>>>>><>>>>>>>>>>>>>>>>:
S ! bA
S ! aC
S ! �
A ! bA
A ! �
B ! aC
B ! bB
B ! �
C ! bB
C ! b
2.
8>>>>><>>>>>:
S ! 0S
S ! 1T
S ! �
T ! 0T
T ! 1S
/
Ejercicio 5.15 . Para cada una de las siguientes expresiones regulares construir un aut�omata
�nito no determin��stico que acepte las frases del lenguaje. Transformar los aut�omatas
�nitos no determin��sticos en aut�omatas �nitos determin��sticos.
1. a�+ b�+ (ab)
2. (1+ (11) + 0)�(00)�
/
Ejercicio 5.16 . De�nir expresiones regulares para los lenguajes de los siguientes aut�omatas
�nitos determin��sticos.
1. El estado inicial es S.
S?>=<89:;/.-,()*+ a //
EDGFb
@A// A?>=<89:; b //
EDGFa
@A// B?>=<89:;/.-,()*+EDGF
a;b
@A//
2. El estado inicial es S.
S?>=<89:;/.-,()*+ a //
EDGFb
@A// A?>=<89:; b //BC@A
b
OOB?>=<89:;BC@A
b
OO
/
Cap��tulo 6
Composicionalidad
introducci�on
Muchas funciones recursivas siguen un patr�on com�un de recursi�on. Estos patro-
nes comunes de recursi�on pueden ser convenientemente capturados por funciones
de orden superior. Por ejemplo: muchas funciones recursivas de�nidas sobre listas
pueden verse como instancias de la funci�on de alto orden, foldr. Es posible de�-
nir una funci�on tal como foldr para todo un rango de tipos de datos adem�as de
listas. Tales funciones se llaman composicionales. Las funciones composicionales
sobre tipos de datos se de�nen en t�erminos de �algebras de acciones sem�anticas que
corresponden a los constructores del tipo de dato. Las funciones composicionales
pueden usarse generalmente para de�nir la sem�antica de los constructos de los len-
guajes de programaci�on. Tales sem�anticas se denominan sem�anticas algebraicas. La
sem�antica algebraica usa a menudo �algebras que son funciones de tuplas a tuplas.
Estas funciones pueden ser vistas como c�alculos que leen valores de un componente
del dominio y escriben valores en un componente del codominio. Los primeros de
estos valores se llaman atributos heredados y los segundos atributos sintetizados.
Los atributos pueden ser tanto heredados como sintetizados. Como explicamos en
la Secci�on 2.4.1, existe una importante relaci�on entre gram�aticas y composicionali-
dad. A cada gram�atica que describe la sintaxis concreta de un lenguaje, se puede
asociar un tipo de dato (posiblemente mutuamente recursivo) que describe la sinta-
xis abstracta del lenguaje. Las funciones composicionales sobre estos tipos de datos
se conocen como funciones determinadas por la sintaxis.
objetivos
Despu�es de estudiar este cap��tulo y resolver los ejercicios:
� sabr�as c�omo generalizar constructores de un tipo de datos para un �algebra;
� sabr�as c�omo escribir funciones composicionales, tambi�en conocidas como folds,
sobre tipos de datos (posiblemente mutuamente recursivos);
� comprender�as las ventajas de usar folds, y habr�a visto que muchos problemas
pueden ser resueltos con un fold;
� sabr�as que un fold aplicado al �algebra de constructores es la funci�on de iden-
tidad;
� ver�as nociones de fusi�on y deforestaci�on;
� sabr�as c�omo escribir c�odigo determinado por la sintaxis;
� entender�as la conexi�on entre tipos de datos, sintaxis abstracta y sintaxis con-
107
108 Composicionalidad
creta.
� comprender�as la idea de atributos heredados y sintetizados;
� podr�as asociar los atributos heredados y sintetizados con las diferentes alterna-
tivas de tipos de datos (posiblemente mutuamente recursivos) (o los distintos
no terminales de la gram�atica);
� podr�as de�nir sem�anticas algebraicas en t�erminos de c�odigo composicional (o
determinado por la sintaxis) de�nido usando �algebras de c�alculos que usan
atributos sintetizados e heredados.
organizaci�on
El cap��tulo est�a organizado como sigue. La secci�on 6.1 muestra c�omo se de�nen las
funciones recursivas composicionales en las listas prede�nidas usando una funci�on
similar a foldr. Tambi�en se muestra c�omo se hace lo mismo con listas de�nidas
por el usuario y ujos. La secci�on 6.2 muestra c�omo de�nir funciones recursivas
composicionales en diferentes tipos de �arboles. La secci�on 6.3 de�ne sem�anticas
algebraicas. En la secci�on 6.4 se muestra la utilidad de la sem�antica algebraica
presentando un evaluador de expresiones, un int�erprete de expresiones que utiliza
una pila y, un compilador de expresiones a una m�aquina de pila. �Estas s�olo di�eren
en la forma en que manejan las expresiones b�asicas (las variables y las de�niciones
locales se manejan de la misma forma). Los tres ejemplos usan un �algebra de c�alculos
que puede leer valores de un ambiente y escribir valores a un ambiente que enlaza
nombres a valores. En una segunda versi�on del evaluador de expresiones se hace
m�as expl��cito la manera de usar los atributos sintetizados y heredados empleando
tuplas. La secci�on 6.5 presenta un ejemplo relativamente complejo del uso de tuplas
en combinaci�on con composicionalidad, tratando el problema del �ambito de variables
cuando se compila lenguajes estructurados en bloque.
6.1 Listas
En esta secci�on se introduce las funciones composicionales sobre un tipo bien cono-
cido: listas. Se de�nen funciones composicionales sobre el tipo de datos prede�nido
[a] para listas (secci�on 6.1.1), sobre un tipo de datos de�nido por el usuario List a
(secci�on 6.1.2), y en ujos o listas in�nitas (secci�on 6.1.3). Tambi�en mostraremos
c�omo construir un �algebra que corresponde directamente a un tipo de datos.
6.1.1 Listas prede�nidas
El tipo de datos lista es quiz�as el ejemplo m�as importante de un tipo de datos. Una
lista es, o la lista vac��a [], o una lista no vac��a (x:xs) que consiste de un elemento x
a la cabeza de la lista, seguido por una cola xs que es a su vez una lista. As��, el tipo
[x] es recursivo. En Hugs, el tipo para listas [x] est�a prede�nido. La de�nici�on
informal de lo anterior corresponde al siguiente (seudo)tipo de datos.
data [x] = x : [x] | []
Muchas funciones recursivas sobre listas son muy parecidas. Calcular la suma de los
elementos de una lista de enteros (sumL, donde la L denota que es una funci�on de
6.1 Listas 109
suma de�nida para listas) es muy similar a calcular el producto de los elementos de
una lista de enteros (prodL).
sumL, prodL :: [Int] -> Int
sumL (x:xs) = x + sumL xs
sumL [] = 0
prodL (x:xs) = x * prodL xs
prodL [] = 1
La funci�on sumL reemplaza al constructor de lista (:) por (+) y al constructor []
por 0 y la funci�on prodL reemplaza al constructor (:) por (*) y a [] por 1. F��jese
que hemos reemplazado el constructor (:) (un constructor con dos variables) por
operadores binarios (+) y (*) (es decir, funciones con dos variables) y el constructor
[] (un constructor con cero variables) por constantes 0 y 1 (es decir, funciones con
cero variables). La similitud entre las de�niciones de las funciones sumL y prodL
puede ser capturada por la siguiente funci�on recursiva de orden superior foldL, que
no es sino una versi�on no curri�cada de la conocida funci�on foldr. No confunda
foldL con la funci�on foldl del preludio de Haskell que funciona al rev�es.
foldL :: (x -> l -> l,l) -> [x] -> l
foldL (op,c) = fold where
fold (x:xs) = op x (fold xs)
fold [] = c
La funci�on foldL reemplaza recursivamente el constructor (:) por un operador op
y el constructor [] por una constante c. Ahora podemos usar foldL para calcular
la suma y multiplicaci�on de los elementos de una lista de enteros como sigue:
? foldL ((+),0) [1,2,3,4]
10
? foldL ((*),1) [1,2,3,4]
24
El par (op,c) es a menudo llamado un �algebra de lista. M�as precisamente, un
�algebra de lista consiste en un tipo l (el soporte del �algebra), un operador binario
op de tipo x->l->l y una constante c de tipo l. F��jese que un tipo (como Int)
puede ser el soporte de un �algebra de lista en m�as de una forma (por ejemplo usando
((+),0) y ((*),1). Aqu�� tienes otro ejemplo de c�omo convertir Int en el soporte
de un �algebra de lista.
? foldL (\_ n -> n+1,0) [1,2,3,4]
4
Esta �algebra de lista no toma en cuenta el valor de la cabeza de una lista, e incre-
menta el resultado as�� obtenido en uno . Corresponde a la funci�on sizeL de�nida
por:
sizeL :: [x] -> Int
sizeL (_:xs) = 1 + sizeL xs
sizeL [] = 0
F��jate que el tipo de sizeL es m�as general que el tipo de sumL y prodL. El tipo de
los elementos de la lista no tiene un papel.
110 Composicionalidad
6.1.2 Listas de�nidas por el usuario
En esta subsecci�on presentaremos un ejemplo de una funci�on fold de�nida sobre un
tipo de datos distinto al de las listas prede�nidas. Para hacerlo simple rehacemos el
ejemplo de las listas de�nidas por el usuario.
data List x = Cons x (List x) | Nil
Las listas de�nidas por el usuario se de�nen de la misma forma que los prede�ni-
dos. Los constructores (:) y [] se reemplazan por constructores Cons y Nil. Los
siguientes son los tipos de los constructores Cons y Nil.
? :t Cons
Cons :: a -> List a -> List a
? :t Nil
Nil :: List a
El tipo del �algebra ListAlgebra que corresponde al tipo de datos List sigue inme-
diatamente a la estructura de ese tipo de datos.
type ListAlgebra x l = (x -> l -> l,l)
El lado izquierdo de la de�nici�on se obtiene del lado izquierdo del tipo de datos
como sigue: el su�jo Algebra se a~nade al �nal del nombre de tipo List y la variable
de tipo l se a~nade al �nal de la parte izquierda de la de�nici�on de tipo. El lado
derecho de la de�nici�on de tipo se obtiene del lado derecho de la de�nici�on del tipo
de datos como sigue: todos los constructores con el valor List x se remplazan por
funciones con valor l que tienen la misma cantidad de argumentos (si los hubiera)
que los constructores correspondientes. Los tipos de los argumentos de constructores
recursivos (es decir argumentos de tipo List x) se reemplazan por argumentos de
funciones recursivas (es decir, argumentos de tipo l). No se cambia los tipos de
los otros argumentos. De igual manera, la de�nici�on de una funci�on fold puede ser
generada autom�aticamente a partir de la de�nici�on de datos.
foldList :: ListAlgebra x l -> List x -> l
foldList (cons,nil) = fold where
fold (Cons x xs) = cons x (fold xs)
fold Nil = nil
Los constructores Cons y Nil a la izquierda de la de�nici�on de la funci�on local fold
se reemplazan en la derecha por las funciones cons y nil. La funci�on fold se invoca
recursivamente para que act�ue en todos los argumentos de constructores recursivos
de tipo List x para producir un resultado del tipo l como lo requieren las funciones
del �algebra (en este caso Cons y cons tienen un argumento recursivo de este tipo).
No se cambian los otros argumentos. Las funciones recursivas sobre listas de�nidas
por el usuario que est�en de�nidas usando foldList, se llaman composicionales.
Cada �algebra de�ne una funci�on composicional �unica. A continuaci�on presentamos
tres ejemplos de funciones composicionales que corresponden a los ejemplos de la
secci�on 6.1.1.
sumList, prodList :: List Int -> Int
6.1 Listas 111
sumList = foldList ((+),0)
prodList = foldList ((*),1)
sizeList :: List x -> Int
sizeList = foldList (const (1+),0)
Vale la pena mencionar una ListAlgebra particular: la ListAlgebra trivial que
reemplaza Cons por Cons y Nil por Nil. Esta �algebra de�ne la funci�on identidad
para listas de�nidas por el usuario.
idListAlgebra :: ListAlgebra x (List x)
idListAlgebra = (Cons,Nil)
idList :: List x -> List x
idList = foldList idListAlgebra
? idList (Cons 1 (Cons 2 Nil))
Cons 1 (Cons 2 Nil)
6.1.3 Listas in�nitas
En esta secci�on consideraremos ujos (o listas in�nitas)
data Stream x = And x (Stream x)
El siguiente es un ejemplo est�andar de un ujo: la lista in�nita de los n�umeros
�bonacci.
fibStream :: Stream Int
fibStream = And 0 (And 1 (restOf fibStream)) where
restOf (And x stream@(And y _)) = And (x+y) (restOf stream)
El tipo del �algebra StreamAlgebra, y la funci�on fold foldStream pueden ser gene-
radas autom�aticamente a partir del tipo de datos Stream.
type StreamAlgebra x s = x -> s -> s
foldStream :: StreamAlgebra x s -> Stream x -> s
foldStream and = fold where
fold (And x xs) = and x (fold xs)
F��jese que este �algebra tiene un solo componente porque Stream tiene solamente
un constructor. Por la misma raz�on, se de�ne la funci�on fold usando solamente
una ecuaci�on. El siguiente ejemplo utiliza una funci�on composicional sobre ujos
de�nidos por el usuario. �Esta calcula el primer elemento de un ujo mon�otono que
es mayor o igual que un valor dado.
firstGreaterThan :: Ord x => x -> Stream x -> x
firstGreaterThan n = foldStream (\x y -> if x>=n then x else y)
112 Composicionalidad
6.2 �Arboles
Ahora que hemos visto c�omo generalizar la funci�on foldr sobre listas prede�nidas
o funciones composicionales sobre listas de�nidas por el usuario y ujos, explicamos
a continuaci�on otra clase com�un de tipos de datos: �arboles. En las subsecciones
siguientes explicamos cuatro clases diferentes de �arboles:
� �arboles binarios;
� �arboles para emparejamientos de par�entesis;
� �arboles de expresiones
� y, �arboles generales
Adem�as, mencionaremos brevemente los conceptos de fusi�on y deforestaci�on.
6.2.1 �Arboles binarios
Un �arbol binario es o un nodo donde el �arbol se divide en dos sub �arboles o una
hoja que guarda un valor.
data BinTree x = Bin (BinTree x) (BinTree x) | Leaf x
Se puede generar autom�aticamente el �algebra que corresponde al tipo BinTreeAlgebra
y la funci�on fold foldBinTree a partir del tipo de datos. F��jese que Bin tiene dos
argumentos recursivos y que Leaf tiene un argumento no recursivo.
type BinTreeAlgebra x t = (t -> t -> t,x -> t)
foldBinTree :: BinTreeAlgebra x t -> BinTree x -> t
foldBinTree (bin,leaf) = fold where
fold (Bin l r) = bin (fold l) (fold r)
fold (Leaf x) = leaf x
En el tipo BinTreeAlgebra, la parte bin del �algebra tiene dos argumentos de tipo t
y la parte leaf del �algebra tiene un argumento de tipo x. Similarmente, en la funci�on
foldBinTree, la funci�on local fold se llama recursivamente con los dos argumentos
del bin y no se llama con el argumento de leaf. Ahora podemos de�nir funciones
composicionales sobre �arboles binarios de una manera muy semejante a la que se
utiliz�o para de�nirlas en listas. En el siguiente ejemplo la funci�on sizeBinTree
calcula el tama~no de un �arbol binario.
sizeBinTree :: BinTree x -> Int
sizeBinTree = foldBinTree ((+),const 1)
?sizeBinTree (Bin (Bin (Leaf 3) (Leaf 7)) (Leaf 11))
3
Si el �arbol consiste de una hoja, entonces el sizeBinTree ignora el valor de la hoja
y retorna 1 como tama~no del �arbol. Si el �arbol consiste de dos sub�arboles, entonces
el sizeBinTree retorna la suma de los tama~nos de esos sub�arboles como tama~no
del �arbol. De forma similar se pueden de�nir las funciones para calcular la suma
y el producto de enteros en las hojas de un �arbol binario. Basta de�nir acciones
sem�anticas apropiadas bin y leaf sobre un tipo t (en este caso Int) que corresponda
a los constructores sint�acticos Bin y Leaf del tipo de dato BinTree.
6.2 �Arboles 113
6.2.2 �Arboles para emparejamiento de par�entesis
En la secci�on 3.3.1 de�nimos el tipo de datos Parentheses para el emparejamiento
de par�entesis.
data Parentheses = Match Parentheses Parentheses
| Empty
Por ejemplo, la frase ()() de la sintaxis concreta para el emparejamiento de par�entesis
se representa por el valor Match Empty (Match Empty Empty) en la sintaxis abs-
tracta Parentheses. Recuerde que la sintaxis abstracta ignora los s��mbolos termi-
nales de los par�entesis de la sintaxis concreta.
De la misma forma que se hizo para listas y �arboles binarios, ahora podemos de-
�nir un �algebra ParenthesesAlgebra y una funci�on fold foldParentheses que
puede ser usada para calcular la profundidad (depthParentheses) y un ancho
(widthParentheses) del emparejamiento de par�entesis en una forma composicio-
nal. La profundidad de una cadena s de emparejamiento de par�entesis es la mayor
cantidad de par�entesis no emparejados que ocurre en una subcadena de s. Por
ejemplo, la profundidad de la cadena ((()))() es 3. El ancho de una cadena s de
emparejamiento de par�entesis es la cantidad de subcadenas de emparejamiento de
par�entesis que no son una subcadena de una cadena circundante del emparejamien-
to de par�entesis. Por ejemplo, el ancho de la cadena ((()))() es 2. Las funciones
composicionales sobre los tipos de datos que describen la sintaxis abstracta de un
lenguaje se llamadan determinadas por la sintaxis.
type ParenthesesAlgebra m = (m -> m -> m,m)
foldParentheses :: ParenthesesAlgebra m -> Parentheses -> m
foldParentheses (match,empty) = fold where
fold (Match l r) = match (fold l) (fold r)
fold Empty = empty
depthParenthesesAlgebra :: ParenthesesAlgebra Int
depthParenthesesAlgebra = (\x y -> max (1+x) y,0)
widthParenthesesAlgebra :: ParenthesesAlgebra Int
widthParenthesesAlgebra = (\_ y -> 1+y,0)
depthParentheses, widthParentheses :: Parentheses -> Int
depthParentheses = foldParentheses depthParenthesesAlgebra
widthParentheses = foldParentheses widthParenthesesAlgebra
parenthesesExample = Match (Match (Match Empty Empty) Empty)
(Match Empty
(Match (Match Empty Empty)
Empty
) )
? depthParentheses parenthesesExample
3
114 Composicionalidad
? widthParentheses parenthesesExample
3
Este ejemplo revela que la sintaxis abstracta no se presta muy bien a la interpre-
taci�on por parte de los humanos. >Cu�al es la representaci�on concreta del ejemplo
de emparejamiento de par�entesis representado por parenthesesExample? Da la ca-
sualidad de ser ((()))()(()). Afortunadamente, podemos escribir f�acilmente un
programa que calcule una representaci�on concreta a partir de otra abstracta. Sa-
bemos exactamente qu�e terminales hemos borrado al pasar de la sintaxis concreta
a una sintaxis abstracta. El �algebra usada por la funci�on a2cParentheses simple-
mente reinserta aquellos terminales que hemos borrado. Note que a2cParentheses
no trata con la distribuci�on de espacios en blanco, sangrado y separadores de l��neas.
Para un ejemplo sencillo, la disposici�on no es realmente importante. Para ejemplos
grandes la disposici�on s�� es muy importante: puede usarse para permitir que las
representaciones concretas luzcan bien.
a2cParenthesesAlgebra :: ParenthesesAlgebra String
a2cParenthesesAlgebra = (\xs ys -> "("++xs++")"++ys,"")
a2cParentheses :: Parentheses -> String
a2cParentheses = foldParentheses a2cParenthesesAlgebra
? a2cParentheses parenthesesExample
((()))()(())
Este ejemplo muestra que una computadora puede f�acilmente interpretar la sintaxis
abstracta (algo que presenta di�cultades a los humanos). A la inversa, los humanos
podemos f�acilmente interpretar la sintaxis concreta (Lo que presenta di�cultades a
las computadoras). Realmente nos gustar��a que las computadoras tambi�en puedan
interpretar la sintaxis concreta. Es aqu�� donde se introduce el an�alisis sint�actico:
pues la utilidad de los analizadores sint�acticos es precisamente calcular una repre-
sentaci�on abstracta a partir de su representaci�on concreta.
Considere nuevamente las funciones parens y nesting de la Secci�on 3.3.1
open = symbol '('
close = symbol ')'
parens :: Parser Char Parentheses
parens = f <$> open <*> parens <*> close <*> parens
<|> succeed Empty
where f a b c d = Match b d
nesting :: Parser Char Int
nesting = f <$> open <*> nesting <*> close <*> nesting
<|> succeed 0
where f a b c d = max (1+b) d
La funci�on nesting podr��a haber sido de�nida usando la funci�on parens y un fold:
nesting' :: Parser Char Int
nesting' = depthParentheses <$> parens
6.2 �Arboles 115
(Recuerde que depthParentheses ha sido de�nida como fold) Las funciones nesting
y nesting' devuelven exactamente el mismo resultado. La funci�on nesting es la
fusi�on de la funci�on fold con el parser parens a partir de la funci�on nesting'.
Usando leyes para analizadores sint�acticos y folds (que no tenemos y que no daremos)
podemos probar que las dos funciones son iguales.
F��jese que la funci�on nesting' primero construye un �arbol mediante la funci�on
parens, y luego la aplana mediante el fold. La funci�on nesting nunca construye un
�arbol y es por lo tanto preferible si se busca e�ciencia. Por otro lado: en la funci�on
nesting' reusamos el analizador parens y la funci�on depthParentheses, en la
funci�on nesting tenemos que escribir nuestro propio analizador, y convencernos
que est�e correcta. Por lo tanto si buscamos \e�ciencia en la programaci�on", es
preferible la funci�on nesting'. Para tener el mejor resultado posible, nos gustar��a
escribir la funci�on nesting' y que nuestro compilador descubra que es mejor usar
la funci�on nesting en los c�alculos. La transformaci�on autom�atica de la funci�on
nesting' en la funci�on nesting se llama deforestaci�on (se eliminan los �arboles).
Algunos (muy pocos) compiladores son lo su�cientemente astutos para ejecutar esta
transformaci�on autom�aticamente.
6.2.3 �Arboles de expresi�on
La gram�atica de emparejamiento de par�entesis s�olo cuenta con un no terminal. Por
lo tanto su sintaxis abstracta puede ser descrita por un solo tipo de datos. En esta
secci�on examinamos nuevamente la gram�atica de expresiones de la secci�on 3.5.
E ! T
E ! E + T
T ! F
T ! T * F
F ! (E)
F ! Digs
Esta gram�atica tiene tres no terminales E, T y F. Usando el enfoque de la secci�on
2.6 transformaremos los no terminales en tipos de datos.
data E = E1 T | E2 E T
data T = T1 F | T2 T F
data F = F1 E | F2 Int
donde hemos traducido Digs por el tipo Int. Puesto que E usa T, T usa F y F usa
E, estos tres tipos son mutuamente recursivos. El tipo de datos principal de los
tres tipos de datos es aquel que correspondiente al s��mbolo inicial E. Puesto que los
tipos de datos son mutuamente recursivos, el tipo del �algebra EAlgebra consiste de
tres tuplas de funciones y tres soportes (el soporte principal es, como siempre, el
que corresponde al principal tipo de datos y es por lo tanto el que corresponde al
s��mbolo inicial).
type EAlgebra e t f = ((t -> e,e -> t -> e)
,(f -> t,t -> f -> t)
116 Composicionalidad
,(e -> f,Int -> f)
)
Finalmente la funci�on foldE para E tambi�en recorre T y F, as�� usa tres funciones
locales mutuamente recursivas.
foldE :: EAlgebra e t f -> E -> e
foldE ((e1,e2),(t1,t2),(f1,f2)) = fold where
fold (E1 t) = e1 (foldT t)
fold (E2 e t) = e2 (fold e) (foldT t)
foldT (T1 f) = t1 (foldF f)
foldT (T2 t f) = t2 (foldT t) (foldF f)
foldF (F1 e) = f1 (fold e)
foldF (F2 n) = f2 n
Ahora podemos usar foldE para escribir un evaluador de expresiones determinado
por la sintaxis evalE. En el �algebra usada en el foldE, todos los variables tipos e,
f y t est�an instanciados con Int.
evalE :: E -> Int
evalE = foldE ((id,(+)),(id,(*)),(id,id))
exE = E2 (E1 (T2 (T1 (F2 2)) (F2 3))) (T1 (F2 1))
? evalE exE
7
Una vez m�as el ejemplo nos muestra que la sintaxis abstracta no puede ser f�acilmente
interpretada por los humanos. La siguiente funci�on a2cE realiza esta tarea para
nosotros.
a2cE :: E -> String
a2cE = foldE ((e1,e2),(t1,t2),(f1,f2))
where e1 = \t -> t
e2 = \e t -> e ++ "+" ++ t
t1 = \f -> f
t2 = \t f -> t ++ "*" ++ f
f1 = \e -> "(" ++ e ++ ")"
f2 = \n -> show n
? a2cE exE
"2*3+1"
6.2.4 �Arboles generales
Un �arbol general consiste de un nodo que contiene un valor, y es donde el �arbol
se divide en una lista de sub�arboles. F��jese que esta lista puede estar vac��a (en tal
caso, por supuesto s�olo tiene inter�es el valor del nodo). El tipo TreeAlgebra y la
funci�on foldTree pueden, como de costumbre, generarse autom�aticamente a partir
de la de�nici�on del tipo de datos.
6.2 �Arboles 117
data Tree x = Node x [Tree x]
type TreeAlgebra x a = x -> [a] -> a
foldTree :: TreeAlgebra x a -> Tree x -> a
foldTree node = fold where
fold (Node x gts) = node x (map fold gts)
F��jese que el constructor Nodo tiene una lista de argumentos recursivos. Por lo tanto
la funci�on nodo del �algebra tiene una lista correspondiente de argumentos recursivos.
Usando la funci�on map se invoca recursivamente la funci�on local fold en cada uno
de los elementos de la lista.
Se puede calcular la suma de los valores en los nodos de un �arbol general como sigue:
sumTree :: Tree Int -> Int
sumTree = foldTree (\x xs -> x + sum xs)
El c�alculo del producto de los valores en los nodos de un �arbol general y el c�alculo
del tama~no de un �arbol general se puede realizar de una forma similar.
6.2.5 E�ciencia
Un fold toma un valor de un tipo de datos y reemplaza sus constructores por fun-
ciones. Si la evaluaci�on de cada una de estas funciones sobre sus argumentos toma
un tiempo constante, la evaluaci�on del fold toma un tiempo lineal con respecto a la
cantidad de constructores de su argumento. Sin embargo algunas funciones requie-
ren m�as de un tiempo constante para la evaluaci�on. Por ejemplo, la concatenaci�on
de listas es lineal en su argumento izquierdo. Y de ello se desprende que si de�nimos
la funci�on reverse mediante:
reverse :: [a] -> [a]
reverse = foldL (\x xs -> xs ++ [x],[])
entonces la funci�on reverse toma un tiempo cuadr�atico en la longitud de su argu-
mento lista. As��, las funciones fold son a menudo funciones e�cientes, pero si las
funciones en el �algebra no son constantes, la funci�on fold generalmente no es lineal.
Frecuentemente un fold no lineal de este tipo puede transformarse en una funci�on
m�as e�ciente. Una t�ecnica que puede usarse en tal caso es la t�ecnica de acumulaci�on
de par�ametros. Por ejemplo, para la funci�on reverse tenemos:
reverse x = reverse' x []
reverse' :: [a] -> [a] -> [a]
reverse' [] ys = ys
reverse' (x:xs) ys = reverse' xs (x:ys)
La evaluaci�on de reverse' xs toma un tiempo lineal en la longitud de xs
Ejercicio 6.1 . De�nir el tipo de un �algebra y una funci�on fold para el siguiente tipo de datos.
data LNTree a b = Leaf a
j Node (LNTree a b) b (LNTree a b)
/
118 Composicionalidad
Ejercicio 6.2 . De�nir las siguientes funciones como folds sobre el tipo de datos BinTree, ver
la secci�on 6.2.1.
1. height, que devuelve la altura de un �arbol.
2. flatten, que devuelve la secuencia de los valores de las hojas en el orden de
izquierda a derecha.
3. maxBinTree, que devuelve el valor m�aximo de las hojas.
4. sp, que devuelve la longitud del camino m�as corto.
5. mapBinTree, que aplica una funci�on a todos los elementos de las hojas.
6. replace, que dado un �arbol y un elemento m, reemplace los elementos de las
hojas por m.
/
Ejercicio 6.3 . Un camino a lo largo de un �arbol binario describe una ruta desde la ra��z del
�arbol a alguna hoja. Elegimos representar caminos por secuencias de Directiones:
data Direction = Left | Right
de tal forma que tomar el sub�arbol izquierdo de un nodo interno se codi�car�a como
Left tomar el sub�arbol derecho se codi�car�a con Right.
1. Dar una funci�on composicional allPaths que produce todos los caminos de
un dado �arbol.
2. Un camino en un �arbol lleva a una �unica hoja. De�nir una funci�on composi-
cional path2Value que, dados un �arbol y un camino en el �arbol, de el elemento
en la hoja �unica.
/
Ejercicio 6.4 . Este ejercicio es sobre resistencias. Hay algunas resistencias b�asicas con capaci-
dad �ja (punto otante), y dadas dos resistencias, pueden ponerse en paralelo (:|:)
o en serie (:*:).
1. De�nir el tipo de datos Resist para representar resistencias. Tambi�en de�nir
el tipo de datos ResistAlgebra y la funci�on foldResist correspondiente.
2. de�nir una funci�on composicional result que determine la capacidad de una
resistencia. (Recordar las reglas 1
r= 1
r1+ 1
r2and r = r1 + r2.)
/
6.3 Sem�anticas algebraicas 119
6.3 Sem�anticas algebraicas
A cada tipo de datos (posiblemente mutuamente recursivos) se puede asociar el tipo
de un �algebra y una funci�on fold. El �algebra es una tupla (un componente para cada
tipo de datos) de tuplas (un componente por cada constructor del tipo de datos)
de acciones sem�anticas. El �algebra hace uso de un conjunto de soportes auxiliares
(uno para cada tipo de datos). Uno de ellos (el correspondiente al tipo de datos) es
el soporte principal del �algebra. La funci�on fold reemplaza recursivamente los cons-
tructores sint�acticos de los tipos de datos por acciones sem�anticas correspondientes
del �algebra. Las funciones de�nidas en t�erminos de una funci�on fold y un �algebra
se llama funciones composicionales. Hay un �algebra especial: aquella cuyos compo-
nentes son las funciones constructoras de los tipos de datos mutuamente recursivos.
Esta �algebra de�ne la funci�on identidad del tipo de datos. La funci�on composicional
que corresponde a un �algebra, es el homomor�smo �unico del �algebra que va desde
el tipo de datos al �algebra dada. Por lo tanto el tipo de datos se denomina tambi�en
�algebra inicial y se dice que las funciones composicionales de�nen las sem�anticas
algebraicas. Resumimos esta importante a�rmaci�on como sigue:
semanticaAlgebraica : : AlgebraInicial -> Algebra
6.4 Expresiones
La primera parte de esta secci�on presenta un evaluador b�asico de expresiones. En
la segunda parte el evaluador se extiende con variables y en la tercera parte con
de�niciones locales.
6.4.1 Evaluaci�on de expresiones
En �esta subsecci�on comenzamos con un ejemplo m�as complicado: un evaluador de
expresiones. El tipo de datos que usaremos para expresiones es diferente del que
se introdujo en la secci�on 6.2.3: aqu�� usaremos uno solo, y por lo tanto un tipo de
datos para expresiones mutuamente no recursivo. Nos restringimos a expresiones con
valor de tipo punto otante en las cuales se de�nen, la suma, resta, multiplicaci�on y
divisi�on. El tipo de datos, el tipo del �algebra correspondiente y la funci�on fold son
como sigue:
infixl 7 `Mul`
infix 7 `Dvd`
infixl 6 `Add`, `Min`
data Expr = Expr `Add` Expr
| Expr `Min` Expr
| Expr `Mul` Expr
| Expr `Dvd` Expr
| Num Float
type ExprAlgebra a = (a->a->a -- add
,a->a->a -- min
,a->a->a -- mul
120 Composicionalidad
,a->a->a -- dvd
,Float->a) -- num
foldExpr :: ExprAlgebra a -> Expr -> a
foldExpr (add,min,mul,dvd,num) = fold where
fold (expr1 `Add` expr2) = fold expr1 `add` fold expr2
fold (expr1 `Min` expr2) = fold expr1 `min` fold expr2
fold (expr1 `Mul` expr2) = fold expr1 `mul` fold expr2
fold (expr1 `Dvd` expr2) = fold expr1 `dvd` fold expr2
fold (Num n) = num n
No hay nada notable acerca de estas de�niciones, a no ser, tal vez el hecho que Expr
no tiene un par�ametro x extra como los ejemplos de listas y �arboles. Calcular el re-
sultado de una expresi�on ahora simplemente consiste en reemplazar los constructores
por funciones apropiadas.
resultExpr :: Expr -> Float
resultExpr = foldExpr ((+),(-),(*),(/),id)
6.4.2 Adici�on de variables
Nuestra pr�oxima meta es extender el evaluador de la subsecci�on anterior, de ma-
nera que tambi�en pueda manejar variables. Los valores de las variables se buscan
t��picamente en un ambiente que asocia nombres de variables a valores. Implemen-
tamos un ambiente como una lista de pares nombre-valor. Para nuestro prop�osito
los nombres son cadenas y los valores son otantes. En los programas siguientes
usaremos las siguientes funciones y tipos:
type Env name value = [name,value]
(?) :: Eq name => Env name value -> name -> value
env ? x = head [ v | (y,v) <- env, x == y]
type Name = String
type Value = Float
El tipo de datos, el tipo del �algebra correspondiente y la funci�on fold ahora son como
sigue. Observe que usamos el mismo nombre (Expr) para el tipo de datos, aunque
di�ere del anterior tipo de datos Expr.
data Expr = Expr `Add` Expr
| Expr `Min` Expr
| Expr `Mul` Expr
| Expr `Dvd` Expr
| Num Value
| Var Name
type ExprAlgebra a = (a->a->a -- add
,a->a->a -- min
,a->a->a -- mul
6.4 Expresiones 121
,a->a->a -- dvd
,Value->a -- num
,Name->a) -- var
foldExpr :: ExprAlgebra a -> Expr -> a
foldExpr (add,min,mul,dvd,num,var) = fold where
fold (expr1 `Add` expr2) = fold expr1 `add` fold expr2
fold (expr1 `Min` expr2) = fold expr1 `min` fold expr2
fold (expr1 `Mul` expr2) = fold expr1 `mul` fold expr2
fold (expr1 `Dvd` expr2) = fold expr1 `dvd` fold expr2
fold (Num n) = num n
fold (Var x) = var x
Ahora Expr tiene un constructor extra: el constructor unario Var. De igual manera
el par�ametro de foldExpr tiene ahora un componente extra: la funci�on unaria var
que corresponde al constructor unario Var. Se necesita un ambiente para calcular el
resultado de una expresi�on. Presentamos primero una forma incorrecta de hacerlo:
se puede usar como par�ametro de una funci�on que calcule un �algebra (explicaremos
por qu�e �esta es una desafortunada elecci�on en la subsiguiente subsecci�on; la idea
b�asica es que aqu�� utilizamos el ambiente como una variable global).
resultExprBad :: Env Name Value -> Expr -> Value
resultExprBad env = foldExpr ((+),(-),(*),(/),id,(env ?))
?resultExprBad [("x",3)] (Var "x" `Mul` Num 2)
6
La forma correcta de utilizar un ambiente es la siguiente: en lugar de trabajar con
un c�alculo que, dado un ambiente, produce un �algebra de valores, es mejor convertir
el propio c�alculo en un �algebra. De esa manera convertiremos al ambiente en una
variable `local'.
(<+>),(<->),(<*>),(</>) :: (Env Name Value -> Value) ->
(Env Name Value -> Value) ->
(Env Name Value -> Value)
f <+> g = \env -> f env + g env
f <-> g = \env -> f env - g env
f <*> g = \env -> f env * g env
f </> g = \env -> f env / g env
resultExprGood :: Expr -> (Env Name Value -> Value)
resultExprGood =
foldExpr ((<+>),(<->),(<*>),(</>),const,flip (?))
?resultExprGood (Var "x" `Mul` Num 2) [("x",3)]
6
Ahora las acciones ((+), (-), : : :) sobre valores se reemplazan por acciones corres-
pondientes ((<+>), <->), : : : en los c�alculos. El c�alculo del resultado de la suma de
dos subexpresiones en un contexto dado consiste en calcular el resultado de las su-
bexpresiones en este ambiente y sumar ambos resultados para producir un resultado
122 Composicionalidad
�nal. El c�alculo del resultado de una variable consiste en buscarlo en el ambiente.
As��, la sem�antica algebraica de una expresi�on es un c�alculo que produce un valor.
Esta importante a�rmaci�on puede ser resumida como sigue:
semanticaAlgebraica : : AlgebraInicial -> Calcular Valor
En este caso el c�alculo es de la forma env -> val. El tipo de valor es un ejemplo
de un atributo sintetizado. El valor de una expresi�on es sintetizado a partir de los
valores de sus subexpresiones. El tipo del ambiente es un ejemplo de un atributo
heredado. El ambiente que se utiliza para el c�alculo de una subexpresi�on de una
expresi�on se hereda del c�alculo de la expresi�on. Puesto que estamos trabajando con
sintaxis abstracta, decimos que los atributos sintetizados y heredados son atributos
del tipo de datos Expr. Si Expr es uno de los tipos de datos mutuamente recursivos
que son generados a partir de los no terminales de una gram�atica, entonces decimos
que los atributos heredados y sintetizados son atributos de los no terminales.
6.4.3 Adici�on de de�niciones
Nuestra pr�oxima meta es extender el evaluador de la subsecci�on anterior de tal forma
que tambi�en se pueda manejar de�niciones. Una de�nici�on es una expresi�on de la
forma Def name expr1 expr2, que deber��a ser interpretada como: sea el valor de
name igual a expr1 en la expresi�on expr2. Las variables se de�nen t��picamente por
la actualizaci�on del ambiente con un par nombre-valor apropiado. El tipo de datos
(llamado otra vez Expr) y el tipo del �algebra correspondiente y la funci�on fold ahora
son como sigue:
data Expr = Expr `Add` Expr
| Expr `Min` Expr
| Expr `Mul` Expr
| Expr `Dvd` Expr
| Num Value
| Var Name
| Def Name Expr Expr
type ExprAlgebra a = (a->a->a -- add
,a->a->a -- min
,a->a->a -- mul
,a->a->a -- dvd
,Value->a -- num
,Name->a -- var
,Name->a->a->a) -- def
foldExpr :: ExprAlgebra a -> Expr -> a
foldExpr (add,min,mul,dvd,num,var,def) = fold where
fold (expr1 `Add` expr2) = fold expr1 `add` fold expr2
fold (expr1 `Min` expr2) = fold expr1 `min` fold expr2
fold (expr1 `Mul` expr2) = fold expr1 `mul` fold expr2
fold (expr1 `Dvd` expr2) = fold expr1 `dvd` fold expr2
fold (Num n) = num n
fold (Var x) = var x
6.4 Expresiones 123
fold (Def x value body) = def x (fold value) (fold body)
Ahora Expr tiene un constructor extra: el constructor ternario Def, que se puede
usar para introducir una variable local. Por ejemplo, la siguiente expresi�on se usa
para calcular la cantidad de segundos por a~no.
seconds = Def "days_per_year" (Num 365) (
Def "hours_per_day" (Num 24) (
Def "minutes_per_hour" (Num 60) (
Def "seconds_per_minute" (Num 60) (
Var "days_per_year" `Mul`
Var "hours_per_day" `Mul`
Var "minutes_per_hour" `Mul`
Var "seconds_per_minute" ))))
De igual manera, ahora el par�ametro de foldExpr tiene un componente extra: la
funci�on ternaria def que corresponde al constructor ternario Def. F��jese que los
�ultimos dos argumentos son recursivos. Ahora podemos explicar por qu�e el pri-
mer uso de los ambientes es inferior al segundo. El tratar de extender la primera
de�nici�on produce lo siguiente:
resultExprBad :: Env Name Value -> Expr -> Value
resultExprBad env =
foldExpr ((+),(-),(*),(/),id,(env ?),error "def")
El �ultimo componente causa con ictos: un cuerpo que contiene una de�nici�on lo-
cal tiene que ser evaluado en un ambiente actualizado. No podemos actualizar el
ambiente en este escenario. Podemos leer el ambiente pero posteriormente ya no es
accesible en el �algebra (que consiste de valores). El extender la segunda de�nici�on
no causa problemas: el ambiente ahora es accesible en el �algebra (que consiste de
c�alculos). Podemos adicionar f�acilmente una nueva acci�on que actualiza el ambiente.
El c�alculo correspondiente al cuerpo de una expresi�on con una de�nici�on local ahora
puede ser evaluado en el ambiente actualizado.
f <+> g = \env -> f env + g env
f <-> g = \env -> f env - g env
f <*> g = \env -> f env * g env
f </> g = \env -> f env / g env
x <:=> f = \g env -> g ((x,f env):env)
resultExprGood :: Expr -> (Env Name Value -> Value)
resultExprGood =
foldExpr ((<+>),(<->),(<*>),(</>),const,flip (?),(<:=>))
?resultExprGood seconds []
31536000
Note que por conseguir un par (x,y) hacia un ambiente (en la de�nici�on del operador
<:=>), a~nadimos el par al ambiente. Por de�nici�on de (?) la relaci�on para x esconde
otras relaciones posibles para x.
124 Composicionalidad
6.4.4 Compilaci�on a una m�aquina de pila
En esta secci�on compilamos expresiones a instrucciones de una m�aquina de pila. En-
tonces podemos usar esta m�aquina de pila para evaluar expresiones compiladas. Esta
secci�on est�a inspirada por un ejemplo en [3]. Imagine una computadora simple para
evaluar expresiones aritm�eticas. Esta computadora tiene una `pila' y puede ejecutar
`instrucciones' que cambian el valor de la pila. La clase de posibles instrucciones
est�a de�nida por el siguiente tipo de datos:
data MachInstr v = Push v | Apply (v -> v -> v)
type StackMachine v = [MachInstr v]
Una instrucci�on o bien pone un valor del tipo v en la pila, o ejecuta un operador que
toma los dos valores del tope de la pila, aplica el operador y devuelve el resultado a
la pila. Una pila (un valor del tipo Stack v para alg�un valor del tipo v) es una lista
de valores, de la cual se puede sacar (pop) valores, en la cual se puede poner (push)
valores, y de la cual se puede tomar (top) el valor superior. El m�odulo Stack para
pilas se encuentra en el ap�endice A. El efecto de ejecutar una instrucci�on de tipo
MachInstr est�a de�nido por:
execute :: MachInstr v -> Stack v -> Stack v
execute (Push x) s = push x s
execute (Apply op) s = let a = top s
t = pop s
b = top t
u = pop t
in push (op a b) u
Una secuencia de instrucciones se ejecuta por la funci�on run de�nida por:
run :: StackMachine v -> Stack v -> Stack v
run [] s = s
run (x:xs) s = run xs (execute x s)
Una de�nici�on alternativa de run puede basarse en foldl.
Una expresi�on puede traducirse (o compilarse ) a una lista de instrucciones mediante
la funci�on compile, de�nida por:
compileExpr :: Expr ->
Env Name (StackMachine Value) ->
StackMachine Value
compileExpr = foldExpr (add,min,mul,dvd,num,var,def) where
f `add` g = \env -> f env ++ g env ++ [Apply (+)]
f `min` g = \env -> f env ++ g env ++ [Apply (-)]
f `mul` g = \env -> f env ++ g env ++ [Apply (*)]
f `dvd` g = \env -> f env ++ g env ++ [Apply (/)]
num v = \env -> [Push v]
var x = \env -> env ? x
def x fd fb = \env -> fb ((x,fd env):env)
6.4 Expresiones 125
compileExpr :: Expr ->
Env Name (StackMachine Value) ->
StackMachine Value
compileExpr = foldExpr (add,min,mul,dvd,num,var,def) where
f `add` g = \env -> f env ++ g env ++ [Apply (+)]
f `min` g = \env -> f env ++ g env ++ [Apply (-)]
f `mul` g = \env -> f env ++ g env ++ [Apply (*)]
f `dvd` g = \env -> f env ++ g env ++ [Apply (/)]
num v = \env -> [Push v]
var x = \env -> env ? x
def x fd fb = \env -> fb ((x,fd env):env)
Para todas las expresiones e, ambientes env y pilas s ahora tenemos:
top (run (compileExpr e env) s) = resultExprGood e env
La demostraci�on de esta igualdad, que es por inducci�on sobre expresiones, ha sido
omitida.
Ejercicio 6.5 . De�na la siguiente funci�on como folds en el tipo de dato Expr que contiene
de�niciones.
1. isSum, que determina si una expresi�on es una suma.
2. vars, que retorna la lista de variables que ocurren en la expresi�on.
/
Ejercicio 6.6 . Este ejercicio trata con expresiones sin de�niciones. La funci�on der est�a de�nida
por:
der :: Expr -> String -> Expr
der (e1 `Add` e2) dx = der e1 dx `Add` der e2 dx
der (e1 `Min` e2) dx = der e1 dx `Min` der e2 dx
der (e1 `Mul` e2) dx = e1 `Mul` (der e2 dx) `Add` (der e1 dx) `Mul` e2
der (e1 `Dvd` e2) dx = (e2 `Mul` (der e1 dx)
`Min` e1 `Mul` (der e2 dx))
`Dvd` (e2 `Mul` e2)
der (Num f) dx = Num 0.0
der (Var s) dx = if s == dx then Num 1.0 else Num 0.0
1. Dar una descripci�on informal de la funci�on der.
2. >Por qu�e la funci�on der no es composicional?
3. De�nir un tipo de dato Exp para representar expresiones que consisten de,
constantes (de punto otante), variables, suma y resta. De�na tambi�en el tipo
ExpAlgebra y el correspondiente foldExp.
4. De�na la funci�on der sobre Exp y muestre que esta funci�on es composicional.
/
126 Composicionalidad
6.5 Lenguajes estructurados en bloques
Esta secci�on presenta un ejemplo m�as complejo del uso de tuplas en combinaci�on con
composicionalidad. El ejemplo muestra el alcance de las variables en un lenguaje
estructurado en bloques. Una variable de un �ambito global es visible en un �ambito
local solamente si no est�a oculta por una variable con el mismo nombre en el �ambito
local.
6.5.1 Bloques
Un bloque es una lista de instrucciones. Una instrucci�on es una declaraci�on de
variable, un uso de variable o un bloque anidado. La representaci�on concreta de
un ejemplo de bloque de nuestro lenguaje estructurado en bloques se ve como sigue
(dcl signi�ca declaraci�on, use representa uso y x, y y z son variables).
use x ; dcl x ;
(use z ; use y ; dcl x ; dcl z ; use x) ;
dcl y ; use y
Las instrucciones est�an separadas por punto y coma. Los bloques anidados est�an
rodeados de par�entesis. El uso de z se re�ere a la declaraci�on local (la �unica decla-
raci�on de z). El uso de y se re�ere a la declaraci�on global (la �unica declaraci�on de
y). El uso local de x re�ere a la declaraci�on local y el uso global de x re�ere a la
declaraci�on global. Note que se permite el uso de variables antes de que se decla-
ren. Presentamos a continuaci�on algunos tipos (de datos) mutuamente recursivos,
que describen la sintaxis abstracta de los bloques, que corresponden a la gram�atica
que describe la sintaxis concreta de los bloques que se usan arriba. Usamos nom-
bres signi�cativos para los constructores de datos y usamos las listas prede�nidas
en lugar de listas de�nidas por el usuario para el BlockAlgebra. Como es usual, el
tipo del �algebra BlockAlgebra, que consiste de dos tuplas de funciones, y la fun-
ci�on fold foldBlock, que utiliza dos funciones locales mutuamente recursivas, puede
generarse a partir de los dos tipos de datos mutuamente recursivos.
type Block = [Statement]
data Statement = Dcl Idf | Use Idf | Blk Block
type Idf = String
type BlockAlgebra b s = ((s -> b -> b,b)
,(Idf -> s,Idf -> s,b -> s)
)
foldBlock :: BlockAlgebra b s -> Block -> b
foldBlock ((cons,empty),(dcl,use,blk)) = fold where
fold (s:b) = cons (foldS s) (fold b)
fold [] = empty
foldS (Dcl x) = dcl x
foldS (Use x) = use x
foldS (Blk b) = blk (fold b)
6.5 Lenguajes estructurados en bloques 127
6.5.2 Generaci�on de c�odigo
La meta de esta secci�on es generar c�odigo a partir de un bloque. El c�odigo consiste
de una secuencia de instrucciones. Hay tres tipos de instrucciones.
� Enter (l,c): ingresa en el l-�esimo bloque anidado en el cual las variables
locales c est�an declaradas.
� Leave (l,c): deja el l-�esimo bloque anidado, donde las variables locales c
fueron declaradas.
� Access (l,c): accede a la c-�esima variable del l-�esimo bloque anidado.
El c�odigo que corresponde al ejemplo anterior deber��a verse como sigue:
[ Enter (0,2), Access (0,0)
, Enter (1,2), Access (1,1),Access (0,1),Access (1,0), Leave (1,2)
, Access (0,1), Leave (0,2)
]
Note, que empezamos enumerando los niveles (l) y las cuentas (c) (que algunas
veces se denominan desplazamientos) desde 0. La sintaxis abstracta del c�odigo a ser
generado se describe con el tipo de datos siguiente.
type Count = Int
type Level = Int
type Variable = (Level,Count)
type BlockInfo = (Level,Count)
data Instruction = Enter BlockInfo
| Leave BlockInfo
| Access Variable
type Code = [Instruction]
La funci�on ab2ac que genera c�odigo abstracto (un valor de tipo Code) a partir
de un bloque abstracto (un valor de tipo Bloque) usa una funci�on composicional
block2Code. Para todas las construcciones sint�acticas de Statement y Block, de-
�nimos acciones sem�anticas apropiadas en un �algebra de c�alculos. La siguiente es
una descripci�on algo simpli�cada de estas acciones sem�anticas.
� Dcl: Cada vez que declaramos una variable local x tenemos que actualizar el
ambiente local le del bloque en el que estamos asociando con x el par nivel
actual{conteo local (l,lc). Es m�as, tenemos que incrementar el conteo de la
variable local lc a lc+1. Note que no hemos generado ning�un c�odigo para una
declaraci�on de instrucciones. En lugar de ello ejecutamos algunos c�alculos que
hacen posible generar un c�odigo apropiado para otras instrucciones.
dcl x (le,l,lc) = (le',lc') where
le' = le `update` (x,(l,lc))
lc' = lc+1
128 Composicionalidad
donde la funci�on update est�a de�nida en el m�odulo AssociationList.
� Use: Cada vez que usamos la variable local x tenemos que generar c�odigo cd'
para ella. Este c�odigo es de la forma [Access (l,lc)]. El par nivel{conteo
local (l,lc) de la variable se busca en el ambiente global e.
use x e = cd' where
cd' = [Access (l,c)]
(l,c) = e ? x
� Blk: Cada vez que introducimos un bloque anidado incrementamos el nivel
global de l a l+1, comenzamos con un conteo de la variable local en 0 y
�jamos el ambiente local del bloque anidado al ambiente global actual e. El
c�alculo para el bloque anidado resulta en un conteo de variable local lcB y un
ambiente local leB. Adem�as necesitamos asegurarnos que el ambiente global
(aquel en el cual buscamos variables) que se utiliza para el c�alculo del bloque
anidado sea igual a leB. El c�odigo generado por el bloque es rodeado por un
par apropiado [Enter lcB]-[Leave lcB].
blk fB (e,l) = cd' where
l' = l+1
(leB,lcB,cdB) = fB (leB,l',e,0)
cd' = [Enter (l',lcB)]++cdB++[Leave (l',lcB)]
� []: No es necesario realizar acciones para un bloque vac��o.
� (:): Para cada bloque no vac��o, realizamos el c�alculo de la primera declaraci�on
del bloque, que dado un ambiente local le y un conteo de variable local lc,
produce un ambiente local leS y un conteo de variable local lcS. Este par
ambiente-conteo se utiliza luego en el c�alculo del resto del bloque para producir
un ambiente local le' y un conteo de variable local lc'. El c�odigo cd' que
se genera es la concatencaci�on cdS++cdB del c�odigo cdS que se genera para la
primera declaraci�on y el c�odgo cdB que se gener�o para el resto del bloque.
cons fS fB (le,lc) = (le',lc',cd') where
(leS,lcS,cdS) = fS (le,lc)
(le',lc',cdB) = fB (leS,lcS)
cd' = cdS++cdB
>Qu�e aspecto debe tener el tipo de computaci�on que realizamos? Para dcl necesi-
tamos tres atributos heredados: un nivel global, un ambiente del bloque local y un
conteo de la variable local. Dos de ellos: el ambiente del bloque local y el conteo de
la variable local son tambi�en atributos sintetizados. Para use necesitamos un atri-
buto heredado: un ambiente de bloque global, y calculamos un atributo sintetizado:
el c�odigo generado. Para blk necesitamos dos atributos heredados: un ambiente de
bloque global y un nivel global, y calculamos dos atributos sintetizados: el conteo de
variable local y el c�odigo generado. M�as a�un hay un atributo extra: un ambiente de
bloque local que, utilizando un ciclo hacia atr�as con el ambiente del bloque global,
es tanto heredado como sintetizado. De hecho, cuando procesamos las declaraciones
de un bloque anidado ya hacemos uso del ambiente de bloque global que estamos
6.5 Lenguajes estructurados en bloques 129
sintetizando (al buscar variables). Para cons calculamos tres atributos sintetizados:
el ambiente de bloque local, el conteo de la varible local y el c�odigo generado. Dos
de ellos, el ambiente del bloque local y el conteo de la variable local son tambi�en
requeridos como atributos heredados. Se desprende de las anteriores consideraciones
que los siguientes tipos satisfacen nuestras necesidades.
type BlockEnv = [(Idf,Variable)]
type GlobalEnv = (BlockEnv,Level)
type LocalEnv = (BlockEnv,Count)
La implementaci�on de block2Code es ahora una traducci�on sencilla de las acciones
arriba descritas. Los atributos que no han sido mencionados en aquellas acciones se
a~naden como componentes extra que no contribuyen a la funcionlidad.
block2Code :: Block -> GlobalEnv -> LocalEnv -> (LocalEnv,Code)
block2Code = foldBlock ((cons,empty),(dcl,use,blk)) where
cons fS fB (e,l) (le,lc) = ((le',lc'),cd') where
((leS,lcS),cdS) = fS (e,l) (le,lc)
((le',lc'),cdB) = fB (e,l) (leS,lcS)
cd' = cdS++cdB
empty (e,l) (le,lc) = ((le,lc),[])
dcl x (e,l) (le,lc) = ((le',lc'),[]) where
le' = (x,(l,lc)):le
lc' = lc+1
use x (e,l) (le,lc) = ((le,lc),cd') where
cd' = [Access (l,c)]
(l,c) = e ? x
blk fB (e,l) (le,lc) = ((le,lc),cd') where
((leB,lcB),cdB) = fB (leB,l') (e,0)
l' = l+1
cd' = [Enter (l',lcB)] ++ cdB ++ [Leave (l',lcB)]
El generador de c�odigo comienza con un ambiente local vac��o, un nuevo nivel y un
nuevo conteo de la variable local. El c�odigo es un atributo sintetizado. El ambiente
global es tanto heredado como sintetizado. Cuando se procesa un bloque ya se utiliza
el ambiente global que estamos sintetizando (cuando buscamos variables).
ab2ac :: Block -> Code
ab2ac b = [Enter (0,c)] ++ cd ++ [Leave (0,c)] where
((e,c),cd) = block2Code b (e,0) ([],0)
aBlock
= [Use "x",Dcl "x"
,Blk [Use "z",Use "y",Dcl "x",Dcl "z",Use "x"]
,Dcl "y",Use "y"]
? ab2ac aBlock
[Enter (0,2),Access (0,0)
,Enter (1,2),Access (1,1),Access (0,1),Access (1,0),Leave (1,2)
,Access (0,1),Leave (0,2)]
130 Composicionalidad
6.6 Ejercicios
Ejercicio 6.7 . Considere su respuesta del ejercicio 2.21, que da una sintaxis abstracta para
pal��ndromes.
1 De�na un tipo PalAlgebra que describen los tipos de las acciones sem�anticas
correspondientes a las construcciones sint�acticas de Pal.
2 De�na la funci�on foldPal que describe c�omo deberian ser aplicados las accio-
nes sem�anticas que corresponden a las construcciones sint�acticas de Pal.
3 De�na las funciones a2cPal y aCountPal como de foldPal.
4 De�na el parser pfoldPal que interprete su entrada en una sem�antica arbitra-
ria PalAlgebra sin construir el �arbol sint�actico abstracto intermedio.
5 Describe los parsers foldPal m1 y pfoldPal m2 donde m1 y m2 corresponden
a las �algebras de a2cPal y aCountPal respectivamente.
/
Ejercicio 6.8 . Considere su respuesta del ejercicio 2.22, que da una sintaxis abstracta para
pal��ndromes espejo.
1 De�na el tipo MirAlgebra que describe las acciones sem�anticas correspondien-
tes a las construcciones sint�acticos de Mir.
2 De�na la funci�on foldMir, que describe c�omo deber��an aplicarse las acciones
sem�anticas correspondientes a las construcciones sint�acticas de Mir.
3 De�na las funciones a2cMir y m2pMir como las de foldMir.
4 De�na el parser pfoldMir, que interpreta su entrada en una sem�antica arbi-
traria MirAlgebra sin construir el �arbol sint�actico abstracto intermedio.
5 Describe los parsers foldMir m1 y pfoldMir m2 donde m1 y m2 corresponden
a las �algebras de a2cMir y m2pMir respectivamente.
/
Ejercicio 6.9 . Considere su repuesta del ejercicio 2.23, que da una sintaxis abstracta para
secuencias de paridad.
1 De�na el tipo EvenAlgebra que describe las acciones sem�anticas que corres-
ponden a las construcciones sint�acticas de Even.
2 De�na la funci�on foldEven, que describe c�omo deber��an aplicarse las acciones
sem�anticas que corresponden a las construcciones sint�acticas de Even.
3 De�na las funciones a2cEven y valEven como las de foldEven.
/
Ejercicio 6.10 . Considere su respuesta del ejercicio 2.24, donde da una sintaxis abstracta para
listas de bits.
1 De�na el tipo BitListAlgebra que describe las acciones sem�anticas que co-
rresponden a las construcciones sint�acticas de BitList.
2 De�na la funci�on foldBitList, que describe c�omo deber��an aplicarse las accio-
nes sem�anticas correspondientes a las construcciones sint�acticas de BitList.
3 De�na la funci�on a2cBitList como instancia de foldBitList.
4 De�na el parser pfoldBitList, que interpreta su entrada en una sem�antica
arbitraria BitListAlgebra sin construir el �arbol sint�actico abstracto interme-
dio.
/
6.6 Ejercicios 131
Ejercicio 6.11 . La siguiente gram�atica describe la sintaxis concreta de un lenguaje de progra-
maci�on simple estructurado en bloques
B ! SR Block
R ! ;SR j � Rest
S ! D j U j N Statement
D ! x j y Declaration
U ! X j Y Usage
N ! (B) Nested Block
1. De�na un tipo de datos Block que describe la sintaxis abstracta correspon-
diente a la gram�atica. >Cu�al es la representaci�on abstracta de x;(y;Y);X?
2. De�na el tipo BlockAlgebra que describe las acciones sem�anticas correspon-
dientes a las construcciones sint�acticas de Block.
3. De�na la funci�on foldBlock que describe c�omo deber�ian aplicarse las acciones
sem�anticas correspondientes a las construcciones sint�acticas de Block.
4. De�na la funci�on a2cBlock, que convierte un bloque abstracto en uno concreto.
Escriba a2cBlock como un foldBlock.
5. La funci�on checkBlock veri�ca si toda variable de un bloque abstracto dado se
declara antes de ser usada (declarada en el mismo o en un bloque circundante).
/
Cap��tulo 7
Calculando con los analizadores
sint�acticos
Los analizadores sint�acticos producen resultados. Por ejemplo, los analizadores para
esquemas de viaje presentados en el cap��tulo 4 retornan una sintaxis abstracta, o un
entero que representa el tiempo neto de viaje en minutos. El tiempo neto de viaje
se calcula directamente insertando las funciones sem�anticas correctas. Otra forma
de calcular el tiempo neto de viaje es calcular primero la sintaxis abstracta y luego
aplicar una funci�on a la sintaxis abstracta que calcula el tiempo neto de viaje. Esta
secci�on muestra varias formas para calcular resultados usando analizadores.
� insertar una funci�on sem�antica en el analizador;
� aplicar fold a la sintaxis abstracta;
� usar una clase en lugar de sintaxis abstracta;
� pasar un �algebra al analizador.
7.1 Inserci�on de funci�ones sem�anticas en el analizador
En el cap��tulo 4 hemos de�nido dos analizadores sint�acticos: uno que calcula la
sintaxis abstracta para un esquema de viaje, y otro que calcula el tiempo neto
de viaje. Estas funciones se obtienen insertando distintas funciones en el analizador
b�asico. Si queremos calcular el tiempo total de viaje, tenemos que insertar diferentes
funciones en el analizador b�asico. Este enfoque es apropiado para un analizador
peque~no, pero tiene algunas desventajas cuando construimos un analizador m�as
grande:
� la sem�antica est�a entrelazada con el proceso de an�alisis sint�actico;
� es dif��cil localizar todas las posiciones donde se debe insertar las funciones
sem�anticas en el analizador.
7.2 Aplicando un fold a la sintaxis abstracta
En lugar de insertar operaciones en un analizador b�asico, podemos escribir un anali-
zador que transforme la entrada en una sintaxis abstracta, y que calcule el resultado
deseado aplicando un fold a la sintaxis abstracta.
Un ejemplo de este tipo de tratamiento se ha expuesto en la secci�on 3.3.1, donde se
han de�nido dos funciones con la misma funcionalidad: nesting y nesting'; ambas
calculan la profundidad m�axima del anidado en una cadena de par�entesis. La funci�on
nesting se de�ne insertando funciones en el analizador b�asico. La funci�on nesting'
133
134 Calculando con los analizadores sint�acticos
se de�ne aplicando un fold a la sintaxis abstracta. Cada una de estas funciones tiene
sus propios m�eritos; repetimos los principales argumentos a continuaci�on.
parens :: Parser Char Parentheses
parens = (\_ b _ d -> Match b d) <$>
open <*> parens <*> close <*> parens
<|> succeed Empty
nesting :: Parser Char Int
nesting = (\_ b _ d -> max (1+b) d) <$>
open <*> nesting <*> close <*> nesting
<|> succeed 0
nesting' :: Parser Char Int
nesting' = depthParentheses <$> parens
La primera de�nici�on (nesting) es m�as e�ciente, porque no construye un �arbol
sint�actico abstracto intermedio. Por otra parte, podr��a ser m�as dif��cil describirla
porque tenemos que insertar funciones en los lugares correctos del analizador b�asico.
La ventaja de la segunda de�nici�on (nesting') es que reusamos tanto el analizador
parens, que retorna un �arbol de sintaxis abstracto, y la funci�on depthParentheses
(o la funci�on foldParentheses, que se usa en la de�nici�on depthParentheses), que
hace una recursi�on sobre el �arbol de sintaxis abstracta. Lo �unico que tenemos que
escribir en la segunda de�nici�on es el depthParenthesesAlgebra. La desventaja
de la segunda de�nici�on es que construimos un �arbol de sintaxis abstracto inter-
medio, que es `aplanado' por el fold. Lo que se busca es evitar construir el �arbol
de la sintaxis abstracta. Para conseguir un mejor balance, nos gustar��a escribir la
funci�on nesting' y que nuestro compilador decida que es mejor usar la funci�on
nesting en los c�alculos. La transformaci�on autom�atica de la funci�on nesting'
en la funci�on nesting se llama deforestaci�on (los �arboles se eliminan). Algunos
compiladores (muy pocos) son lo bastante astutos para realizar esta transformaci�on
autom�aticamente.
7.3 Deforestaci�on
La deforestaci�on elimina los �arboles intermedios en los c�alculos. La secci�on anterior
da un ejemplo de deforestaci�on en el tipo de datos Parentheses. Esta secci�on esboza
la idea general.
Suponga que tenemos un tipo de dato AbstractTree
data AbstractTree = ...
A partir de este tipo de dato construimos el tipo de las �algebras y un fold, ver
cap��tulo 6.
type AbstractTreeAlgebra a = ...
foldAbstractTree :: AbstractTreeAlgebra a -> AbstractTree -> a
7.4 Usando una clase en lugar de sintaxis abstracta 135
Un analizador para el tipo de dato AbstractTree (que retorna un valor de AbstractTree)
tiene el siguiente tipo:
parseAbstractTree :: Parser Symbol AbstractTree
Donde Symbol es alg�un tipo de s��mbolos de entrada (por ejemplo Char). Supongamos
ahora que de�nimos la funci�on p que analiza un AbstractTree, y que luego calcula
alg�un valor haciendo un fold con un �algebra f sobre este �arbol:
p = foldAbstractTree f . parseAbstractTree
Entonces la deforestaci�on dice que p es igual a la funci�on parseAbstractTree en
la cual la presencia de los constructores del tipo de dato AbstractTree se han
reemplazado por los componentes correspondientes del �algebra f. Las siguientes dos
secciones describen una forma de implementar tal funci�on deforestada.
7.4 Usando una clase en lugar de sintaxis abstracta
Las clases se pueden usar para implementar el c�alculo deforestado o fusionado de un
fold con un analizador sint�actico. Esto da una soluci�on deseada e�ciente.
Por ejemplo, para el lenguaje de par�entesis, de�nimos la siguiente clase:
class Parens a where
match :: a -> a -> a
empty :: a
Note que los tipos de las funciones en la clase Parens corresponden exactamente a
los dos tipos que ocurren en el tipo ParenthesesAlgebra. Esta clase se usa en el
analizador para par�entesis:
parens :: Parens a => Parser Char a
parens = (\_ b _ d -> match b d) <$>
open <*> parens <*> close <*> parens
<|> succeed empty
El �algebra est�a impl��cita en esta funci�on: lo �unico que sabemos es que existen fun-
ciones empty y match del tipo correcto; no sabemos nada sobre su implementaci�on.
Para obtener una funci�on parens que retorne un valor de tipo Parentheses creamos
la siguiente instancia de la clase Parens.
instance Parens Parentheses where
match = Match
empty = Empty
ahora podemos escribir:
?(parens :: Parser Char Parentheses) "()()"
[(Match Empty (Match Empty Empty), "")
,(Match Empty Empty, "()")
,(Empty, "()()")
]
136 Calculando con los analizadores sint�acticos
Observe que tenemos que suministrar el tipo de parens a esta expresi�on, de otra
manera, Hugs no sabe qu�e instancia de Parens usar. Esta es la forma en que
convertimos el �algebra impl��cita de la `clase' en un �algebra expl��cita de la `instancia'.
Otra instancia de Parens se puede usar para calcular la profundidad del anidamiento
de los par�entesis:
instance Parens Int where
match b d = max (1+b) d
empty = 0
Y ahora podemos escribir:
?(parens :: Parser Char Int) "()()"
[(1, ""), (1, "()"), (0, "()()")]
As�� la respuesta depende del tipo que queremos que tenga nuestra funci�on parens.
Esto tambi�en muestra inmediatamente un problema con este enfoque, que es en
otros aspectos elegante: no funciona si queremos calcular dos resultados diferentes
del mismo tipo, porque Haskell no permite de�nir dos instancias (o m�as) con el
mismo tipo. Por lo tanto, una vez que hayamos de�nido la instancia Parens Int
como se hizo arriba, no podemos usar la funci�on parens' para calcular, por ejemplo,
el ancho (tambi�en un Int) de una cadena de par�entesis.
7.5 Pasar un �algebra al analizador sint�actico
En la secci�on anterior se muestra c�omo implementar un analizador con un �algebra
impl��cita. Puesto que este enfoque falla cuando queremos de�nir analizadores dife-
rentes con el mismo tipo de resultado, hacemos que las �algebra sean expl��citas. As��
obtenemos la siguiente de�nici�on de parens:
parens :: ParenthesesAlgebra a -> Parser Char a
parens (match,empty) = par where
par = (\_ b _ d -> match b d) <$>
open <*> par <*> close <*> par
<|> succeed empty
Note que ahora es f�acil de�nir distintos analizadores con el mismo tipo de resultado:
nesting, breadth :: Parser Char Int
nesting = parens (\b d -> max (1+b) d,0)
breadth = parens (\b d -> d+1,0)
Cap��tulo 8
Gram�aticas de atributos
introducci�on
En los cap��tulos anteriores hemos visto que al describir el mapeo de una estructura
reconocida (es decir de un �arbol de an�alisis) en cierto signi�cado, las �algebras juegan
un rol importante: para cada tipo recursivo T tenemos una funci�on foldT, y para
cada constructor del tipo de datos tenemos una funci�on correspondiente como un
componente en el �algebra. En el cap��tulo 6 se introduce un lenguaje en el cual se
permit��an las declaraciones locales. La evaluaci�on de expresiones en este lenguaje
puede ser resuelta eligiendo un �algebra adecuada. El dominio de esa �algebra era un
tipo de datos de orden superior (un tipo de dato que contiene funciones). Desafor-
tunadamente el c�odigo resultante ser��a sorprendente para muchos. En este cap��tulo
desarrollaremos un formalismo relacionado, el cual facilitar�a la construcci�on de las
�algebras involucradas.
Este formalismo, llamado gram�aticas de atributos, fue originalmente propuesto por
Donald Knuth en [10]. Las gram�aticas de atributos proveen una soluci�on para la
descripci�on sistem�atica de las fases de un compilador que ocurren despu�es de re-
alizar los an�alisis lexicogr�a�co y sint�actico. Aunque parecen diferentes de lo que
hemos visto hasta ahora, y son probablemente un poco m�as f�aciles de escribir, pue-
den ser mapeadas directamente en un programa funcional. Tradicionalmente tales
gram�aticas de atributos son usadas como la entrada de un generador de compi-
ladores. As�� como hemos visto con la introducci�on de un conjunto adecuado de
combinadores de an�alisis sint�actico, se puede evitar el uso de generadores de anali-
zadores sint�acticos, e incluso ganar mucha exibilidad en la extensi�on del formalismo
gramatical propio introduciendo combinadores m�as complicados, mostraremos como
se puede trabajar sin un sistema de procesamiento de gram�aticas de atributos que
no es de prop�osito de especial. Pero as�� como el concepto de una gram�atica libre de
contexto fue muy �util para comprender los aspectos fundamentales de los combina-
dores de an�alisis sint�actico, una comprensi�on de las gram�aticas de atributos ayudar�a
signi�cativamente a describir la parte sem�antica del proceso de reconocimiento y de
compilaci�on.
Las gram�aticas de atributos se introducir�an usando una secuencia de problemas cada
vez m�as complejos. El ejemplo inicial parece un poco arti�cial, y que trate con un
problema no interesante y altamente espec���co. Sin embargo, fue escogido por su
simplicidad y para no distraer nuestra atenci�on de los aspectos sem�anticos espec���cos
y relacionados al lenguaje de programaci�on. En la �ultima parte de este cap��tulo
demostraremos las t�ecnicas en un ejemplo m�as grande: un compilador peque~no para
137
138 Gram�aticas de atributos
data Tree = Leaf Int
| Bin Tree Tree
type TreeAlgebra a = (Int -> a, a -> a -> a)
foldTree :: TreeAlgebra a -> Tree -> a
foldTree alg@(leaf, _ ) (Leaf i) = leaf i
foldTree alg@(_ , bin) (Bin l r) = bin (foldTree alg l)
(foldTree alg r)
Listado 9: rm.start.hs
una parte de un lenguaje de programaci�on.
objetivos
En este cap��tulo aprender�a:
� c�omo escribir clases espec���cas de programas funcionales;
� el concepto de una gram�atica de atributos;
� c�omo transformar estas gram�aticas manualmente a programas funcionales.
8.1 Programas Circulares
Empezamos desarrollando de una manera no convencional de examinar los progra-
mas funcionales, y especialmente aquellos programas que usan un poco funciones
que descienden recursivamente sobre estructuras de datos. En nuestro caso se puede
pensar acerca de estos constructores de datos como �arboles de sintaxis abstracta.
Cuando calculamos una propiedad de tal objeto recursivo (por ejemplo, un progra-
ma) de�nimos dos conjunto de funciones: un conjunto que describe como visitar
recursivamente los nodos del �arbol, y otro conjunto de �algebras que describe qu�e
calcular en cada nodo cuando se lo visita.
Uno de los m�as importantes pasos en este proceso es decidir cual ser�a el tipo del
soporte de tales �algebras. Una vez tomado este paso, estos tipos ser�an una gu��a
para pasos posteriores de dise~no. Veremos que tales tipos de soportes pueden ser
funciones en s�� mismas, y que la decisi�on sobre el tipo de tales funciones no siempre es
sencilla. En esta secci�on presentaremos una visi�on sobre los c�alculos recursivos que
nos permitir�an \dise~nar" el tipo del soporte de una manera incremental. Haremos
esto construyendo �algebras extra��das de otras �algebras. De esta manera de�niremos
el signi�cado del lenguaje en una manera sem�anticamente composicional.
8.2 El problema rep min
Uno de los famosos ejemplos en el que se demuestra el poder de la evaluaci�on perezosa
es el problema del rep min [2]. Muchos se han preguntado sobre c�omo este programa
logra su meta puesto que a primera vista parece imposible que este problema resuelva
8.2 El problema rep min 139
minAlg = (id, min :: Int->Int->Int)
rep_min :: Tree -> Tree
rep_min t = foldTree repAlg t
where m = fold minAlg t
repAlg = (const (Leaf m), Bin)
Listado 10: rm.sol1.hs
cosa alguna, utilizaremos este problema y una secuencia de diferentes soluciones para
construir un entendimiento de toda una clase de tales programas.
En el listado 9 presentamos el tipo de datos Tree, junto al �algebra asociada. El
tipo del soporte de un �algebra es el tipo que describe los objetos del �algebra. Lo
representamos por un par�ametro de tipo �algebra:
type TreeAlgebra a = (Int -> a, a -> a -> a)
La funci�on de evaluaci�on asociada a foldTree reemplaza sistem�aticamente los cons-
tructores Leaf y Bin por sus correspondientes operaciones del �algebra alg que se
pasa como argumento.
Ahora queremos construir una funci�on rep min :: Tree -> Tree que retorna un
Tree con la misma \forma" que su argumento Tree, pero con los valores en sus
hojas remplazados por el m��nimo valor que ocurre en el �arbol original. Por ejemplo,
?rep_min (Bin (Bin (Leaf 1) (Leaf 7)) (Leaf 11))
Bin (Bin (Leaf 1) (Leaf 1)) (Leaf 1)
8.2.1 Una soluci�on directa
Una soluci�on directa al problema rep min consiste de una funci�on en la cual foldTree
se usa dos veces: una vez para calcular el valor m��nimo de los valores de la hoja, y
otra para construir el Tree resultante. La funci�on rep min que resuelve el problema
de esta forma se expone en el listado 10. F��jese que la variable m es una variable
global del �algebra repAlg, que se usa en la llamada a la construcci�on del �arbol
foldTree.
Una de las desventajas de esta soluci�on es que en el proceso del c�alculo el empareja-
miento de patrones asociado con la inspecci�on de los nodos del �arbol se realiza dos
veces para cada nodo del �arbol.
Aunque esta soluci�on no representa ning�un problema, trataremos de construir una
soluci�on que llama a foldTree una sola vez.
8.2.2 Lambda lifting
Queremos obtener un programa para el problema rep min en el cual el empareja-
miento de patrones se utilice s�olo una vez. El programa del listado 11 es un paso
intermedio hacia esta meta. En este programa se ha eliminado la variable global m
y la segunda llamada a foldTree ya no construye un Tree, sino una funci�on que
construye un �arbol del tipo Int ->Tree, que toma el valor m��nimo calculado como
140 Gram�aticas de atributos
repAlg = ( \_ -> \m -> Leaf m
,\lfun rfun -> \m -> let lt = lfun m
rt = rfun m
in Bin lt rt
)
rep_min' t = (foldTree repAlg t) (foldTree minAlg t)
Listado 11: rm.sol2.hs
infix 9 `tuple`
tuple :: TreeAlgebra a -> TreeAlgebra b -> TreeAlgebra (a,b)
(leaf1, bin1) `tuple` (leaf2, bin2) = (\i -> (leaf1 i, leaf2 i)
,\l r -> (bin1 (fst l) (fst r)
,bin2 (snd l) (snd r)
)
)
min_repAlg :: TreeAlgebra (Int, Int -> Tree)
min_repAlg = (minAlg `tuple` repAlg)
rep_min'' t = r m
where (m, r) = foldTree min_repAlg t
Listado 12: rm.sol3.hs
un argumento. Note que hemos enfatizado el hecho que se retorne una funci�on a
trav�es de una notaci�on super ua: el primer lambda en las de�niciones de la funci�on
que constituyen el algebra repAlg es requerido por el tipo del �algebra, el segundo
lambda, que podr��a haber sido omitido esta ah�� por que el conjunto de soportes del
�algebra contiene funciones del tipo Int -> Tree.
Este proceso se realiza de manera rutinaria en los compiladores de lenguajes funcio-
nales y se conoce como lambda-lifting.
8.2.3 El producto cartesiano de los c�alculos
Ahora estamos listos para formular una soluci�on en la cual foldTree se invoca una
sola vez. Note que en la �ultima soluci�on las dos llamadas a foldTree no inter�eren
entre s��. Debido a ello podr��amos realizar tanto el c�alculo de la funci�on de cons-
trucci�on del �arbol como del valor m��nimo de una sola vez, uniendo los resultados
de las dos operaciones. La soluci�on se da en el listado 12. Primero se de�ne una
funci�on tuple. Esta funci�on toma dos TreeAlgebras como argumentos y construye
una tercera, la que tiene como soporte tuplas de los soportes del �algebra original.
8.2 El problema rep min 141
mergedAlg :: Tree_Algebra (Int -> (Int,Tree))
mergedAlg = (\i -> \m -> (i, Leaf m)
,\lfun rfun -> \m -> let (lm,lt) = lfun m
(rm,rt) = rfun m
in (lm `min` rm
, Bin lt rt
)
)
rep_min''' t = r
where (m, r) = (foldTree mergedAlg t) m
Listado 13: rm.sol4.hs
8.2.4 Fusi�on de los productos cartesianos
En el pr�oximo paso transformamos el tipo del conjunto soporte del ejemplo anterior,
(Int, Int->Tree), en un tipo equivalente Int -> (Int, Tree). Esta transforma-
ci�on no es esencial aqu��, pero la usamos para demostrar que si calculamos un pro-
ducto cartesiano de funciones, podr��amos transformar ese tipo en un nuevo tipo en
el cual calculamos solamente una funci�on, que toma como sus argumentos el produc-
to cartesiano de todos los argumentos de las funciones en la tupla, y retorna como
su resultado el producto cartesiano de los tipos resultantes. En nuestro ejemplo el
c�alculo del valor m��nimo podr��a ser visto como una funci�on del tipo ()->Int. Como
una consecuencia el argumento del nuevo tipo es ((), Int), el cual es isom�or�co
solamente a Int, y el tipo del resultado se convierte en (Int, Tree).
Queremos tambi�en mencionar que la inversa no es por lo general verdadera; dada
una funci�on del tipo (a, b) -> (c, d), no es posible por lo general dividir esta
funci�on en dos funciones del tipo a -> c y b -> d, las cuales juntas logran el mismo
efecto. La nueva versi�on se presenta en el listado 13.
F��jese como hemos introducido aqu��, una vez m�as, extra lambdas en la de�nici�on
de las funciones del �algebra, para tratar de hacer expl��citos los diferentes roles de
los par�ametros. Los par�ametros despu�es del segundo lambda est�an ah�� por que
construimos valores en un conjunto soporte de orden superior. Los par�ametros
despu�es del primer lambda est�an ah�� por que trabajamos con un TreeAlgebra. Un
paso curioso tomado aqu�� es que parte del resultado, en nuestro caso el valor m, es
pasado de vuelta como un argumento al resultado de (foldTree mergedAlg t). La
evaluaci�on perezosa hace que esto funcione.
Que tales programas fueran posibles fue originalmente una gran sorpresa para mu-
chos programadores funcionales, especialmente para aquellos que sol��an programar
en LISP o ML, lenguajes que requieren que los argumentos sean evaluados completa-
mente antes que una llamada sea evaluada (llamada evaluaci�on estricta, en contraste
con la evaluaci�on perezosa). Debido a este sorprendente comportamiento esta clase
de programas llegan a ser conocida como programas circulares. Note empero, que
no existe nada circular en este programa. Cada valor est�a de�nido en t�erminos
de otros valores, y ning�un valor est�a de�nido en t�erminos de s�� mismo (como en
ones=1:ones).
142 Gram�aticas de atributos
rep_min'''' t = r
where (m, r) = tree t m
tree (Leaf i) = \m -> (i, Leaf m)
tree (Bin l r) = \m -> let (lm, lt) = tree l m
(rm, rt) = tree r m
in (lm `min` rm, Bin lt rt)
Listado 14: rm.sol5.hs
Finalmente en el listado 14 se muestra la versi�on de este programa en el cual se ha
introducido la funci�on foldTree.
De esta manera obtenemos la soluci�on original dada en Bird [2].
Concluyendo, hemos transformado sistem�aticamente un programa que inspecciona
cada nodo dos veces en un programa equivalente que inspecciona cada nodo sola-
mente una vez. La soluci�on resultante pasa de nuevo a ser parte del resultado de
una llamada como un argumento hacia la misma llamada. La evaluaci�on perezosa
hace que esto sea posible.
Ejercicio 8.1 . El problema frente profundo es el problema de encontrar el llamado frente de
un �arbol. El frente de un �arbol es la lista de todos los nodos que se encuentran
en el nivel m�as profundo. Como en el problema de rep min, los �arboles involucra-
dos son elementos del tipo de dato Tree. Vea el listado 9. Una soluci�on directa
es calcular la altura del �arbol y pasar el resultado de esta funci�on a una funci�on
frontAtLevel :: Tree -> Int -> [Int].
1. De�nir las funciones height y frontAtLevel
2. Dar las cuatro soluciones diferentes como se de�nieron en el problema rep min.
/
Ejercicio 8.2 . Hacer de nuevo el anterior ejercicio pero esta vez para el problema del frente alto
/
8.3 Un peque~no compilador
Esta secci�on construye un compilador peque~no para un lenguaje peque~no (o parte
de �este). El compilador traduce este c�odigo en c�odigo para una m�aquina de pila
hipot�etica.
8.3.1 El lenguaje
El lenguaje que consideramos en esta secci�on tiene enteros, booleanos, aplicaci�on
de funciones, y una expresi�on if-then-else. Un lenguaje con solo estos constructo-
res es inservible, y usted extender�a el lenguaje en los ejercicios con algunos otros
constructores, lo que har�a del lenguaje algo m�as interesante. Tomamos la siguiente
8.3 Un peque~no compilador 143
data ExprAS = If ExprAS ExprAS ExprAS
| Apply ExprAS ExprAS
| ConInt Int
| ConBool Bool deriving Show
type ExprASAlgebra a = (a -> a -> a -> a
,a -> a -> a
,Int -> a
,Bool -> a
)
foldExprAS :: ExprASAlgebra a -> ExprAS -> a
foldExprAS (iff,apply,conint,conbool) = fold
where fold (If ce te ee) = iff (fold ce) (fold te) (fold ee)
fold (Apply fe ae) = apply (fold fe) (fold ae)
fold (ConInt i) = conint i
fold (ConBool b) = conbool b
Listado 15: ExprAbstractSyntax.hs
gram�atica libre de contexto como sintaxis concreta del lenguaje.
Expr0 ! if Expr1 then Expr1 else Expr1 j Expr1
Expr1 ! Expr2 Expr2�
Expr2 ! Int j Bool
donde Int genera enteros y Bool booleanos. El listado 15 expone una sintaxis abs-
tracta para nuestro lenguaje.
Note que usamos un solo tipo de datos para la sintaxis abstracta, en vez de tres
tipos de datos (uno para cada no terminal); esto simpli�ca el c�odigo un poco. El
listado 15 tambi�en contiene la de�nici�on de un fold, y el tipo de �algebra para la
sintaxis abstracta.
Encontrar�a un analizador para las expresiones en el listado 16.
8.3.2 Una m�aquina de pila
En la Secci�on 6.4.4 hemos de�nido una m�aquina de pila con la cual se pueden
evaluar expresiones aritm�eticas simples. de�nimos aqu�� una maquina de pila que
tiene algunas instrucciones m�as. El lenguaje de la secci�on anterior ser�a compilado
en c�odigo para �esta m�aquina de pila en la siguiente secci�on.
La m�aquina de pila que usaremos tiene las siguientes instrucciones:
� puede cargar un entero;
� puede cargar un booleano;
� dado un argumento y una funci�on en la pila, puede invocar a la funci�on con el
argumento;
� puede colocar una etiqueta en el c�odigo;
144 Gram�aticas de atributos
sptoken :: String -> Parser Char String
sptoken s = (\_ b _ -> b) <$>
many (symbol ' ') <*> token s <*> many1 (symbol ' ')
boolean = const True <$> token "True" <|> const False <$> token "False"
parseExpr :: Parser Char ExprAS
parseExpr = expr0
where expr0 = (\a b c d e f -> If b d f) <$>
sptoken "if"
<*> parseExpr
<*> sptoken "then"
<*> parseExpr
<*> sptoken "else"
<*> parseExpr
<|> expr1
expr1 = chainl expr2 (const Apply <$> many1 (symbol ' '))
<|> expr2
expr2 = ConBool <$> boolean
<|> ConInt <$> natural
Listado 16: ExprParser.hs
� dado un booleano en la pila, puede saltar a una etiqueta siempre que el bo-
oleano sea falso;
� puede saltar a una etiqueta (incondicionalmente).
El listado 17 muestra el tipo de datos para las instrucciones.
8.3.3 Compilaci�on a la m�aquina de pila
>C�omo compilamos las diferentes expresiones a c�odigo de la m�aquina de pila? Que-
remos de�nir una funci�on compile del tipo
compile :: ExprAS -> [InstructionSM]
data InstructionSM = LoadInt Int
| LoadBool Bool
| Call
| SetLabel Label
| BrFalse Label
| BrAlways Label
type Label = Int
Listado 17: InstructionSM.hs
8.3 Un peque~no compilador 145
� ConInt i se traduce a LoadInt i.
compile (ConInt i) = [LoadInt i]
� ConBool b se traduce a LoadBool b.
compile (ConBool b) = [LoadBool b]
� Una aplicaci�on Apply f x se traduce compilando en primer lugar el argumento
x, luego la `funci�on' f (puesto que al presente es imposible de�nir funciones en
nuestro lenguaje, hemos puesto la palabra funci�on entre comillas) y �nalmente
colocando un Call en el tope de la pila
compile (Apply f x) = compile x ++ compile f ++ [Call]
� Una expresi�on if - then - else If ce te ee se traduce compilando en primer
lugar la expresi�on condicional ce. Luego saltamos a una etiqueta (que ser�a
colocada luego del c�odigo de la expresi�on else ee) en caso que la expresi�on
booleana resultara falsa. Luego compilamos la expresi�on then te. Despu�es
de la expresi�on then siempre saltamos al �nal del c�odigo de la expresi�on if-
then- else, para la cual necesitamos otra etiqueta. Luego determinamos la
etiqueta para la expresi�on else, compilamos la expresi�on else ee, y �nalmente
determinamos la etiqueta para el �nal de la expresi�on if-then-else.
compile (If ce te ee) = compile ce
++ [BrFalse ?lab1]
++ compile te
++ [BrAlways ?lab2]
++ [SetLabel ?lab1]
++ compile ee
++ [SetLabel ?lab2]
Note que aqu�� usamos etiquetas, pero >de d�onde vienen estas etiquetas?
De la descripci�on anterior se desprende que tambi�en necesitamos etiquetas cuando
compilamos una expresi�on. A~nadimos un argumento etiqueta (un entero, utilizado
para la primera etiqueta en el c�odigo compilado) a la funci�on compile y queremos
que la funci�on compile retorne la primera etiqueta no utilizada. Cambiamos el tipo
de funci�on compile de la siguiente manera.
compile :: ExprAS -> Label -> ([InstructionSM],Label)
type Label = Int
Los cuatro casos en la de�nici�on de compile tienen que ocuparse de las etiquetas.
Obtenemos la siguiente de�nici�on de compile:
compile (ConInt i) = \l -> ([LoadInt i],l)
compile (ConBool b) = \l -> ([LoadBool b],l)
compile (Apply f x) = \l -> let (xc,l') = compile x l
146 Gram�aticas de atributos
compile = foldExprAS compileAlgebra
compileAlgebra :: ExprASAlgebra (Label -> ([InstructionSM],Label))
compileAlgebra = (\cce cte cee -> \l ->
let (cc,l') = cce (l+2)
(tc,l'') = cte l'
(ec,l''') = cee l''
in ( cc
++ [BrFalse l]
++ tc
++ [BrAlways (l+1)]
++ [SetLabel l]
++ ec
++ [SetLabel (l+1)]
,l'''
)
,\cf cx -> \l -> let (xc,l') = cx l
(fc,l'') = cf l'
in (xc ++ fc ++ [Call],l'')
,\i -> \l -> ([LoadInt i],l)
,\b -> \l -> ([LoadBool b],l)
)
Listado 18: CompileExpr.hs
(fc,l'') = compile f l'
in (xc ++ fc ++ [Call],l'')
compile (If ce te ee) = \l -> let (cc,l') = compile ce (l+2)
(tc,l'') = compile te l'
(ec,l''') = compile ee l''
in ( cc
++ [BrFalse l]
++ tc
++ [BrAlways (l+1)]
++ [SetLabel l]
++ ec
++ [SetLabel (l+1)]
,l'''
)
La funci�on compile es un fold, el tipo del soporte de su �algebra es una funci�on del
tipo Label -> ([InstructionSM],Label). La de�nici�on de la funci�on compile
como un fold se muestra en el listado 18.
8.4 Ejercicios 147
8.4 Ejercicios
Ejercicio 8.3 . Extender el ejemplo de generaci�on de c�odigo a~nadiendo variables al tipo de
datos Expr. /
Ejercicio 8.4 . Extender el ejemplo de generaci�on de c�odigo a~nadiendo tambi�en de�niciones al
tipo de datos Expr. /
Cap��tulo 9
Pumping Lema: el poder de expresi�on
de los lenguajes
introducci�on
En este texto hemos presentado muchas formas de mostrar que un lenguaje es regular
o libre de contexto, pero hasta ahora no hemos dado alg�un medio para mostrar la no
regularidad o la no libertad de contexto de un lenguaje. En este cap��tulo llenamos
este vac��o introduciendo el llamado Pumping Lemma. Por ejemplo el Pumping
Lemma para lenguajes regulares dice
SI el lenguaje L es regular,
ENTONCES tiene la siguiente propiedad P : cada palabra su�cientemente larga
w 2 L tiene una subcadena que puede repetirse cualquier cantidad de veces,
dando como resultado siempre otra palabra de L
En la aplicaci�on de los Pumping Lemma estos se usan en una forma contrapositiva.
En el caso regular esto signi�ca que si P no es v�alida uno puede concluir que L no
es regular. Aunque las ideas subyacentes al Pumping Lemma son muy simples, una
formulaci�on precisa no lo es. Como consecuencia, toma alg�un esfuerzo familiarizarse
en la aplicaci�on del pumping lemma.
objetivos
Despu�es de haber estudiado este cap��tulo ser�as capaz de:
� demostrar que un lenguaje no es regular;
� demostrar que un lenguaje no es libre de contexto;
� identi�car lenguajes y gram�aticas como regulares, libres de contexto o ninguna
de �estas;
� dar ejemplos de lenguajes no regulares y/o no libres de contexto.
9.1 El Pumping Lemma para lenguajes regulares
En esta secci�on damos el Pumping Lemma para lenguajes regulares. El lema da
una propiedad que satisfacen todos los lenguajes regulares. La propiedad es una
a�rmaci�on de la forma: en frases m�as largas que una cierta longitud, se puede
149
150 Pumping Lema: el poder de expresi�on de los lenguajes
identi�car una subcadena que puede reproducirse resultando en una cadena que a�un
es frase del lenguaje. La idea subyacente a esta propiedad es simple: los lenguajes
regulares son aceptados por aut�omatas �nitos. Dado un AFD para un lenguaje
regular, una frase de un lenguaje describe un camino desde el estado inicial a alg�un
estado �nito. Cuando la longitud de tal frase excede la cantidad de estados, entonces
por lo menos un estado se visita dos veces; consecuentemente el camino contiene un
ciclo que puede ser repetido tan a menudo como se desee. La prueba del siguiente
lema se da en la secci�on 9.3.
Teorema 1: Pumping Lemma para lenguajes regulares
Sea L un lenguaje regular. Entonces
existe n 2 IIN :
para todo x; y; z : xyz 2 L y jyj � n :
existe u; v; w : y = uvw y jvj > 0 :
para todo i 2 IIN : xuv iwz 2 L
2
Este pumping lemma es �util para mostrar que un lenguaje no pertenece a la familia
de los lenguajes regulares. Su aplicaci�on es t��pica de los Pumping Lemma en general;
se usan negativamente para mostrar que un lenguaje dado no pertenece a alguna
familia.
El Teorema 1 nos permite demostrar que un lenguaje L no es regular mostrando
que:
para todo n 2 IIN :
existe x; y; z : xyz 2 L y jyj � n :
para todo u; v; w : y = uvw y jvj > 0 :
existe i 2 IIN : xuv iwz 62 L
Note que si n = 0, podemos elegir y = �, y como no existe v tal que jvj > 0 y tal que
y = uvw, la a�rmaci�on anterior vale para todos aquellos v (en realidad ninguno).
Como ejemplo, probaremos que el lenguaje L = fambm j m � 0g no es regular.
Sea n 2 IIN.
Tome s = anbn con x = �, y = an, y z = b
n.
Sea u; v; w tal que y = uvw con v 6= �, es decir, u = ap, v = aq y w = ar con
p+ q + r = n y q > 0.
Tome i = 2, entonces
xuv2wz 62 L
( defn. x; u; v; w; z, c�alculo
ap+2q+rbn 62 L
( p+ q + r = n
n+ q 6= n
( aritm�etica
q > 0
( q > 0
verdad
9.2 El Pumping Lemma para lenguajes libres de contexto 151
Note que aqu�� usamos el Pumping Lemma (y no su demostraci�on) para probar que
un lenguaje no es regular. Este tipo de demostraci�on puede verse como un tipo de
juego: `para todo' representa un elemento arbitrario que puede ser elegido por el
oponente; `existe' representa un elemento particular que se puede elegir. La elecci�on
de los elementos correctos ayuda a `ganar' el juego, donde ganar signi�ca probar que
un lenguaje no es regular.
Ejercicio 9.1 . Probar que el siguiente lenguaje no es regular
fak2
j k � 0g
/
Ejercicio 9.2 . Mostrar que el siguiente lenguaje no es regular.
fx j x 2 fa; bg� ^ nr a x < nr b xg
donde nr a x es la cantidad de ocurrencias de a en x. /
Ejercicio 9.3 . Demostrar que el siguiente lenguaje no es regular
fakbm j k � m � 2kg
/
Ejercicio 9.4 . Mostrar que el siguiente lenguaje no es regular.
fakblam j k > 5 ^ l > 3 ^m � lg
/
9.2 El Pumping Lemma para lenguajes libres de con-
texto
El Pumping Lemma para lenguajes libres de contexto proporciona una propiedad
que es satisfecha por todos los lenguajes libres de contexto. Esta propiedad es
una a�rmaci�on de la forma: en frases que exceden una cierta longitud, se pueden
identi�car dos sublistas de longitud limitada que al ser reproducidas producen frases
que todav��a pertenecen al lenguaje. La idea subyacente a esta propiedad es la
siguiente. Los lenguajes libres de contexto se describen usando gram�aticas libres
de contexto. Para cada frase en el lenguaje existe un �arbol de derivaci�on. Cuando
las frases tienen un �arbol de derivaci�on que es m�as alto que la cantidad de no
terminales, entonces por lo menos un no terminal aparece dos veces en los nodos del
�arbol; consecuentemente se puede insertar un sub�arbol tan frecuentemente como se
desee.
Como ejemplo de una aplicaci�on del Pumping Lemma, considere la gram�atica libre
de contexto con las siguientes producciones:
S ! aAb
A ! cBd
A ! e
B ! fAg
152 Pumping Lema: el poder de expresi�on de los lenguajes
El siguiente �arbol de an�alisis representa la derivaci�on de la frase acfegdb.
S
��������
????
????
a A
��������
????
????
b
c B
��������
????
????
d
f A g
e
Si reemplazamos el sub�arbol con ra��z en la aparici�on inferior del no terminal A por el
sub�arbol con ra��z en la aparici�on superior del no terminal A obtenemos el siguiente
�arbol de an�alisis:
S
��������
????
????
a A
��������
????
????
b
c B
��������
????
????
d
f A
��������
????
????
g
c B
��������
????
????
d
f A g
e
Este �arbol de an�alisis representa la derivaci�on de la frase acfcfegdgdb. As�� `empuja-
mos'1 la derivaci�on de la frase acfegdb hacia la derivaci�on de la frase acfcfegdgdgdb.
Repitiendo este paso una vez m�as, obtenemos un �arbol de derivaci�on para la frase
acfcfcfegdgdgdb
Podemos aplicar repetidamente este proceso para obtener �arboles de derivaci�on para
todas las frases de la forma
a(cf)ie(gd)ib
1NT: en ingl�es \pump".
9.2 El Pumping Lemma para lenguajes libres de contexto 153
Para i � 0. Se obtiene el caso i = 0 si reemplazamos en el �arbol de an�alisis para
la frase acfegdb el sub�arbol establecido por la ocurrencia superior del no terminal A
por el sub�arbol establecido por la ocurrencia inferior de A:
S
��������
>>>>
>>>
a A b
e
Este es un �arbol de derivaci�on para la frase aeb. Este paso puede verse como un
paso `de empuje' negativo.
La prueba del siguiente lema se da en la secci�on 9.3.
Teorema 2: Pumping Lemma para lenguajes libres de contexto
Sea L un lenguaje libre de contexto. Entonces
existe c; d : c; d 2 IIN :
para todo z : z 2 L y jzj > c :
existe u; v; w; x; y : z = uvwxy y jvx j > 0 y jvwx j � d :
para todo i 2 IIN : uv iwx iy 2 L
2
El Pumping Lemma es una herramienta con la cual probamos que un lenguaje
dado no es libre de contexto. La prueba por obligaci�on es mostrar que la propiedad
compartida por todos los lenguajes libres de contexto no se satisface para el lenguaje
en consideraci�on.
El Teorema 2 nos permite probar que un lenguaje L no es libre de contexto mos-
trando que
para todo c; d : c; d 2 IIN :
existe z : z 2 L and jzj � c :
para todo u; v; w; x; y : z = uvwxy and jvx j > 0 and jvwx j � d :
existe i 2 IIN : uv iwx iy 62 L
Por ejemplo, probaremos que el lenguaje T de�nido por
T = fanbncn j n > 0g
no es libre de contexto.
Demostraci�on: Sea c; d 2 IIN.
Tome z = arbrcr con r = max(c; d).
Sea u; v; w; x; y tal que that z = uvwxy, jvxj > 0 y jvwx j � d
Note que la elecci�on de r garantiza que la subcadena vwx tenga una de las siguientes
formas:
� vwx consiste de s�olo as, o s�olo bs, o s�olo cs.
� vwx contiene ambos as y bs, o ambos bs y cs.
154 Pumping Lema: el poder de expresi�on de los lenguajes
as��, vwx no contiene as, bs, y cs.
Tome i = 0, entonces
� Si vwx consiste de s�olo as, o s�olo bs, o s�olo cs, entonces es imposible escribir
la cadena uwy como asbscs para alg�un s, puesto que se decrementa solamente
la cantidad de terminales de un tipo.
� Si vwx contiene ambos as y bs, o ambos bs y cs queda en alguna parte del
borde entre as y bs, o en el borde entre bs y cs. Entonces la cadena uwy puede
escribirse como
uwy = asbtcr
uwy = arbpcq
para alg�un s, t, p, q, respectivamente, al menos uno de s y t o de p y q es
menor que r. Nuevamente, esta lista no es elemento de T .
2
Ejercicio 9.5 . Probar que el siguiente lenguaje no es libre de contexto
fak2
j k � 0g
/
Ejercicio 9.6 . Probar que el siguiente lenguaje no es libre de contexto
fai j i es un n�umero primo g
/
Ejercicio 9.7 . Probar que el siguiente lenguaje no es libre de contexto
fww j w 2 fa; bg�g
/
9.3 Demostraciones de los Pumping Lemma
Esta secci�on damos las demostraciones de los Pumping Lemma.
Demostraci�on: del Pumping Lemma para lenguajes regulares, Teorema 1.
Como L es un lenguaje regular, existe un aut�omata de estados �nitos determin��stico
D tal que L = Ldfa D.
Sea n la cantidad de estados de D.
Sea s un elemento de L, y una sublista de s tales que jyj � n y s = xyz .
Considere la secuencia de estados por los que pasa D mientras procesa y. Como
length(y) � n, esta secuencia tiene m�as de n entradas, por tanto al menos un estado
ocurre dos veces, digamos el estado A.
Tome u; v; w como sigue
� u es la parte inicial de y procesada antes de la primera ocurrencia de A,
� v es la parte (no vac��a) de y procesada a partir de la primera a la segunda
ocurrencia de A
� w es la parte restante de y
9.3 Demostraciones de los Pumping Lemma 155
Note que D pod��a haber evitado el procesamiento de v, y por tanto habr��a aceptado
xuwz . Adem�as, D puede repetir el procesamiento entre la primera y la segunda
ocurrencia de A tanto como se desee, y por tanto acepta xuv iwz para todo i 2 IIN.
Formalmente una simple demostraci�on por inducci�on muestra que (8i : i � 0 :
xuviwz 2 L). 2
Demostraci�on: del Pumping Lemma para lenguajes libres de contexto, Teorema
2.
Sea G = (T;N;R; S) una gram�atica libre de contexto tal que L = L(G). Sea m
la longitud del lado derecho de cualquier producci�on, y sea k la cantidad de no
terminales de G.
Tome c = mk. En el lema 3 de abajo probamos que si z es una lista tal que jzj > c,
entonces en todos los �arboles de derivaci�on para z existe un camino de longitud de
al menos k+1.
Sea z 2 L tal que jzj > c. Como la gram�atica G tiene k no terminales, existe al
menos un no terminal que ocurre m�as de una vez en el camino de longitud k+1 (que
contiene k+2 s��mbolos, del que al menos uno es un terminal, y todos los otros son
no terminales) de un �arbol de derivaci�on para z. Considere el no terminal A que
satisface los siguientes requerimientos.
� A ocurre al menos dos veces en el camino de longitud k+1 de un �arbol de
derivaci�on para z.
Llame w a la lista correspondiente al �arbol de derivaci�on con ra��z en el A inferior, y
llame vwx a la lista correspondiente al �arbol de derivaci�on con ra��z en la A superior
(que contiene la lista w ).
� Se elige A tal que a lo sumo uno de v y x es igual a �.
� Finalmente, suponemos que debajo de la ocurrencia superior de A ning�un
otro no terminal que satisface los mismos requerimientos ocurre, es decir, las
dos As son el par de no terminales m�as abajo del �arbol que satisfacen los
requerimientos citados anteriormente.
Primero probamos que el no terminal A satisface los requerimientos citados ante-
riormente. Probamos esta a�rmaci�on por contradicci�on. Suponga que para todos
los no terminales A que aparecen dos veces en un camino de longitud de al menos
k+1, ambos v y x son iguales a �. v y x son los extremos de la lista vwx que corres-
ponde al �arbol con ra��z en el A superior. Entonces podemos reemplazar el �arbol con
ra��z en el A superior por el �arbol con ra��z en el A inferior sin cambiar la lista que
corresponde al �arbol. Entonces podemos reemplazar todos los caminos de longitud
por lo menos k+1 por un camino de longitud al menos k. Pero esto contradice el
Teorema 3 de abajo. Por lo tanto existe un no terminal A que sa�sface los requeri-
mientos mencionados arriba.
Existe un �arbol de derivaci�on para z en el cual el camino desde la ocurrencia supe-
rior de A hasta la hoja tiene una longitud de al menos k+1, ya que o bien debajo
de A ning�un no terminal ocurre dos veces, o bien existen uno o m�as no terminales
B que ocurren dos veces, pero las listas de los bordes v0 y x0 de la lista x0w0x0 que
corresponde al �arbol con ra��z en el B superior son vac��os. Como las listas v0 y x0 son
vac��as, podemos reemplazar el �arbol establecido en la ocurrencia superior de B por
el �arbol establecido en la ocurrencia inferior de B sin cambiar la lista correspondien-
te al �arbol de derivaci�on. Como podemos hacer esto para todos los no terminales
156 Pumping Lema: el poder de expresi�on de los lenguajes
B que ocurren dos veces despu�es de la ocurrencia superior de A, existe un �arbol de
derivaci�on para z en el cual el camino desde la ocurrencia superior de A hasta la
hoja tiene una longitud de a lo sumo k+1. Del Teorema 3 que damos m�as abajo,
sale que la longitud de vwz es a lo sumo mk+1, por lo tanto de�nimos d = mk+1.
Suponga que z = uvwxy , es decir, la lista correspondiente al sub�arbol a la izquierda
(derecha) de la ocurrencia superior de A es u (y). Esta situaci�on se ilustra como
sigue:
S
rrrrrrrrrrrrr
��������
<<<<
<<<<
LLLL
LLLL
LLLL
L
u A
rrrrrrrrrrrrr
��������
<<<<
<<<<
LLLL
LLLL
LLLL
L y
v A
�������
????
??? x
w
Probamos por inducci�on que (8i : i � 0 : uv iwx iy 2 L). En esta demostraci�on
aplicamos el proceso de substituci�on del �arbol descrito en el ejemplo antes del lema.
Para i = 0 tenemos que probar que la lista uwy es una frase de L. La lista uwy se
obtiene si el �arbol con ra��z en el A superior se reemplaza por el �arbol establecido
en el A inferior. Suponga que para todo i � n tenemos que uv iwx iy 2 L. La lista
uv i+1wx i+1y se obtiene si el �arbol establecido en el A inferior del �arbol de derivaci�on
para uv iwx iy 2 L se reemplaza por el �arbol establecido en el A superior. Esto
prueba el paso de inducci�on. 2
Esta demostraci�on del pumping lemma frecuentemente re�ere al siguiente Teorema.
Teorema 3:
Sea G una gram�atica libre de contexto, y suponga que el lado derecho m�as largo
de sus producciones tiene longitud m. Sea t un �arbol de derivaci�on para una lista
z 2 L G. Si height t � j, entonces jzj � mj . 2
Demostraci�on: Demostraremos un resultado ligeramente m�as fuerte: si t es un
�arbol de derivaci�on para una lista z, pero la ra��z de t no es necesariamente el s��mbolo
inicial, y height t � j, entonces jzj � mj . Probamos esta a�rmaci�on por inducci�on
sobre j.
Para el caso base suponemos j = 1. Entonces el �arbol t corresponde a una �unica
producci�on de G, y como el lado derecho de cualquier producci�on tiene longitud a
lo sumo m, tenemos que jzj � m = mj .
Para el paso inductivo, suponemos que para todo �arbol de derivaci�on t cuya altura es
a lo sumo j tenemos jzj � mj , donde z es la lista correspondiente a t. Supongamos
tener un �arbol t de altura j+1. Sea A la ra��z de t, y supongamos que la parte
superior del �arbol corresponde a la producci�on A! v de G. Para todos los �arboles
s con ra��z en los s��mbolos de v tenemos que height s � j, de esta manera, la hip�otesis
de inducci�on se aplica a estos �arboles, y las listas correspondientes a los �arboles con
ra��z en los s��mbolos de v todas tienen longitud de a lo sumo mj . Como A ! v es
una producci�on de G, y como el lado derecho m�as largo de cualquier producci�on
9.4 Ejercicios 157
tiene longitud m, la lista correspondiente al �arbol establecido en A tiene longitud
de a lo sumo m�mj = mj+1, lo que prueba el paso inductivo. 2
resumen
Esta secci�on presenta los Pumping Lemma. �Estos se usan para probar que los
lenguajes no son regulares o no son libres de contexto.
9.4 Ejercicios
Ejercicio 9.8 . Probar que el siguiente lenguaje no es regular.
fx j x 2 f0; 1g� ^ nr 1 x = nr 0 xg
donde la funci�on nr toma un elemento a y una lista x, y devuelve la cantidad de
ocurrencias de a en x. /
Ejercicio 9.9 . Considere el siguiente lenguaje:
faibj j 0 � i � jg
1. >Es libre de contexto? Si lo es, dar una gram�atica libre de contexto y demostrar
que la gram�atica genera el lenguaje. Si no lo es, indicar por qu�e.
2. >El lenguaje es regular? Si lo es, dar una gram�atica regular y demostrar que
esta gram�atica genera el lenguaje. Si no lo es indicar por qu�e.
/
Ejercicio 9.10 . Considere el siguiente lenguaje:
fww j w 2 fa; bg�g
1. >Es libre de contexto? Si lo es, dar una gram�atica libre de contexto y demostrar
que la gram�atica genera el lenguaje. Si no lo es, indicar por qu�e.
2. >El lenguaje es regular? Si lo es, dar una gram�atica regular y demostrar que
esta gram�atica genera el lenguaje. Si no lo es indicar por qu�e.
/
Ejercicio 9.11 . Considere la gram�atica G con las siguientes producciones.
8>>>>><>>>>>:
S ! fAg
S ! �
A ! S
A ! AA
A ! gf
1. >Es la gram�atica . . .
� . . . libre de contexto?
� . . . regular?
158 Pumping Lema: el poder de expresi�on de los lenguajes
>Por qu�e?
2. Indicar el lenguaje de G sin referirse a G. Demostrar que la descripci�on es
correcta.
3. >El lenguaje de G es . . .
� . . . libre de contexto?
� . . . regular?
>Por qu�e?
/
Ejercicio 9.12 . Considere la gram�atica G con las siguientes producciones.
8>>><>>>:
S ! �
S ! 0
S ! 1
S ! S0
1. >Es la gram�atica . . .
� . . . libre de contexto?
� . . . regular?
>Por qu�e?
2. Indicar el lenguaje de G sin referirse a G. Demostrar que la descripci�on es
correcta.
3. >El lenguaje de G es . . .
� . . . libre de contexto?
� . . . regular?
>Por qu�e?
/
Bibliograf��a
[1] A.V. Aho, Sethi R., and J.D. Ullman. Compilers - Principles, Techniques and
Tools. Addison-Wesley, 1986.
[2] R.S. Bird. Using circular programs to eliminate multiple traversals of data.
Acta Informatica, 21:239{250, 1984.
[3] R.S. Bird and P. Wadler. Introduction to Functional Programming. Prentice
Hall, 1988.
[4] W.H. Burge. Parsing. In Recursive Programming Techniques. Addison-Wesley,
1975.
[5] J. Fokker. Functional parsers. In J. Jeuring and E. Meijer, editors, Advanced
Functional Programming, volume 925 of Lecture Notes in Computer Science.
Springer-Verlag, 1995.
[6] R. Harper. Proof-directed debugging. Journal of Functional Programming,
1999. To appear.
[7] G. Hutton. Higher-order functions for parsing. Journal of Functional Program-
ming, 2(3):323 { 343, 1992.
[8] G. Hutton and E. Meijer. Monadic parsing in haskell. Journal of Functional
Programming, 8(4):437 { 444, 1998.
[9] B.W. Kernighan and R. Pike. Regular expressions | languages, algorithms,
and software. Dr. Dobb's Journal, April:19 { 22, 1999.
[10] D.E. Knuth. Semantics of context-free languages. Math. Syst. Theory, 2(2):127{
145, 1968.
[11] Niklas R�ojemo. Garbage collection and memory eÆciency in lazy functional
languages. PhD thesis, Chalmers University of Technology, 1995.
[12] S.D. Swierstra and P.R. Azero Alcocer. Fast, error correcting parser combina-
tors: a short tutorial. In SOFSEM'99, 1999.
[13] P. Wadler. How to replace failure by a list of successes: a method for excep-
tion handling, backtracking, and pattern matching in lazy functional languages.
In J.P. Jouannaud, editor, Functional Programming Languages and Computer
Architecture, pages 113 { 128. Springer, 1985. LNCS 201.
[14] P. Wadler. Monads for functional programming. In J. Jeuring and E. Meijer,
editors, Advanced Functional Programming. Springer, 1995. LNCS 925.
159
Ap�endice A
El m�odulo Stack
module Stack
( Stack
, emptyStack
, isEmptyStack
, push
, pushList
, pop
, popList
, top
, split
, mystack
)
where
newtype Stack x = MkS [x]
instance Show x => Show (Stack x) where
showsPrec = showsPrecStack
showsPrecStack :: Show x => Int -> Stack x -> String -> String
showsPrecStack p (MkS xs) = showList xs
emptyStack :: Stack x
emptyStack = MkS []
isEmptyStack :: Stack x -> Bool
isEmptyStack (MkS xs) = null xs
push :: x -> Stack x -> Stack x
push x (MkS xs) = MkS (x:xs)
pushList :: [x] -> Stack x -> Stack x
pushList xs (MkS ys) = MkS (xs ++ ys)
161
162 El m�odulo Stack
pop :: Stack x -> Stack x
pop (MkS xs) = if isEmptyStack (MkS xs)
then error "pop on emptyStack"
else MkS (tail xs)
popIf :: Eq x => x -> Stack x -> Stack x
popIf x stack = if top stack == x
then pop stack
else error "argument and top of stack don't match"
popList :: Eq x => [x] -> Stack x -> Stack x
popList xs stack = foldr popIf stack (reverse xs)
top :: Stack x -> x
top (MkS xs) = if isEmptyStack (MkS xs)
then error "top on emptyStack"
else head xs
split :: Int -> Stack x -> ([x], Stack x)
split 0 stack = ([], stack)
split n (MkS []) = error "attempt to split the emptystack"
split (n+1) (MkS (x:xs)) = (x:ys, stack')
where
(ys, stack') = split n (MkS xs)
mystack = MkS [1,2,3,4,5,6,7]
Ap�endice B
Respuestas a los ejercicios
2.1 Tres de las cuatro cadenas est�an en L�:
abaabaaabaa; aaaabaaaa; baaaaabaa
2.2 f esg.
2.3
� L
= De�nici�on de concatenaci�on de lenguajes
fst j s 2 �; t 2 Lg
= s 2 �
�
y las otras igualdades se demuestran de forma similar.
2.4 Ln debe satisfacer:
L0 = ?
Ln+1 = LnL
Esto sigue de que queremos que L0 satisfaga
L0L = L
Eligiendo L0 = f�g tenemos:
L0L
= De�nici�on de concatenaci�on de lenguajes
fst j s 2 f�g; t 2 Lg
= De�nici�on de concatenaci�on de cadenas
ft j t 2 Lg
= De�nici�on de comprensi�on de conjuntos
L
como quer��amos probar.
2.5 El operador estrella sobre el lenguaje L concatena elementos individuales de L,
en cambio el operador estrella sobre conjuntos los pone en una lista.
163
164 Respuestas a los ejercicios
2.6 La secci�on 2.1 contiene una de�nici�on inductiva del conjunto de secuencias sobre
un conjunto arbitrarioX . Varias de�niciones sint�acticas para tales conjuntos pueden
seguir inmediatamente de esto.
1. Una gram�atica para X = fag es
S ! �
S ! aS
2. Una gram�atica para X = fa; bg es
S ! �
S ! XS
X ! a j b
2.7 An�alogo a la construcci�on de la gram�atica para PAL .
P ! � j a j b j aPa j bPb
2.8 An�alogo a la construcci�on de PAL.
M ! � j aMa j bMb
2.9 Primero dar una de�nici�on inductiva para secuencias de paridad y luego dar la
siguiente gram�atica:
P ! � j 1P1 j 0P j P0
Hay muchas otras soluciones.
2.10 Primero dar una de�nici�on inductiva de L, luego dar la gram�atica:
S ! � j aSb j bSa j SS
Nuevamente, hay muchas otras soluciones.
2.11 Una frase es una forma sentencial que consiste solamente de terminales que
se pueden derivar en cero o m�as pasos de derivaci�on a partir del s��mbolo inicial
(para ser m�as precisos: la forma sentencial que consiste s�olo del s��mbolo inicial). El
s��mbolo inicial es un no terminal. Los no terminales de una gram�atica no pertene-
cen al alfabeto (el conjunto de terminales) del lenguaje que describimos usando la
gram�atica. Entonces el s��mbolo inicial no puede ser una frase del lenguaje. Como
consecuencia tenemos que realizar por lo menos un paso de derivaci�on a partir del
s��mbolo inicial antes de que terminemos en una frase del lenguaje.
2.12 El lenguaje consiste solamente de la cadena vac��a, es decir, f�g.
2.13 Esta gram�atica genera el lenguaje vac��o, es decir, �.
2.14 Las frases de este lenguaje consisten en cero o m�as concatenaciones de ab.
2.15 Si. Todo lenguaje �nito es libre de contexto. Se puede obtener una gram�atica
libre de contexto tomando un no terminal y a~nadiendo una regla de producci�on para
Respuestas a los ejercicios 165
cada frase del lenguaje. Para el lenguaje del ejercicio 2.1 esto d�a:
S ! ab
S ! aa
S ! baa
2.18 Una gram�atica equivalente para listas de bits es:
L = BR
R = � j ,BR
B = 0 j 1
2.19
1 Esta gram�atica genera f a2nbm j n � 0; m � 0g
2 Una gram�atica equivalente no recursiva a izquierda es:
S ! AB
A ! � j aaA
B ! � j bB
2.20
Expr ! Expr+Expr j Expr*Expr j Int
Donde Int es un no terminal que produce enteros.
2.21 Pal��ndromes
1 data Pal = Pal1 | Pal2 | Pal3 | Pal4 Pal | Pal5 Pal
aPal1 = Pal4 (Pal5 (Pal4 Pal1))
aPal2 = Pal5 (Pal4 Pal2)
2 a2cPal Pal1 = ""
a2cPal Pal2 = "a"
a2cPal Pal3 = "b"
a2cPal (Pal4 p) = "a" ++ a2cPal p ++ "a"
a2cPal (Pal5 p) = "b" ++ a2cPal p ++ "b"
3 aCountPal Pal1 = 0
aCountPal Pal2 = 1
aCountPal Pal3 = 0
aCountPal (Pal4 p) = aCountPal p + 2
aCountPal (Pal5 p) = aCountPal p
2.22 Pal��ndromes espejo
1 data Mir = Mir1 | Mir2 Mir | Mir3 Mir
aMir1 = Mir2 (Mir3 (Mir2 Mir1))
aMir2 = Mir2 (Mir3 (Mir3 Mir1))
2 a2cMir Mir1 = ""
a2cMir (Mir2 m) = "a" ++ a2cMir m ++ "a"
a2cMir (Mir3 m) = "b" ++ a2cMir m++"b"
3 m2pMir Mir1 = Pal1
m2pMir (Mir2 m) = Pal4 (m2pMir m)
m2pMir (Mir3 m) = Pal5 (m2pMir m)
2.23 Secuencias de paridad
166 Respuestas a los ejercicios
1 data Even = Even1 Even | Even2 Odd | Even3
data Odd = Odd1 Odd | Odd2 Even
aEven1 = Even2 (Odd1 (Odd2 (Even1 (Even1 Even3))))
aEven2 = Even1 (Even2 (Odd1 (Odd2 (Even1 Even3))))
2 a2cEven (Even1 x) = a2cEven x ++ "0"
a2cEven (Even2 y) = a2cOdd y ++ "1"
a2cEven Even3 = ""
a2cOdd (Odd1 y) = a2cOdd y ++ "0"
a2cOdd (Odd2 x) = a2cEven x ++ "1"
a2cEven aEven1 = "00101"
a2cEven aEven2 = "01010"
3 valEven (Even1 x) = 2 * (valEven x)
valEven (Even2 y) = 2 * (valOdd y) + 1
valEven Even3 = 0
valOdd (Odd1 y) = 2 * (valOdd y)
valOdd (Odd2 x) = 2 * (valEven x) + 1
valEven aEven1 = 5
valEven aEven2 = 10
2.24 Listas de bits
1 data BitList = List1 Bit Rest
data Rest = Rest1 | Rest2 Bit Rest
data Bit = Bit1 | Bit2
aBitList1 = List1 Bit1 (Rest2 Bit2 (Rest2 Bit1 Rest1))
aBitList2 = List1 Bit1 (Rest2 Bit1 (Rest2 Bit2 Rest1))
2 a2cBitList (List1 b r) = a2cBit b ++ a2cRest r
a2cRest Rest1 = ""
a2cRest (Rest2 b r) = "," ++ a2cBit b ++ a2cRest r
a2cBit Bit1 = "0"
a2cBit Bit2 = "1"
3 concatBitList :: BitList -> BitList -> BitList
concatBitList (List1 b1 r1) (List1 b2 r2) =
List1 b1 (newRest r1 b2 r2)
newRest r1 b r = case r1 of
Rest1 -> Rest2 b r
Rest2 b' r' -> Rest2 b' (newRest r' b r)
2.25 Solo damos la notaci�on EBNF para las producciones que cambian.
Digs ! Dig�
Z ! Sign?Nat
SoS ! LoD�
LoD ! Letter j Dig
2.26
L(G?) = L(G) [ f�g
2.27 Sea L(P ) es lenguaje de�nido mediante el predicado P . Entonces tenemos
L(P1) \ L(P2) = L(�x:P1(x)^ P2(x)), etc.
Respuestas a los ejercicios 167
2.28
� Se puede generar L1 con:
S ! ZC
Z ! aZb j �
C ! c�
y generar L2 con:
S ! AZ
Z ! bZc j �
A ! a�
�
L1 \ L2 = fanbncn j n 2 IINg
Como veremos en el Cap��tulo 9, este lenguaje no es libre de contexto.
2.32 No, el lenguaje L = faab; baag tambi�en satisface L = LR.
2.36 L = fanbn j n 2 IINg. Este lenguaje tambi�en es generado por la gram�atica:
S ! aSb j �
2.39
S ! A j �
A ! aAb j ab
2.40
1. f a2n+1 j n � 0 g
2. A = aaA j a
3. A = Aaa j a
2.41
1. f abn j n � 0 g
2. X ! aY
Y ! bY j �
3. X ! aY j a
Y ! bY j b
2.42
1. S ! T j US
T ! Xa j Ua
X ! aS
U ! S j Y T
Y ! SU
Las formas sentenciales aS y SU han sido abstra��das en los no terminales X
y Y .
168 Respuestas a los ejercicios
2. S ! aSa j Ua j US
U ! S j SUaSa j SUUa
El no terminal T ha sido sustituido por sus alternativas aSa y Ua.
2.43
S ! 1O
O ! 1O j 0N
N ! 1�
2.46
1. primera derivaci�on m�as a la izquierda:
Sentence
)
Subject Predicate
)
theyPredicate
)
theyVerbNounPhrase
)
theyareNounPhrase
)
theyareAdjective Noun
)
theyare yingNoun
)
theyare yingplanes
2. segunda derivaci�on m�as a la izquierda:
Sentence
)
Subject Predicate
)
theyPredicate
)
theyAuxVerb Verb Noun
)
theyareVerb Noun
)
theyare yingNoun
)
theyare yingplanes
2.48 Esta es una gram�atica no ambigua para el lenguaje de par�entesis y corchetes
(aunque es posible que no sea trivial convencerse de que realmente es no ambigua).
S ! () j S() j (S) j S(S) j [] j S[] j [S] j S[S]
Respuestas a los ejercicios 169
2.49 Esta es una derivaci�on m�as a la izquierda para: |3|4�:
� ) �4 ) 4 ) 3 � 4 ) �3 � 4
) |3 � 4 ) |3|4 ) |3|4� ) |3|4�
Observe que la gram�atica de este ejercicio es la misma que la gram�atica presentada
a continuaci�on, excepto por nombre de los elementos.
E = E+T j T
T = T*F j F
F = 0 j 1
3.1
capital = satisfy (\s -> ('A' <= s) && (s <= 'Z'))
3.2 Un s��mbolo igual a a satisface (==a), por tanto:
symbol a = satisfy (==a)
3.3 La funci�on epsilon es un caso especial de succeed:
epsilon :: Parser s ()
epsilon = succeed ()
3.4 Sea xs :: [s], entonces
(f <$> succeed a) xs
= f de�nition of <$> g
[ (f x,ys) j (x,ys) succeed a xs ]
= f de�nition of succeed g
[ (f a,xs) ]
= f de�nition of succeed g
succeed (f a) xs
3.5 The type and results of list <$> symbol 0a0 are:
list <$> symbol 'a' :: Parser Char (String -> String)
(list <$> symbol) 'a' [] = []
(list <$> symbol) 'a' (x:xs) | x == 'a' = [(list x,xs)]
| otherwise = []
3.6 El tipo y resultados de list <$> symbol 'a' <*> p son:
list <$> symbol 'a' <*> p :: Parser Char String
(list <$> symbol 'a' <*> p) [] = []
(list <$> symbol 'a' <*>) p (x:xs)
| x == 'a' = [(list 'a' x,ys) | (x,ys) <- p xs]
| otherwise = []
170 Respuestas a los ejercicios
3.7
pBool :: Parser Char Bool
pBool = const True <$> token "True"
<|> const False <$> token "False"
3.9
1 data Pal2 = Nil | Leafa | Leafb | Twoa Pal2 | Twob Pal2
2 palin2 :: Parser Char Pal2
palin2 = (\x y z -> Twoa y) <$>
symbol 'a' <*> palin2 <*> symbol 'a'
<|> (\x y z -> Twob y) <$>
symbol 'b' <*> palin2 <*> symbol 'b'
<|> const Leafa <$> symbol 'a'
<|> const Leafb <$> symbol 'b'
<|> succeed Nil
3 palina :: Parser Char Int
palina = (\x y z -> y+2) <$>
symbol 'a' <*> palina <*> symbol 'a'
<|> (\x y z -> y) <$>
symbol 'b' <*> palina <*> symbol 'b'
<|> const 1 <$> symbol 'a'
<|> const 0 <$> symbol 'b'
<|> succeed 0
3.10
1 data English = E1 Subject Pred
data Subject = E2 String
data Pred = E3 Verb NounP | E4 AuxV Verb Noun
data Verb = E5 String | E6 String
data AuxV = E7 String
data NounP = E8 Adj Noun
data Adj = E9 String
data Noun = E10 String
2 english :: Parser Char English
english = E1 <$> subject <*> pred
subject = E2 <$> token "they"
pred = E3 <$> verb <*> nounp
<|> E4 <$> auxv <*> verb <*> noun
verb = E5 <$> token "are"
<|> E6 <$> token "flying"
auxv = E7 <$> token "are"
nounp = E8 <$> adj <*> noun
adj = E9 <$> token "flying"
noun = E10 <$> token "planes"
3.11 Como <|> usa ++, �este se eval�ua m�as e�cientemente asociando a derecha.
3.12 La funci�on es la misma que <*>, pero en cambio de aplicar el resultado del
primer analizador en el segundo, ambos se emparejan:
(<,>) :: Parser s a -> Parser s b -> Parser s (a,b)
Respuestas a los ejercicios 171
(p <,> q) xs = [((x,y),zs)
|(x,ys) <- p xs
,(y,zs) <- q ys
]
3.13 `Transformador de analizadores sint�acticos', o `modi�cador de analizadores
sint�acticos' o `posprocesador de analizadores', etcetera.
3.14 The transformator <$> does to the result part of parsers what map does to the
elements of a list.
3.15 Los combinadores de an�alisis <*> y <,> se pueden de�nir el uno en t�erminos
del otro:
p <*> q = h <$> (p <,> q)
where -- h :: (b -> a,b) -> a
h (f,y) = f y
p <,> q = (h <$> p) <*> q
where -- h :: a -> (b -> (a,b))
h x y = (x, y)
3.16 Si. Se puede combinar el analizador par�ametro de <$> con un analizador que
no consuma la entrada y siempre retorne la funci�on par�ametro de <$>:
f <$> p = succeed f <*> p
3.17
f ::
Char->Parentheses->Char->Parentheses->Parentheses
open ::
Parser Char Char
f <$> open ::
Parser Char (Parentheses->Char->Parentheses->Parentheses)
parens ::
Parser Char Parentheses
(f <$> open) <*> parens ::
Parser Char (Char->Parentheses->Parentheses)
3.18 A la izquierda. Si.
3.19
test p s = not (null (filter (null . snd) (p s)))
test p = not . null . filter (null . snd) . p
3.20 Introducir la abreviaci�on
listOfa = list <$> symbol 'a'
y usar el resultado de los ejercicios 3.5 y 3.6.
xs = []
172 Respuestas a los ejercicios
many (symbol 'a') []
= de�nici�on de many, de�nici�on de listOfa
(listOfa <*> many (symbol 'a') <|> succeed []) []
= de�nici�on de <|>
listOfa <*> many (symbol 'a')) [] ++ succeed [] []
= ejercicio 3.6 , de�nici�on de succeed
[] ++ [([], [])]
=
[([], [])]
xs = ['a']
many (symbol 'a') ['a']
= de�nici�on de many, de�nici�on de listOfa
(listOfa <*> many (symbol 'a') <|> succeed []) ['a']
= def <|>
(listOfa <*> many (symbol 'a')) ['a'] ++ succeed [] ['a']
= ejercicio 3.6 , c�alculo anterior
[(['a'],[]),([],['a'])]
xs = ['b']
many (symbol 'a') ['b']
= de�nici�on de many, de�nici�on de listOfa
(listOfa <*> many (symbol 'a') <|> succeed []) ['b']
= de�nici�on de <|>
(listOfa <*> many (symbol 'a')) ['b'] ++ succeed [] ['b']
= ejercicio 3.6
[([],['b'])]
xs = ['a','b']
many (symbol 'a') ['a','b']
=
(listOfa <*> many (symbol 'a')) ['a','b'] ++ succeed [] ['a','b']
= ejercicio 3.6 , c�alculo anterior
[ (['a'],['b']),([],['a','b'])]
xs = ['a','a','b']
many (symbol 'a') ['a','a','b']
=
(listOfa <*> many (symbol 'a')) ['a','a','b'] ++ succeed [] ['a','a','b']
= ejercicio 3.6 , c�alculo anterior
[(['a','a'],['b']),(['a'],['a','b']),([],['a','a','b'])]
Respuestas a los ejercicios 173
3.21 Se introducir�a al �ultimo la alternativa vac��a porque el combinador <|> usa
concatenaci�on de listas para unir las listas de �exitos. Esto tambi�en vale para las
llamadas recursivas; entonces se introduce primero el an�alisis sint�actico greedy de
las tres as, luego dos as con una cadena restante de un solo elemento, luego una
a, y �nalmente el resultado vac��o con la cadena original de entrada como cadena
restante.
3.23
-- Combinators for repetition
psequence :: [Parser s a] -> Parser s [a]
psequence [] = succeed []
psequence (p:ps) = list <$> p <*> psequence ps
psequence' :: [Parser s a] -> Parser s [a]
psequence' = foldr f (succeed [])
where f p q = list <$> p <*> q
choice :: [Parser s a] -> Parser s a
choice = foldr (<|>) failp
3.24
token :: Eq s => [s] -> Parser s [s]
token = psequence . map symbol
3.26
-- Applications of EBNF combinators
natural :: Parser Char Int
natural = foldl (\a b -> a*10 + b) 0 <$> many1 newdigit
integer :: Parser Char Int
integer = (const negate <$> (symbol '-')) `option` id <*> natural
identifier :: Parser Char String
identifier = list <$> satisfy isAlpha <*> greedy (satisfy isAlphanum)
parenthesised p = pack (symbol '(') p (symbol ')')
commaList :: Parser Char a -> Parser Char [a]
commaList p = listOf p (symbol ',')
3.27
expr = chainr (chainl term (const (:-:) <$> symbol '-'))
(const (:+:) <$> symbol '+')
174 Respuestas a los ejercicios
3.28 Se puede dar las demostraciones usando las leyes para comprensiones de listas,
pero aqui preferimos aprovechar la siguiente ecuaci�on
f <$> p) xs = map (cross (f,id)) (p xs) (B.1)
donde cross se de�ne como:
cross :: (a -> c,b -> d) -> (a, b) -> (c, d)
cross (f,g) (a,b) = (f a,g b)
Tiene la siguiente propiedad:
cross (f,g) . cross (h,k) = cross (f.h,g.k) (B.2)
Adem�as usaremos las siguientes leyes sobre map en la demostraci�on: map es distri-
butiva con respecto a la composici�on, concatenaci�on y la funci�on concat.
map f . map g = map (f.g) (B.3)
map f (x ++ y) = map f x ++ map f y (B.4)
map f . concat = concat . map (map f) (B.5)
1. (h <$> (f <$> p)) xs
=
map (cross (h,id)) ((f <$> p) xs)
=
map (cross (h,id)) (map (cross (f,id)) (p xs))
= f map distribuye sobre (.): B.3 g
map ((cross (h,id)) . (cross (f,id))) (p xs)
= f ecuacion B.2 para cross g
map (cross (h.f,id)) (p xs)
= f ecuacion B.1 para <$> g
((h.f) <$> p) xs
2. (h <$> (p <|> q)) xs
=
map (cross (h,id)) ((p <|> q) xs)
=
map (cross (h,id)) (p xs ++ q xs)
= f map distribuye sobre (++): B.4 g
map (cross (h,id)) (p xs) ++ map (cross (h,id)) (q xs)
= f ecuacion B.1 para <$> g
(h <$> p) xs ++ (h <$> q) xs
= f de�nicion de <|> g
((h <$> p) <|> (h <$> q)) xs
Respuestas a los ejercicios 175
3. Primero observemos que (p <*> q) xs puede escribirse como
(p <*> q) xs = concat (map (mc q) (p xs)) (B.6)
donde
mc q (f,ys) = map (cross (f,id)) (q ys)
Ahora calculamos
( ((h.) <$> p) <*> q) xs
=
concat (map (mc q) (((h.) <$> p) xs))
= f ecuacion B.1 para <$> g
concat (map (mc q) (map (cross ((h.),id)) (p xs)))
= f map distribuye sobre (.) g
concat (map (mc q . (cross ((h.),id))) (p xs))
= f propiedad B.7 abajo g
concat (map ((map (cross (h,id))) . mc q) (p xs))
= f map distribuye sobre (.) g
concat (map (map (cross (h,id)) (map (mc q) (p xs))))
= f map distribuye sobre concat: B.5 g
map (cross (h,id)) (concat (map (mc q) (p xs)))
= f ecuacion B.6 g
map (cross (h,id)) ((p <*> q) xs)
= f ecuacion B.1 para <$> g
(h <$> (p <*> q)) xs
Queda demostrar la propiedad:
mc q . cross ((h.),id) = map (cross (h,id)) . mc q (B.7)
Esta propiedad tambi�en se puede demostrar haciendo c�alculos:
((map (cross (h,id))) . mc q) (f,ys)
=
map (cross (h,id)) (mc q (f,ys))
= f de�nicion de mc q g
map (cross (h,id)) (map (cross (f,id)) (q ys))
= f map y cross distribuyen respecto a (.) g
176 Respuestas a los ejercicios
map (cross (h.f,id)) (q ys)
= f de�nicion de mc q g
mc q (h.f,ys)
= f de�nicion de cross g
(mc q . (cross ((h.),id))) (f,ys)
3.29
pPal :: Parser Char Pal
pPal = const Pal1 <$> epsilon
<|> const Pal2 <$> symbol 'a'
<|> const Pal3 <$> symbol 'b'
<|> (\o p q -> Pal4 p) <$> symbol 'a' <*> pPal <*> symbol 'a'
<|> (\o p q -> Pal5 p) <$> symbol 'b' <*> pPal <*> symbol 'b'
3.30
pMir :: Parser Char Mir
pMir = const Mir1 <$> epsilon
<|> (\o p q -> Mir2 p) <$> symbol 'a' <*> pMir <*> symbol 'a'
<|> (\o p q -> Mir3 p) <$> symbol 'b' <*> pMir <*> symbol 'b'
3.31
pBitList :: Parser Char BitList
pBitList = List1 <$> pBit <*> pRest
pBit = const Bit1 <$> symbol '0'
<|> const Bit2 <$> symbol '1'
pRest = (\a b c -> Rest2 b c) <$> symbol ',' <*> pBit <*> pRest
<|> const Rest1 <$> epsilon
3.32
-- Parser for floating point numbers
fixed :: Parser Char Float
fixed = (+) <$> (fromInteger <$> integer)
<*>
(((\x y -> y) <$> symbol '.' <*> fractpart) `option` 0.0)
fractpart :: Parser Char Float
fractpart = foldr f 0.0 <$> greedy digit
where f d n = (n + fromInteger d)/10.0
3.33
Respuestas a los ejercicios 177
float :: Parser Char Float
float = f <$> fixed
<*>
(((\x y -> y) <$> symbol 'E' <*> integer) `option` 0)
where f m e = m * power e
power e | e<0 = 1.0 / power (-e)
| otherwise = fromInteger (10^e)
4.1
data FloatLiteral = FL1 IntPart FractPart ExponentPart FloatSuffix
| FL2 FractPart ExponentPart FloatSuffix
| FL3 IntPart ExponentPart FloatSuffix
| FL4 IntPart ExponentPart FloatSuffix
deriving Show
type ExponentPart = String
type ExponentIndicator = String
type SignedInteger = String
type IntPart = String
type FractPart = String
type FloatSuffix = String
digit = satisfy isDigit
digits = many1 digit
floatLiteral = (\a b c d e -> FL1 a c d e) <$>
intPart <*> period <*> optfract <*> optexp <*> optfloat
<|> (\a b c d -> FL2 b c d) <$>
period <*> fractPart <*> optexp <*> optfloat
<|> (\a b c -> FL3 a b c) <$>
intPart <*> exponentPart <*> optfloat
<|> (\a b c -> FL4 a b c) <$>
intPart <*> optexp <*> floatSuffix
intPart = digits
fractPart = digits
exponentPart = (++) <$> exponentIndicator <*> signedInteger
signedInteger = (++) <$> option sign "" <*> digits
exponentIndicator = token "e" <|> token "E"
sign = token "+" <|> token "-"
floatSuffix = token "f" <|> token "F"
<|> token "d" <|> token "D"
period = token "."
optexp = option exponentPart ""
optfract = option fractPart ""
optfloat = option floatSuffix ""
4.2 Las de�niciones de tipos de datos y tipos son como antes, s�olo los analizadores
devuelvenn otro resultado (sem�antico).
digit = f <$> satisfy isDigit
where f c = ord c - ord '0'
178 Respuestas a los ejercicios
digits = foldl f 0 <$> many1 digit
where f a b = 10*a + b
floatLiteral = (\a b c d e -> (fromInt a + c) * power d) <$>
intPart <*> period <*> optfract <*> optexp <*> optfloat
<|> (\a b c d -> b * power c) <$>
period <*> fractPart <*> optexp <*> optfloat
<|> (\a b c -> (fromInt a) * power b) <$>
intPart <*> exponentPart <*> optfloat
<|> (\a b c -> (fromInt a) * power b) <$>
intPart <*> optexp <*> floatSuffix
intPart = digits
fractPart = foldr f 0.0 <$> many1 digit
where f a b = (fromInt a + b)/10
exponentPart = (\x y -> y) <$> exponentIndicator <*> signedInteger
signedInteger = (\ x y -> x y) <$> option sign id <*> digits
exponentIndicator = symbol 'e' <|> symbol 'E'
sign = const id <$> symbol '+'
<|> const negate <$> symbol '-'
floatSuffix = symbol 'f' <|> symbol 'F'<|> symbol 'd' <|> symbol 'D'
period = symbol '.'
optexp = option exponentPart 0
optfract = option fractPart 0.0
optfloat = option floatSuffix ' '
power e | e < 0 = 1 / power (-e)
| otherwise = fromInt (10^e)
4.3 El esquema de an�alisis sint�actico para los literales de punto otante de Java es:
digit = f <$> satisfy isDigit
where f c = .....
digits = f <$> many1 digit
where f ds = ..
floatLiteral = f1 <$>
intPart <*> period <*> optfract <*> optexp <*> optfloat
<|> f2 <$>
period <*> fractPart <*> optexp <*> optfloat
<|> f3 <$>
intPart <*> exponentPart <*> optfloat
<|> f4 <$>
intPart <*> optexp <*> floatSuffix
where
f1 a b c d e = .....
f2 a b c d = .....
f3 a b c = .....
f4 a b c = .....
intPart = f <$> digits
where f ds = .....
fractPart = f <$> many1 digit
where f ds = ....
exponentPart = f <$> exponentIndicator <*> signedInteger
Respuestas a los ejercicios 179
where f x y = .....
signedInteger = f <$> option sign ?? <*> digits
where f x y = .....
exponentIndicator = f1 <$> symbol 'e' <|> f2 <$> symbol 'E'
where
f1 c = ....
f2 c = ..
sign = f1 <$> symbol '+' <|> f2 <$> symbol '-'
where
f1 h = .....
f2 h = .....
floatSuffix = f1 <$> symbol 'f'
<|> f2 <$> symbol 'F'
<|> f3 <$> symbol 'd'
<|> f4 <$> symbol 'D'
where
f1 c = .....
f2 c = .....
f3 c = .....
f4 c = .....
period = symbol '.'
optexp = option exponentPart ??
optfract = option fractPart ??
optfloat = option floatSuffix ??
6.1
type LNTreeAlgebra a b x = (a -> x,x -> b -> x -> x)
foldLNTree :: LNTreeAlgebra a b x -> LNTree a b -> x
foldLNTree (leaf,node) = fold where
fold (Leaf a) = leaf a
fold (Node l m r) = node (fold l) m (fold r)
6.2
1. -- height (Leaf x) = 0
-- height (Bin lt rt) = 1 + (height lt) `max` (height rt)
height :: BinTree x -> Int
height = foldBinTree heightAlgebra
heightAlgebra = (\u v -> 1 + u `max` v, const 0)
2. -- flatten (Leaf x) = [x]
-- flatten (Bin lt rt) = (flatten lt) ++ (flatten rt)
flatten :: BinTree x -> [x]
flatten = foldBinTree ((++) ,\x -> [x])
3. -- maxBinTree (Leaf x) = x
-- maxBinTree (Bin lt rt) = (maxBinTree lt) `max` (maxBinTree rt)
maxBinTree :: Ord x => BinTree x -> x
maxBinTree = foldBinTree (max, id)
180 Respuestas a los ejercicios
4. -- sp (Leaf x) = 0
-- sp (Bin lt rt) = 1 + (sp lt) `min` (sp rt)
sp :: BinTree x -> Int
sp = foldBinTree spAlgebra
spAlgebra = (\u v -> 1 + u `min` v, const 0)
5. -- mapBinTree f (Leaf x) = Leaf (f x)
-- mapBinTree f (Bin lt rt) = Bin (mapBinTree f lt) (mapBinTree f rt)
mapBinTree :: (a -> b) -> BinTree a -> BinTree b
mapBinTree f = foldBinTree (Bin, Leaf . f)
6. -- replace (Leaf x) y = Leaf y
-- replace (Bin lt rt) y = Bin (replace lt y) (replace rt y)
replace :: BinTree a -> a -> BinTree a
replace = foldBinTree repAlgebra
repAlgebra = (\f g -> \y -> Bin (f y) (g y), \x y -> Leaf y)
6.3
1. --allPaths (Leaf x) = [[]]
--allPaths (Bin lt rt) = map (Left:) (allPaths lt)
-- ++ map (Right:) (allPaths rt)
allPaths :: BinTree a -> [[Direction]]
allPaths = foldBinTree psAlgebra
psAlgebra :: BinTreeAlgebra a [[Direction]]
psAlgebra = (\u v -> map (Left:) u ++ map (Right:) v, const [[]])
2. --path2Value (Leaf x) = \bs -> if null bs then x else error"no roothpath"
--path2Value (Bin lt rt) =
-- \bs -> case bs of
-- [] -> error "no roothpath"
-- (Left:rs) -> path2Value lt rs
-- (Right:rs) -> path2Value rt rs
--
path2Value :: BinTree a -> [Direction] -> a
path2Value = foldBinTree pvAlgebra
pvAlgebra :: BinTreeAlgebra a ([Direction] -> a)
pvAlgebra = (\fl fr -> \ bs -> case bs of
{[] -> error "no roothpath"
;(Left:rs) -> fl rs
;(Right:rs) -> fr rs
}
, \x -> \ bs -> if null bs
then x
else error "no roothpath")
6.4
1. data Resist = Resist :|: Resist
| Resist :*: Resist
Respuestas a los ejercicios 181
| BasicR Float
deriving Show
type ResistAlgebra a = (a -> a -> a, a -> a -> a, Float -> a)
foldResist :: ResistAlgebra a -> Resist -> a
foldResist (par, seq,basic) = fold where
fold (r1 :|: r2) = par (fold r1) (fold r2)
fold (r1 :*: r2) = seq (fold r1) (fold r2)
fold (BasicR f) = basic f
2. result :: Resist -> Float
result = foldResist resultAlgebra
resultAlgebra :: ResistAlgebra Float
resultAlgebra = (\u v -> (u * v) /(u + v), (+), id)
6.5
1. isSum = foldExpr ((&&)
,\x y -> False
,\x y -> False
,\x y -> False
,const True
,const True
,\x -> (&&)
)
2. vars = foldExpr ((++)
,(++)
,(++)
,(++)
,const []
,\x -> [x]
,\x y z -> x : (y ++ z)
)
6.6
1. der calcula una diferenciaci�on simb�olica de una expresi�on.
2. La funci�on der no es una funci�on composicional sobre Expr porque la parte
derecha de las expresiones `Mul` y `Dvd` no solo usan der e1 dx y der e2
dx, sino tambi�en e1 y e2 entre ellos.
3. data Exp = Exp `Plus` Exp
| Exp `Sub` Exp
| Con Float
| Idf String
deriving Show
type ExpAlgebra a = (a -> a -> a
,a -> a -> a
,Float -> a
,String -> a
182 Respuestas a los ejercicios
)
foldExp :: ExpAlgebra a -> Exp -> a
foldExp (plus, sub, con, idf) = fold where
fold (e1 `Plus` e2) = plus (fold e1) (fold e2)
fold (e1 `Sub` e2) = sub (fold e1) (fold e2)
fold (Con n) = con n
fold (Idf s) = idf s
4. -- der :: Exp -> String -> Exp
-- der (e1 `Plus` e2) dx = (der e1 dx) `Plus` (der e2 dx)
-- der (e1 `Sub` e2) dx = (der e1 dx) `Sub` (der e2 dx)
-- der (Con f) dx = Con 0.0
-- der (Idf s) dx = if s == dx then Con 1.0 else Con 0.0
der = foldExp derAlgebra
derAlgebra :: ExpAlgebra (String -> Exp)
derAlgebra = (\f g -> \s -> f s `Plus` g s
,\f g -> \s -> f s `Sub` g s
,\n -> \s -> Con 0.0
,\s -> \t -> if s == t then Con 1.0 else Con 0.0
)
6.7
1 type PalAlgebra p = (p, p, p, p -> p, p -> p)
2 foldPal :: PalAlgebra p -> Pal -> p
foldPal (pal1,pal2,pal3,pal4,pal5) = fPal where
fPal Pal1 = pal1
fPal Pal2 = pal2
fPal Pal3 = pal3
fPal (Pal4 p) = pal4 (fPal p)
fPal (Pal5 p) = pal5 (fPal p)
3 a2cPal = foldPal (""
,"a"
,"b"
,\p ->"a"++p++"a"
,\p ->"b"++p++"b"
)
aCountPal = foldPal (0,1,0,\p->p+2,\p->p)
4 pfoldPal :: PalAlgebra p -> Parser Char p
pfoldPal (pal1, pal2, pal3, pal4, pal5) = pPal where
pPal = const pal1 <$> epsilon
<|> const pal2 <$> syma
<|> const pal3 <$> symb
<|> (\_ p _ -> pal4 p) <$> syma <*> pPal <*> syma
<|> (\_ p _ -> pal5 p) <$> symb <*> pPal <*> symb
syma = symbol 'a'
symb = symbol 'b'
Respuestas a los ejercicios 183
5 El parser pfoldPal m1 devuelve la representaci�on concreta de un pal��ndrome.
El parser pfoldPal m2 devuelve la cantidad de as que ocurren en un pal��ndrome.
6.8
1 type MirAlgebra m = (m,m->m,m->m)
2 foldMir :: MirAlgebra m -> Mir -> m
foldMir (mir1,mir2,mir3) = fMir where
fMir Mir1 = mir1
fMir (Mir2 m) = mir2 (fMir m)
fMir (Mir3 m) = mir3 (fMir m)
3 a2cMir = foldMir ("",\m->"a"++m++"a",\m->"b"++m++"b")
m2pMir = foldMir (Pal1,Pal4,Pal5)
4 pfoldMir :: MirAlgebra m -> Parser Char m
pfoldMir (mir1, mir2, mir3) = pMir where
pMir = const mir1 <$> epsilon
<|> (\_ m _ -> mir2 m) <$> syma <*> pMir <*> syma
<|> (\_ m _ -> mir3 m) <$> symb <*> pMir <*> symb
syma = symbol 'a'
symb = symbol 'b'
5 El analizador sint�actico pfoldMir m1 devuelve la representaci�on concreta de
un pal��ndrome. El analizador pfoldMir m2 devuelve la representaci�on abs-
tracta de un pal��ndrome.
6.9
1 type EvenAlgebra e o = ((e->e,o->e,e),(o->o,e->o))
2 foldEven :: EvenAlgebra e o -> Even -> e
foldEven ((even1,even2,even3),(odd1,odd2)) = fEven where
fEven (Even1 e) = even1 (fEven e)
fEven (Even2 o) = even2 (fOdd o)
fEven Even3 = even3
fOdd (Odd1 o) = odd1 (fOdd o)
fOdd (Odd2 e) = odd2 (fEven e)
3 a2cEven = foldEven ((\x->x++"0",\x->x++"1","")
,(\x->x++"0",\x->x++"1")
)
valEven = foldEven ((\x->2*x,\x->2*x+1,0)
,(\x->2*x,\x->2*x+1)
)
6.10
1 type BitListAlgebra l r b = (b->r->l,(r,b->r->r),(b,b))
2 foldBitList :: BitListAlgebra l r b -> BitList -> l
foldBitList (list1,(rest1,rest2),(bit1,bit2)) = fBitList
184 Respuestas a los ejercicios
where
fBitList (List1 b r) = list1 (fBit b) (fRest r)
fRest Rest1 = rest1
fRest (Rest2 b r) = rest2 (fBit b) (fRest r)
fBit Bit1 = bit1
fBit Bit2 = bit2
3 a2cBitList = foldBitList ((++)
,("",\b r -> ","++b++r)
,("0","1")
)
4 pfoldBitList :: BitListAlgebra l r b -> Parser Char l
pfoldBitList (list1, (rest1,rest2), (bit1,bit2)) = pBitList
where
pBitList = list1 <$> pBit <*> pRest
pBit = const bit1 <$> symbol '0'
<|> const bit2 <$> symbol '1'
pRest = (\_ b r -> rest2 b r) <$> symbol ','
<*> pBit
<*> pRest
<|> const rest1 <$> epsilon
6.11
1. data Block = B1 Stat Rest
data Rest = R1 Stat Rest | Nix
data Stat = S1 Decl | S2 Use | S3 Nest
data Decl = Dx | Dy
data Use = UX | UY
data Nest = N1 Block
La representaci�on abstracta de x;(y;Y);X es
B1 (S1 Dx) (R1 stat2 rest2) where
stat2 = S3 (N1 block)
block = B1 (S1 Dy) (R1 (S2 UY) Nix)
rest2 = R1 (S2 UX) Nix
2. type BlockAlgebra b r s d u n =
(s -> r -> b
,(s -> r -> r, r)
,(d -> s, u -> s, n -> s)
,(d,d)
,(u,u)
,b -> n
)
3. foldBlock :: BlockAlgebra b r s d u n -> Block -> b
foldBlock (b1, (r1, nix), (s1, s2, s3), (dx, dy), (ux, uy), n1) =
Respuestas a los ejercicios 185
foldB
where
foldB (B1 stat rest) = b1 (foldS stat) (foldR rest)
foldR (R1 stat rest) = r1 (foldS stat) (foldR rest)
foldR Nix = nix
foldS (S1 decl) = s1 (foldD decl)
foldS (S2 use) = s2 (foldU use)
foldS (S3 nest) = s3 (foldN nest)
foldD Dx = dx
foldD Dy = dy
foldU UX = ux
foldU UY = uy
foldN (N1 block) = n1 (foldB block)
4. a2cBlock :: Block -> String
a2cBlock = foldBlock a2cBlockAlgebra
a2cBlockAlgebra ::
BlockAlgebra String String String String String String
a2cBlockAlgebra =
(b1, (r1, nix), (s1, s2, s3), (dx, dy), (ux, uy), n1)
where b1 u v = u ++ v
r1 u v = ";" ++ u ++ v
nix = ""
s1 u = u
s2 u = u
s3 u = u
dx = "x"
dy = "y"
ux = "X"
uy = "Y"
n1 u = "(" ++ u ++ ")"
8.1 El tipo TreeAlgebra y la funci�on foldTree se de�nen en el problema rep-min.
1. height = foldTree heightAlgebra
heightAlgebra = (const 0, \l r -> (l `max` r) + 1)
--frontAtLevel :: Tree -> Int -> [Int]
--frontAtLevel (Leaf i) h = if h = 0 then [i] else []
--frontAtLevel (Bin l r) h =
-- if h > 0 then frontAtLevel l (h-1) ++ frontAtLevel r (h-1)
-- else []
frontAtLevel = foldTree frontAtLevelAlgebra
frontAtLevelAlgebra =
( \i h -> if h = 0 then [i] else []
, \f g h -> if h > 0 then f (h-1) ++ g (h-1) else []
)
2. (a) Soluci�on directa: imposible dar una soluci�on como la del 10.
heightAlgebra = (const 0, \l r -> (l `max` r) + 1)
front t = foldTree frontAtLevelAlgebra t h
186 Respuestas a los ejercicios
where h = foldTree heightAlgebra t
(b) Lambda lifting
front' t = foldTree
frontAtLevelAlgebra
t
(foldTree heightAlgebra t)
(c) El producto cartesiano de los c�alculos
htupfr :: TreeAlgebra (Int, Int -> [Int])
htupfr = (heightAlgebra `tuple` frontAtLevelAlgebra)
= ( \i -> ( 0
, \h -> if h == 0 then [i] else []
)
, \(lh,f) (rh,g) -> ( (lh `max` rh) + 1
, \h -> if h > 0
then f (h-1) ++ g (h-1)
else []
)
)
front'' t = fr h
where (h, fr) = foldTree htupfr t
(d) Fusi�on de los productos cartesianos
Es �util notar que la fusi�on del �algebra se construye tal que
foldTree mergedAlgebra t i = (height t, frontAtLevel t i)
Por consiguiente, las de�niciones que corresponden a la cuarta soluci�on
son
mergedAlgebra :: TreeAlgebra (Int -> (Int, [Int]))
mergedAlgebra =
(\i -> \h -> ( 0
, if h == 0 then [i] else []
)
, \lfun rfun -> \h -> let (lh, xs) = lfun (h-1)
(rh, ys) = rfun (h-1)
in
( (lh `max` rh) + 1
, if h > 0 then xs ++ ys else []
)
)
front''' t = fr
where (h, fr) = foldTree mergedAlgebra t h
8.2 El problema frente alto es el problema de encontrar la primera lista no-vac��a
de nodos que est�an en el nivel m�as bajo. Las soluciones son similares al problema
frente profundo.
1. --lowest :: Tree -> Int
--lowest (Leaf i) = 0
Respuestas a los ejercicios 187
--lowest (Bin l r) = ((lowest l) `min` (lowest r)) + 1
lowest = foldTree lowAlgebra
lowAlgebra = (const 0, \ l r -> (l `min` r) + 1)
2. (a) Soluci�on directa: imposible dar una soluci�on como la del 10.
lfront t = foldTree frontAtLevelAlgebra t l
where l = foldTree lowAlgebra t
(b) Lambda lifting
lfront' t = foldTree
frontAtLevelAlgebra
t
(foldTree lowAlgebra t)
(c) El producto cartesiano de los c�alculos
ltupfr :: Tree_Alg (Int, Int -> [Int])
ltupfr =
( \i -> ( 0
, (\l -> if l == 0 then [i] else [])
)
, \ (lh,f) (rh,g) -> ( (lh `min` rh) + 1
, \l -> if l > 0
then f (l-1) ++ g (l-1)
else []
)
)
lfront'' t = fr l
where (l, fr) = foldTree ltupfr t
(d) Fusi�on de los productos cartesianos
Es �util notar que la fusi�on del �algebra se construye tal que
foldTree mergedAlgebra t i = (lowest t, frontAtLevel t i)
Por consiguiente, las soluciones que corresponden a la cuarta soluci�on son
lmergedAlgebra :: Tree_Alg (Int -> (Int, [Int]))
lmergedAlgebra =
( \i -> \l -> ( 0
, if l == 0 then [i] else []
)
, \lfun rfun -> \l ->
let (ll, lres) = lfun (l-1)
(rl, rres) = rfun (l-1)
in
( (ll `min` rl) + 1
, if l > 0 then lres ++ rres else []
)
)
lfront''' t = fr
where (l, fr) = foldTree lmergedAlgebra t l
188 Respuestas a los ejercicios
9.1 Seguimos el patr�on de demostraciones para lenguajes no regulares dado en el
texto.
Sea n 2 IIN.
Tomamos s = an2 con x = �, y = a
n, y z = an2�n.
Sean u; v; w tales que y = uvw con v 6= �, es decir, u = ap, v = a
q y w = ar con
p+ q + r = n y q > 0.
Tomando i = 2, entonces
xuv2wz 62 L
( defn. x; u; v; w; z, c�alculo
ap+2q+r
an2�n 62 L
( p+ q + r = n y q > 0
n2 + q no es un cuadrado
(
n2 < n2 + q < (n + 1)2
(
q < 2n+ 1
9.2 Usando el patr�on de demostraci�on.
Sea n 2 IIN.
Tomamos s = anbn+1 con x = an, y = b
n, y z = b.
Sean u; v; w tales que y = uvw con v 6= �, es decir, u = bp, v = b
q y w = br con
p+ q + r = n y q > 0.
Tomando i = 0, entonces
xuwz 62 L
( defn. x; u; v; w; z, c�alculo
anbp+r+1 62 L
(
p+ q + r � p+ r + 1
(
q > 0
9.3 Usando el patr�on de demostraci�on.
Sea n 2 IIN.
Tomamos s = anb2n con x = an, y = b
n, y z = bn.
Sean u; v; w tales que y = uvw con v 6= �, es decir, u = bp, v = b
q y w = br con
p+ q + r = n y q > 0.
Tomando i = 2, tenemos
xuv2wz 62 L
( defn. x; u; v; w; z, c�alculo
anb2n+q 62 L
(
2n+ q > 2n
(
q > 0
9.4 Usando el patr�on de demostraci�on.
Respuestas a los ejercicios 189
Sea n 2 IIN.
Tomamos s = a6bn+4
an con x = a
6bn+4, y = a
n, y z = �.
Sean u; v; w tales que y = uvw con v 6= �, es decir, u = ap, v = a
q y w = ar con
p+ q + r = n y q > 0.
Tomando i = 6, entonces
xuv6wz 62 L
( defn. x; u; v; w; z, c�alculo
a6bn+4
an+5q 62 L
(
n+ 5q > n+ 4
(
q > 0
9.5 Seguimos el patr�on de demostraci�on dado en el texto para lenguajes que no son
libres de contexto.
Sean c; d 2 IIN.
Tomamos z = ak2 con k = max(c; d).
Sean u; v; w; x; y tales que z = uvwxy, jvxj > 0 y jvwx j � d
Es decir, u = ap, v = a
q, w = ar, x = a
s y y = at con p + q + r + s + t = k2,
q + r + s � d y q + s > 0.
Tomamos i = 2, entonces
uv2wx2y 62 L
( defn. u; v; w; x; y, c�alculo
ak2+q+s 62 L
( q + s > 0
k2 + q + s no es un cuadrado
(
k2 < k2 + q + s < (k + 1)2
(
q + s < 2k + 1
( defn. k
q + s � d
9.6 Usando el patr�on de demostraci�on.
Sean c; d 2 IIN.
Tomamos z = ak con k es primo y k > max(c; d).
Sean u; v; w; x; y tales que z = uvwxy, jvxj > 0 y jvwx j � d
Es decir, u = ap, v = a
q, w = ar, x = a
s y y = at con p + q + r + s + t = k,
q + r + s � d y q + s > 0.
Tomando i = k + 1, entonces
uvk+1wx k+1y 62 L
( defn. u; v; w; x; y, c�alculo
ak+kq+ks 62 L
(
190 Respuestas a los ejercicios
k(1 + q + s) no es primo
(
q + s > 0
9.7 Usamos el patr�on de demostraci�on.
Sea c; d 2 IIN.
Tomamos z = akbkakbk con k = max(c; d).
Sean u; v; w; x; y tales que z = uvwxy, jvxj > 0 y jvwx j � d
Observe que la elecci�on para k garantiza que la subcadena vwx es de una de las
siguientes formas:
� vwx consiste de solamente as, o solamente bs.
� vwx contiene tanto as como bs.
Tomamos i = 0, entonces
� Si vwx consiste de solamente as, o solamente de bs, entonces es imposible
escribir la cadena uwy como ww para alguna w, ya que solamente la cantidad
de terminales de un tipo se decrementa.
� Si vwx contiene tanto as como bs, est�a en alg�un lugar del borde entre as y bs,
o en el borde entre bs y as. Entonces la cadena uwy puede escribirse como
uwy = asbtapbq
para alg�un s, t, p, q, respectivamente. Por lo menos uno de entre s ,t, p and q
es menor que k, mientras dos de ellos son iguales a k. Nuevamente esta frase
no es un elemento del lenguaje.
�Indice de Materias
�arbol de an�alisis, 22
ambigua, 23
aut�omata de estado �nito no determi-
n��stico, 80
BNF, 16
completa, 14
consistente, 14
derivaci�on, 18
expresi�on regular, 94
gram�atica, 15
identi�cador, 95
inductivamente, 13
interpretaci�on estructural, 23
Ldfa lenguaje de un aut�omata de es-
tado �nito determin��stico, 80
lenguaje de un aut�omata de estado �-
nito determin��stico, 80
lenguaje de un aut�omata de estado �-
nito no determin��stico, 82
lenguaje de una expresi�on regular, 94
Lre, 94
mathematical induction, 14
nfa, 82
no ambigua, 23
Pumping Lemma para lenguajes libres
de contexto, 149
Pumping Lemma para lenguajes regu-
lares, 147
regla de producci�on, 15
s��mbolo, 95
s��mbolo inicial, 15
s��mbolo no terminal, 15
191