manual cup2

21
1 FIUSAC Organización de lenguajes y compiladores 1 Seccion A Ing. Mario Bautista Aux. Wener Aldana Manual de CUP2 Jose Javier Cardona Polanco 200910523

Upload: jojagt

Post on 01-Dec-2015

151 views

Category:

Documents


0 download

DESCRIPTION

Manual breve sobre CUP2

TRANSCRIPT

Page 1: Manual CUP2

1

FIUSAC

Organización de lenguajes y compiladores 1

Seccion A

Ing. Mario Bautista

Aux. Wener Aldana

Manual de CUP2

Jose Javier Cardona Polanco

200910523

Page 2: Manual CUP2

2

Manual

Introductorio

de CUP2

Por Javier Cardona

Page 3: Manual CUP2

3

Índice

Objetivos……………………………………………………………………………………………… 4

Introducción…………………………………………………………………………………………..5

Un Compilador……………………………………………………………………………………….6

Acerca de CUP2…………………………………………………………………………………….9

Acciones semánticas………………………………………………………………………………10

Creando y utilizando un parser…………………………………………………………………12

Declaraciones de símbolos y definiendo reglas gramaticales………………………. 12

Asignación de acciones semánticas a las reglas gramaticales………………………14

Eliminación de ambigüedad……………………………………………………………………..14

Conexión con JFlex………………………………………………………………………………..16

Manejo y recuperación de Errores……………………………………………………………16

Ejemplo……………………………………………………………………...……….………...........17

Page 4: Manual CUP2

4

Objetivos

Introducir los aspectos generales de CUP2 y orientar la “instalación”.

Facilitar el uso de CUP2.

Ejemplificar el uso de las reglas gramaticales que maneja CUP2.

Conectar con JFlex para poder estudiar los aspectos básicos de un

compilador.

Page 5: Manual CUP2

5

Introducción

El presente manual podrán encontrar una pequeña introducción a los

compiladores y a su vez a los pasos que toma el compilador para analizar un

archivo fuente. Si bien el objetivo de este manual no es profundizar en este

tema se consideró necesario el explorarlo para aquel lector que no tuviera los

conocimientos básicos.

Se tomó en cuenta el hecho de que es de mucho mas ayuda los ejemplos que

la simple explicación en texto plano, por lo que este manual contiene las

explicaciones graficas que se consideraron necesarias.

Al final de este manual podrán encontrar un ejemplo sencillo de una calculadora

sencilla. La estructura final de un archivo CUP2 es muy parecido a CUP (versión

1) por lo que si ustedes tienen conocimientos previos podrán entender este

ejemplo sin mucho análisis.

Si el lector tiene conocimientos previos sobre CUP, es bueno aclarar ahora que

si bien el nombre CUP2 indica que esta es la versión 2 del mismo, esto no es así

ya que cuando se creo CUP2 este fue reinventado completamente.

Sinceramente espero que sea de su ayuda, disfruten el manual.

Page 6: Manual CUP2

6

Un compilador

A grandes rasgos, un compilador es un programa que lee un programa escrito

en un lenguaje (lenguaje fuente) y que lo traduce a un programa equivalente en

otro lenguaje (lenguaje objeto), este lenguaje final por lo general es lenguaje de

máquina. Como parte importante de éste proceso de “traducción”, el

compilador informa a su usuario de la presencia de errores en el programa

fuente.

Si nos ponemos a pensar, estamos diciendo que hay una enorme cantidad de

compiladores hoy día. Hay miles de lenguajes fuente y también hay muchos

lenguajes objeto. Esto nos deja con una cantidad de compiladores

increíblemente grande y nos da la idea de la necesidad de nuevos

compiladores. Es por esto que la habilidad para realizar compiladores hoy día

sigue siendo necesaria.

A continuación mostramos un diagrama bastante sencillo de lo que un

compilador regular puede hacer.

Un compilador realiza esta labor de traducción analizando el programa fuente

en diferentes pasos. Para ello realiza este análisis en dos etapas principales. La

primera etapa analiza el programa fuente con un analizador lineal o léxico, un

analizador jerárquico o sintáctico y un analizador semántico.

Page 7: Manual CUP2

7

Podemos visualizar de manera mas amplia y especifica los componentes de un

compilador al ver el siguiente esquema que contiene todas las fases de un

compilador.

Estas fases y analizadores se pueden realizar “a mano” programándolos con

técnicas ya establecidas y orientadas a nuestro lenguaje fuente. Pero también

exisen generadores de analizadores léxicos y sintácticos. Uno de los

generadores léxicos mas utilizados, al menos en la USAC, es JFlex que toma las

especificaciones escritas en lenguaje de JFlex y las transforma y las devuelve

como una clase Java que contiene nuestro analizador léxico.

Page 8: Manual CUP2

8

Es mas aconsejable utilizar estas herramientas ya que ahorran mucho tiempo y

el manejo de errores puede llegar a ser mucho mas sencillo que si lo

programáramos nosotros mismos.

Un generador de analizadores sintácticos es CUP2, que toma reglas

gramaticales de un lenguaje fuente y las transforma mediante métodos como el

LR(0), LALR(1) y LR(1) para dar como resultado dos clases Java que manejan

nuestras gramáticas según nosotros las hayamos declarado.

Representación de un buen compilador.

Page 9: Manual CUP2

9

Acerca de CUP2

Los desarrolladores de CUP2 considera que CUP2 es un sistema para la

generación de analizadores de especificaciones concisas. CUP2 está

completamente escrito en Java y combina las ideas algorítmicas de su

predecesor CUP con las características de Java modernas.

Mientras CUP soporta LALR (1) solamente, CUP2 incluye LR(0), LR (1) y LALR (1)

de la caja y permite fáciles extensiones para otras clases de gramática.

A diferencia de otros generadores de analizadores sintácticos tradicionales

(incluyendo también CUP) CUP2 no crea código fuente parser basado en un

lenguaje de definición de la gramática independiente, sino que utiliza la API de

Java, las llamadas se combina con las estructuras de datos para representar la

información relacionada con la gramática.

Esto significa que no hay un lenguaje especial para definir gramáticas y

acciones semánticas, pero todo se hace en Java puro. Aún más, CUP2 se

exime de generar un analizador como un archivo de código fuente, sino más

bien produce una corriente de código Java serializado, que luego puede ser

cargado por las clases de tiempo de ejecución CUP2.

Definiendo Gramáticas

Si tienes alguna relación con la versión anterior de CUP (si cursaste el curso de

compi1 en la USAC) es probable que te parezca conocida la forma de declarar

las gramáticas en CUP2 ya que utiliza recursos de java como los enums.

Además también utiliza, en mi opinión la mejor manera de declarar gramáticas

tipo 2, que es el BNF (Backus-Naur Form). BNF es un metalenguaje usado para

expresar gramáticas libres de contexto: es decir, una manera formal de

describir lenguajes formales.

Page 10: Manual CUP2

10

Como pequeña introducción definiremos algunas reglas de esta notación.

Regla gramatical Representación en BNF No terminal <no_terminal > Terminal (se escribe en negrita)

try

Produce / Símbolo de producción

::=

Simbolo OR (<opcion1> | <opcion2> | <opcion3> )

Producción completa <try>:: try <cuerpo_try> catch <cuerpo_catch>

Rango de el abecedario de minúsculas.

<rango_de_letras_minusculas> ::= [a-z]

Rango del abecedario en mayúsculas y minúsculas.

<rango_de_letras_minusculas> ::= [a-zA-Z]

A continuación les presentamos un pequeño ejemplo de cómo definir un

identificador típico, que tiene como regla iniciar con una letra y puede seguir de

muchos números, letras y guiones bajos.

< 𝑙𝑒𝑡𝑟𝑎 >∷= [𝒂 − 𝒛𝑨 − 𝒛]

< 𝑑𝑖𝑔𝑖𝑡𝑜 >∷= [𝟎 − 𝟗]

< 𝑖𝑑𝑒𝑛𝑡𝑖𝑓𝑖𝑐𝑎𝑑𝑜𝑟 >∷=< 𝑙𝑒𝑡𝑟𝑎 > (< 𝑙𝑒𝑡𝑟𝑎 > | < 𝑑𝑖𝑔𝑖𝑡𝑜 > _ ∗

Para una mejor y más extensa explicación buscar en el libro del dragón y en

Wikipedia.

Acciones semánticas.

Para realizar las acciones que tu gramática tiene como objetivo realizar

utilizamos estas acciones semánticas. Que constan en acciones que devuelven

valores en reducciones de la gramática que van produciendo los resultados

para generar una acción final con los valores de nuestro archivo de entrada.

Seguiremos utilizando los ejemplos del Manual de CUP2.

Page 11: Manual CUP2

11

Para cada No Terminal tenemos que crear una clase que extienda de la clase

SymbolValue<T>. Para el ejemplo que venimos trabajando declararíamos las

clases de la siguiente manera:

Una vez hecho esto podemos utilizar el metodo prod que habiamos

mencionado antes con un argumento mas y este es el de la acción que

queremos que realice. La estructura general del método quedaría de la

siguiente manera:

Page 12: Manual CUP2

12

Creando y Utilizando un parser.

Primero tenemos que crear la tabla de análisis sintáctico y luego somos

capaces de inicializar el analizador con él. Esto se muestra en el siguiente

listado.

Una vez creada nuestra LRParsingTable llamada “tablichi” a ésta le mandamos

un scanner que a su vez le mandara los tokens a la tabla para que sean

analizados y ejecutadas sus acciones, como lo indicábamos en la sección

anterior.

Declaraciones de simbolos y definiendo reglas gramaticales

Como mencionábamos antes CUP2 utiliza enums para alojar a los terminales y a

los no terminales de una gramática. En el manual oficial de CUP2 podemos

encontrar un clásico ejemplo que vemos desde que empezamos compiladores

con el libro del dragón.

< 𝑒 >∷= ( < 𝑒 > + < 𝑡 > ) | < 𝑡 >

< 𝑡 >∷= < 𝑡 > + < 𝑓 > | < 𝑓 >

< 𝑓 >∷= ( < 𝒆 > ) | 𝒏𝒖𝒎𝒆𝒓𝒐

Page 13: Manual CUP2

13

El equivalente de estas producciones para CUP2 seria el siguiente:

Para que esta producción tenga sentido para CUP2 tenemos que declara los

terminales y no terminales al igual que lo hacíamos en anteriormente en CUP

(versión 1).

Declaración de no terminales:

Declaración de terminales:

Page 14: Manual CUP2

14

Asignación de acciones semánticas a las reglas gramaticales.

Con el fin de calcular cualquier valor o construir cualquier representación de la

entrada del analizador, necesitamos equipar el analizador con acciones

semánticas. Las acciones semánticas en CUP2 son los métodos de Java que

contienen código que se ejecuta cada vez que el parser decide reducir los

símbolos hasta ahora reconocidos de acuerdo con una de sus reglas.

Como habíamos definido antes las acciones semánticas son las que se van a

realizar cono objetivo final por nuestra gramática.

Las acciones se verían de la siguiente manera en un producción cualquiera.

Eliminación de ambigüedad

Si bien CUP2 realiza varias tareas para eliminar la ambigüedad el mismo esto no

quiere decir que no pueda llevar a errores esta ambiguierdad.

Una gramatica tipo 2 es ambigua si existe una cadena que tiene más de una

derivación por la izquierda o más de una derivación por la derecha o si tiene dos

o más árboles de derivación. En caso de que toda cadena tenga un único árbol

de derivación, la gramática no es ambigua.

Page 15: Manual CUP2

15

Anteriormente utilizábamos uno de los ejemplos clásicos de el libro del dragon,

que es del de la precedencia de multiplicación sobre la suma.

Ahora utilizaremos otro ejemplo clásico para ejemplificar la ambigüedad de una

gramática al representar gráficamente los arboles permitidos por esta misma

gramática.

< 𝐸 >∷= < 𝐸 > + < 𝐸 >

< 𝐸 >∷= < 𝐸 > ∗ < 𝐸 >

< 𝐸 >∷= 𝑥

< 𝐸 >∷= 𝑦

Tenemos que la anterior gramática nos puede producir estos dos árboles que

como vemos son diferentes. El problema con que sean diferentes es que las

acciones semánticas que mencionábamos antes serian afectadas porque las

reducciones se realizarían sin estandarización.

Para eliminar esta ambigüedad no existe un solo método que lo solucione todo,

lamentablemente. Así que en muchos casos recae en nuestra habilidad como

desarrolladores de compiladores y en nuestra experiencia desarrollando

gramáticas.

Una de las soluciones que, por la experiencia, pueden funcionar es el agregar

producciones y no terminales a nuestra gramática para que sepa por que

camino irse. Ya que el problema principal es que el compilador no va a saber

que producción tomar si es que viene una <E>. Podría tomar cualquiera de las

dos primeras producciones. Podriamos replantear la gramatica de la siguiente

manera.

< 𝐸 >∷ = < 𝐸2 > < 𝐸3 > | 𝑒

< 𝐸2 >∷= 𝑥 | 𝑦

< 𝐸3 >∷= + −) < 𝐸 >

Page 16: Manual CUP2

16

Conexión con JFlex

Para conectar con CUP2 con JFlex es muy sencillo. Si ya tienes experiencia con

CUP (versión 1) conectándolo con JFlex se te hara muy fácil ya que es igual con

pequeñas diferencias. Para más información por favor consulta la

documentación de CUP2 y de JFlex.

A continuación una ejemplificación de cómo se vería un .jflex

Manejo de y recuperación de Errores.

Como mencionábamos en el inicio de este manual, el manejo y recuperación de

errores es una de las funciones principales del compilador, ya que esta indicaría

al usuario final en donde están los errores que su programa fuente tiene

indicándole los caracteres de error o el orden esperado de tokens. Para esto se

necesita la posición de los tokens o caracteres que provocan el error dentro del

archivo fuente.

Page 17: Manual CUP2

17

Aspecto importante de la construcción de analizadores con CUP2 es el apoyo a

la recuperación de errores sintácticos. En estos casos un analizador preparada

simplemente se detiene con una excepción. Sería muy útil si el analizador

intenta continuar con el análisis en un punto, en el que está seguro de lo que se

entiende o incluso trata de dar consejos, cómo corregir la entrada.

El método mas común de recuperación de errores y el que analizaremos en

esta ocasión es el famoso modo pánico. Que lo que hace es que una vez

encuentra un error en la gramática empieza a ignorar todos los tokes que le

sigan al error hasta que encuentre un token centinela y eso le indique que la

instrucción a terminado y por tanto puede volver a empezar a analizar.

Un ejemplo de la aplicación de este metodoo es el siguiente (tomado del

manual de CUP2).

Page 18: Manual CUP2

18

A continuación presento la compilación de los ejemplos que he presentado. De

los dos archivos JFlex y CUP2.

Page 19: Manual CUP2

19

A esto se le tiene que sumar una clase principal para correrlo y la clase de

analizador léxico que le envie los tokens. Ya que el principal objetivo de este

manual no es dar ejemplos sino una introducción y guiar al usuario para iniciarse

en CUP2, te puedo recomendar que visites la pagina:

https://www2.in.tum.de/repos/cup2/trunk/

Aquí podrás encontrar mucha mas documentación y ejemplos. Espero que

realmente este manual pueda ser de ayuda y que les haya gustado.

Page 20: Manual CUP2

20

Conclusiones

CUP2 trata de una mejor manera el análisis sintáctico y de una manera

mas directa con el lenguaje Java, utilizando directamente las estructuras

para realizar las listas de los terminales y no terminales lo que te da un

acceso mas directo a la construcción de tu parser.

El uso de este tipo de herramientas es de gran ayuda tanto para el

usuario que se dedica a construir compiladores como para nosotros los

estudiantes que pretendemos entender las funciones de un compilador ya

que se puede manipular de una manera muy cercana los componentes

del compilador como lo es el parser.

El manejo de errores es una de las herramientas mas importantes de un

compilador y sin duda de mucha ayuda para el usuario final. Con CUP2 es

bastante sencillo y poderosa la implementación de esta herramienta.

Page 21: Manual CUP2

21

Bibliografía

Technische Universitat Munchen. CUP2 User Manual [en línea]. Alemania.

[Consulta: 20 de mayo de 2013]. Disponible en:

http://www2.in.tum.de/~petter/cup2/

Aho, Alfred V.; Ravi Sethi, Jeffrey D. Ullman (2008). «Introducción a la

Compilación». Compiladores: Principios, técnicas y prácticas. México: Addison

Wesley.

Ruben Aldo, Kristan. Ambigüedad en una Gramatica [en línea]. Universal.

[Consulta: 19 de mayo de 2013]. Disponible en:

http://teodelacomp.blogspot.com/2011/03/37-eliminacion-de-la-

ambiguedad.html

Archivo Wiki. Compilador [en línea]. Universal. [Consulta: 18 de mayo de 2013].

Disponible en: http://en.wikipedia.org/wiki/Compiler