Gram - Carreras de Informática y Sistemas --- UMSS · Indice General 1 Ob jetiv os 7 1.1 Historia...

193

Transcript of Gram - Carreras de Informática y Sistemas --- UMSS · Indice General 1 Ob jetiv os 7 1.1 Historia...

Gram�aticas y An�alisis Sint�actico

Johan Jeuring

Doaitse Swierstra

Johan Jeuring

Dept. of Computer Science

Utrecht University

[email protected]

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

4 �INDICE GENERAL

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.

/

68 Combinadores para el an�alisis sint�actico

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. /

78 Gram�aticas y dise~no de analizadores sint�acticos

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

/

106 Lenguajes regulares

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).

/

132 Composicionalidad

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. /

148 Gram�aticas de atributos

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

160 BIBLIOGRAF�IA

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