Trabajo Práctico Teorı́a de Lenguajes Segundo cuatrimestre de 2010 1 Introducción El objetivo de este trabajo práctico es desarrollar un compositor de fórmulas matemáticas. El mismo tomará como entrada la descripción de una fórmula en una versión muy simplificada del lenguaje utilizado por LATEX y producirá como salida un archivo PostScript. Para simplificar el trabajo utilizaremos una tipografı́a monoespaciada. Esto permitirá resolver el problema sin necesidad de contar con información sobre la métrica de los distintos caracteres de la tipografı́a. 2 Lenguaje de entrada La siguiente gramática ambigua describe la sintaxis que deben seguir las expresiones que acepte el programa: E → | | | | | | | | EE E^E E E E^E E E E^E E/E (E) {E} l (concatenación) (superı́ndice) (subı́ndice) (super y subı́ndice) (sub y superı́ndice) (división) (encerrar entre paréntesis) (agrupar) (cualquier caracter salvo ^, , /, {, }, ( y )) Para eliminar la ambigüedad en la interpretación, se debe tener en cuenta que la división es la de menor precedencia, seguida de la concatenación. Ambas son asociativas a izquierda. El superı́ndice y subı́ndice son no asociativos (no es lı́cito escribir a^b^c ni a b c) y tienen más precedencia que la concatenación. Por ejemplo, la cadena (A^BC^D/E^F_G+H)-I es equivalente a {({{A^B}{C^D}}/{{{E^F_G}+}H})-}I, y el resultado se podrı́a ver ası́: ABCD -I EFG+H ( ) 1 3 Descripción de la salida PostScript es un lenguaje de programación orientado a la descripción de páginas inventado por Adobe Systems. Una de sus caracterı́sticas distintivas es que prácticamente todas las operaciones se realizan sobre una pila. Los operandos deben ser apilados para operar con ellos, por lo tanto aparecen en el código antes que el operador, como en la notación postfija. El manual de referencia del lenguaje PostScript se puede obtener en http://www.adobe.com/devnet/postscript/index.html. Existen distintas convenciones para especificar tamaños de letra. Una de las más usadas es la de indicar el interlineado para el cual está diseñada la tipografı́a. Cuando se habla de una tipografı́a de 10 puntos usando esta convención, se quiere decir la tipografı́a está diseñada para que la distancia vertical entre las lı́neas de base de los caracteres sea de 10 puntos cuando se usa interlineado normal. Un punto es 1/72 de pulgada. En PostScript, las tipografı́as están diseñadas para que las lı́neas de base de los caracteres estén separadas verticalmente por una unidad. En la tipografı́a monoespaciada Courier, la separación horizontal entre cada caracter es de 0.6 unidades. Durante la ejecución de un programa en PostScript se mantienen como parte del estado gráfico el punto actual y la tipografı́a actual. El operador show dibuja la cadena de caracteres que esté en el tope de la pila usando la tipografı́a actual y tomando el punto actual como inicio de la lı́nea de base para el texto. Por ejemplo, los comandos: /Courier findfont setfont 1 5 moveto (The quick brown fox) show 1 4 moveto (jumps over the lazy dog) show producen el siguiente resultado: The quick brown fox jumps over the lazy dog al que le agregamos una grilla y algunas anotaciones para que se vea cuáles son las dimensiones y coordenadas resultantes: { 0.6 The quick brown fox{ jumps over the lazy dog 1.0 (1,5) (1,4) 2 Cuadro 1: Ejemplo de salida para la cadena de entrada (A^BC^D/E^F G+H)-I %!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 55 33 0 14 translate 12 12 scale .03 setlinewidth /Courier findfont setfont gsave 0.69 0.47 moveto 1.00 1.00 scale (A) show grestore gsave 1.29 0.92 moveto 0.70 0.70 scale (B) show grestore gsave 1.71 0.47 moveto 1.00 1.00 scale (C) show grestore gsave 2.31 0.92 moveto 0.70 0.70 scale (D) show grestore gsave 0.60 -0.68 moveto 1.00 1.00 scale (E) show grestore gsave 1.20 -0.93 moveto 0.70 0.70 scale (G) show grestore gsave 1.20 -0.23 moveto 0.70 0.70 scale (F) show grestore gsave 1.62 -0.68 moveto 1.00 1.00 scale (+) show grestore gsave 2.22 -0.68 moveto 1.00 1.00 scale (H) show grestore gsave 0.60 0.28 moveto 2.22 0.00 rlineto stroke grestore gsave 0.00 -0.36875 moveto 1.00 2.475 scale (\() show grestore gsave 2.82 -0.36875 moveto 1.00 2.475 scale (\)) show grestore gsave 3.42 0.00 moveto 1.00 1.00 scale (-) show grestore gsave 4.02 0.00 moveto 1.00 1.00 scale (I) show grestore showpage Al comienzo de la ejecución de un programa PostScript, una unidad mide 1 punto. El operador scale permite cambiar arbitrariamente el tamaño de una unidad. Esto permitirá seleccionar el tamaño al que se quiere que aparezcan los distintos caracteres. Este operador tiene efecto global: afecta a todos los comandos que se ejecuten después de él. Para acotar su alcance se pueden usar los operadores gsave y grestore, que guardan y recuperan el estado gráfico de una pila dedicada. Para el super y subı́ndice se suele reducir el tamaño de letra a un 70 % del original y desplazar la lı́nea de base hacia arriba o hacia abajo respectivamente. Para las divisiones, la lı́nea de división se suele alinear con la lı́nea horizontal de los signos más y menos (aproximadamente 0.28 unidades sobre la lı́nea de base). El numerador y el divisor se ubican centrados horizontalmente con respecto a la lı́nea de división. En el cuadro 1 se ve una posible salida para la cadena de entrada (A^BC^D/E^F_G+H)-I, cuya imagen mostramos en la sección anterior. 3 4 Modo de uso El programa debe poder ser invocado por lı́nea de comandos, recibiendo como parámetro la especificación de la fórmula y escribiendo el resultado en su salida standard o en un archivo. En caso de que lo recibido como parámetro no sea una fórmula válida se lo debe informar en la salida de error standard. 5 Implementación Hay dos grupos de herramientas que se pueden usar para generar los analizadores léxico y sintáctico, y agregar las acciones requeridas para generar el documento de salida: • Uno utiliza expresiones regulares y autómatas finitos para el análisis lexicográfico, y la técnica LALR para el análisis sintáctico. Ejemplos de esto son lex y yacc, que generan código C o C++, o JLex y CUP, que generan código Java. flex y bison son implementaciones libres y gratuitas de lex y yacc. Son parte de todas las distribuciones Linux. Para Windows pueden ser instalados como parte de Cygwin (http://www.cygwin.com/), o de DJGPP (http://www.delorie.com/djgpp/). También se pueden conseguir en forma independiente en http://www.gnu.org/. JLex y CUP se pueden conseguir en http://www.jflex.de/ y http://www2.cs.tum.edu/projects/cup/ • El otro grupo utiliza la técnica ELL(k) tanto para el análisis léxico como para el sintáctico, generando parsers descendentes iterativos recursivos. Ejemplos son JavaCC, que genera código Java, y ANTLR, que está escrito en Java pero puede generar código Java, C++ o C#. ANTLR se puede conseguir en http://www.antlr.org/ y JavaCC en http://javacc.dev.java.net/. Se puede utilizar la herramienta GSview para ver que el código PostScript generado sea sintácticamente correcto y visualizar el resultado. Se puede obtener en http://www.cs.wisc.edu/~ghost/gsview/ La entrega debe incluir: • Un programa que cumpla con lo solicitado. • El código fuente impreso del programa. Si se usaron herramientas generadoras de código, imprimir la fuente ingresada a la herramienta, no el código generado. • Informe conteniendo: 4 – Las modificaciones a la gramática o indicaciones adicionales que hayan sido necesarias para utilizarla con la herramienta elegida. – Descripción de cómo se implementó la solución – Ejemplos de fórmulas sintácticamente correctas e incorrectas, los resultados obtenidos y conclusiones. 5