Anda di halaman 1dari 74

[Escribir texto]

 

I.S.C. Leonel Vzquez Aguirre

Introduccin En estos apuntes de estudio de la materia Programacin de Sistemas deseo facilitarte el que aprendas los conceptos necesarios para el uso y construccin de compiladores. T como futuro Ingeniero en Sistemas Computacionales tienes que conocer como funciona un compilador este concepto te va ayudar a ser ms profesional en tu carrera para poder ayudarte en la toma de decisiones. Ahora empecemos dando lectura al siguiente cuento para adentrarnos al tema fascinante de los compiladores El compilador de Mara... Sin m, Mara vivira ms feliz en su integridad fsica y en su reputacin. La mayora de las injurias que recibe son provocadas por mi trabajo. De hecho, gran parte de los programadores que la emplean han proferido en alguna ocasin una expresin idiomtica de frustracin en contra de ella despus de que he hecho mi trabajo y he notificado que han cometido errores al programar. Supongo que a nadie le gusta que le digan que se equivoca. A pesar de ello, el trabajo que realizo es fundamental para construir muchas aplicaciones que corren en Mara y que son su gran orgullo (normalmente por ellos ha recibido halagos, le han comprado ms memoria, un tapetito verde para su mouse y es considerada imprescindible para su dueo). Probablemente soy subestimado y relegado a un segundo trmino (o a un ltimo trmino, como dira mi compadre el sistema operativo) pero en mi interior reconozco que dependo tanto de Mara como ella de m. La vida as es y lo acepto. Ojal que los programadores tambin lo aceptaran: habra menos frustracin y menos dedos pintados en el monitor de Mara. Mi trabajo consiste en dos partes fundamentales: comunicarme con un programador por medio del programa que l escribe y transformar lo que escribi a una forma que Mara lo entienda. Acto como un vnculo entre los programadores y Mara. Orgullosamente pertenenezco a la familia de traductores y muchos de mis parientes hacen trabajos similares al mo pero con otros nombres: editores, intrpretes, formadores de texto, ensambladores, etc. Cuando me comunico con un programador puede ser para dos cosas: para decirle que ha cometido errores (lo cual me ocupa gran parte del tiempo) y para decirle que todo est bien (lo cual ocurre despus de mucho insistir, en algunos casos). Generalmente notifico tres tipos de errores: 1. Error de lxico, que ocurre cuando el programador escribe una palabra que no conozco. 2. Error gramatical o sintctico, que ocurre cuando una palabra no se emplea de manera correcta en el programa o cuando se omite. Este es el error ms comn y se
I.S.C. Leonel Vzquez Aguirre

debe al desconocimiento del lenguaje por parte del programador. Siempre digo dnde ocurri el error y en ocasiones puedo sugerir como corregirlo. Algunos programadores sacan provecho de ello y nunca aprenden a escribir bien a la primera; esperan que yo marque el error y corrigen de acuerdo a lo que sugiero (mal, muchachitos, mal). 3. Error semntico, que ocurre cuando lo que se escribe, a pesar de ser correcto gramaticalmente, no puede o no debe ejecutarlo Mara. Cuido que lo indicado por el programador sea realizable por Mara para que ella no ofrezca informacin imprecisa que pueda hacer que pierda credibilidad. Cuando termina esta etapa de comunicacin con el programador y le digo que No hay errores, empieza el trabajo que el programador no ve (en ocasiones porque se pone a celebrar despus de leer el mensaje anterior). Debo convertir lo escrito en el programa a una forma que Mara puede entender. Lo primero que hago es obtener una relacin de todas las entidades manejadas en el programa y las guardo en una tabla. Despus convierto todas a las rdenes a una forma de lenguaje, llamado cdigo intermedio, que permitir que cualquier pariente de Mara entienda lo que el programador escribi. Digamos que es un lenguaje universal que toda computadora entiende. Cuando termino esto, debo determinar qu operaciones pueden suprimirse (porque se hacen varias veces o porque no son necesarias) y cules pueden escribirse de otra manera sin perder sentido el programa. Este proceso se conoce como optimizar cdigo y tiene como propsito que Mara no trabaje mucho. Cuando tengo el cdigo optimizado, entonces lo convierto a la forma en que Mara lo entiende y puede ejecutar. Ah es donde termina mi trabajo. Lo que ocurra despus es responsabilidad de Mara y tambin del programador: el fracaso, debido a que ocurran errores de ejecucin (que es responsable Mara de ellos, como la falta de memoria) y de lgica (responsabilidad total del programador: el programa no hace lo que el quiere). O el xito: el programa corri y todo mundo feliz. En ambos casos, NO SOY RESPONSABLE. Como ven, a veces me hacen responsable de cosas que yo no hago (claro, como no me puedo defender) y cuando hago bien mi trabajo, nadie lo nota. An no escucho a nadie que diga: Este buen programa se lo debo al excelente compilador que tengo!. Esto y mucho ms nunca lo escucharan mis odos Ser por ello que me crearon sin ellos? Autor desconocido

I.S.C. Leonel Vzquez Aguirre

Qu es la programacin de sistemas? Es la rama de la computacin que se encarga de estudiar el software de sistemas software base. Qu es el software base? Software que puede soportar otro software. Qu es el software de aplicacin? Software que est diseado y escrito para realizar tareas especficas personales, empresariales o cientficas.

Programacin de Sistemas

I.S.C. Leonel Vzquez Aguirre

Herramientas de la Programacin de Sistemas Cargadores Un cargador es un programa que coloca en la memoria principal de la computadora, el programa objeto guardado en algn dispositivo de almacenamiento secundario dejndolo listo para su ejecucin. Allocation (Asignacin): un programa objeto contiene instrucciones traducidas y valores de datos del programa fuente y especfica direcciones en memoria dnde cargarn estos elementos. Relocation (Relocalizacin): que modifica el programa objeto de forma que puede cargarse en una direccin diferente de la localidad especificada originalmente. Loading (Carga): que lleva el programa objeto a la memoria para su ejecucin. Linking (Ligado): que combina dos o ms programas objeto independientes y proporciona la informacin necesaria para realizar diferencias entre ellos.

Funciones

Tipos de cargadores Dependiendo de la manera en que se manejen los procesos de liga y de carga, podemos clasificar a los cargadores en: Cargadores Bootstrap o Iniciales: Indican a la computadora la forma de poner, dentro de la memoria principal, unos datos que estn guardados en un perifrico de memoria externa (cinta, disco, etc.). Sirven para cargar en la memoria pequeos programas que inician el funcionamiento de una computadora. Algunos programas que utilizan algunos de estos son: BIOS Sistema Operativo

Cargadores Absolutos. Como ya se menciono el programa cargador pone en memoria las instrucciones guardadas en sistemas externos. Independientemente de que sea un cargador inicial o no, si dichas instrucciones se almacenan siempre en el mismo espacio de memoria (cada vez que se ejecuta el programa cargador), se dice que es un cargador absoluto. Ejemplos de programas que utilizan cargadores absolutos: MSN Antivirus. Cargadores reubicables. En el proceso de carga reubicable (relocalizable), un mismo programa puede ejecutarse en diferentes posiciones de memoria. Para esto, el programa 5

I.S.C. Leonel Vzquez Aguirre

objeto debe utilizar referencias a relativas a una direccin especial llamada direccin de reubicacin. Algunos programas que utilizan algunos de estos son: Word, Excel, Internet Explorer, estos son todos los que al mismo tiempo se ejecuten varias veces en un mismo instante. Cargadores Ligadores. Conocidos tambin como linker, esto es montar un programa que consiste en aadir al programa objeto obtenido en la traduccin las rutinas externas a las que hace referencia dicho programa. Generalmente, dichas rutinas se encuentran guardadas en un archivo especial al que suele denominarse librera porque estn almacenadas todas las rutinas externas susceptibles de ser utilizadas por los diferentes programas del usuario. Ventajas

El mismo programa ligador puede tambin realizar la tarea de carga. Evita tener que guardar el cdigo ejecutable en un archivo. Ahorra espacio en disco.

Desventajas Se gasta tiempo en ligar todos los mdulos cuando se necesita ejecutar el programa. Caractersticas principales Cuando se utilizan subrutinas en un programa, el cdigo ejecutable de cada una de ellas debe encontrarse en memoria al tiempo de ejecucin. Antes de cargar un programa, debe ligarse su cdigo objeto con los cdigos objeto guardados en uno o mas archivos de cada una de las subrutinas invocadas, obteniendo un programa ejecutable que contiene el cdigo del modulo invocador, como el cdigo de los mdulos invocados. Macroprocesador Con el fin de evitar al programador la tediosa repeticin de partes idnticas de un programa, los ensambladores y compiladores cuentan con macroprocesadores que permiten definir una abreviatura para representar una parte de un programa y utilizar esa abreviatura cuantas veces sea necesario. Para utilizar una macro, primero hay que declararla. En la declaracin se establece el nombre que se le dar a la macro y el conjunto de instrucciones que representar. El programador escribir el nombre de la macro en cada uno de los lugares donde se requiera la aplicacin de las instrucciones por ella representadas. La declaracin se realiza una sola vez, pero la utilizacin o invocacin a la macro (macrollamada) puede hacerse cuantas veces sea necesario. 6
I.S.C. Leonel Vzquez Aguirre

La utilizacin de macros posibilita la reduccin del tamao del cdigo fuente, aunque el cdigo objeto tiende a ser mayor que cuando se utilizan funciones. Es tan comn el empleo de macroinstrucciones se les considera como una extensin de los lenguajes. De manera similar se considera al procesador de macroinstrucciones o macroprocesador como una extensin del ensamblador o compilador utilizado. El macroprocesador se encarga, en una primera pasada, de registrar todas las declaraciones de macros y de rastrear el programa fuente para detectar todas las macrollamadas. En cada lugar donde encuentre una macrollamada, el macroprocesador har la sustitucin por las instrucciones correspondientes. A este proceso de sustitucin se le denomina expansin de la macro. El macroprocesador elabora dos tablas para el manejo de las macros: Una tabla de macronombres que consiste de los nombres de las macros y un ndice que le permite localizar la definicin de la macro en otra tabla llamada tabla de macrodefiniciones. Como su nombre lo indica, la tabla de macrodefiniciones contiene las definiciones de todas las macros a utilizar en el programa. En ocasiones es conveniente agrupar macros, de acuerdo a las tareas que realizan, y almacenarlas en archivos que se constituyen en bibliotecas de macros. De esta manera, cuando se requiera la utilizacin de alguna macro en particular, se incluye en el programa fuente el archivo de la biblioteca de macros correspondiente. La mayora de los ensambladores y compiladores permiten el uso de pseudoinstrucciones condicionales del tipo IF ... ELSE, por medio de las cuales se puede controlar la traduccin de ciertos bloques de programa. Ensamblador Cuando se empezaron a utilizar smbolos mnemotcnicos, se escribieron programas para traducir automticamente los programas escritos en lenguaje ensamblador a lenguaje mquina, a estos traductores se les dio el nombre de ensambladores. La entrada para un ensamblador es un programa fuente escrito en lenguaje ensamblador y da como salida un programa objeto, escrito en lenguaje de mquina. Para evitar confusiones, de aqu en adelante llamaremos lenguaje ensamblador al conjunto de nemotcnicos y a las reglas para su manejo. Funcin El programa lee el archivo escrito en lenguaje ensamblador y sustituye cada uno de los cdigos mnemotcnicos que aparecen por su cdigo de operacin correspondiente en sistema binario.
I.S.C. Leonel Vzquez Aguirre

Motivos para utilizarlo

Rapidez: Como el programador directamente selecciona las instrucciones que se ejecutan en el programa, el programa final queda ms optimizado que un programa generado por un compilador. Mayor control de la computadora: Un programa puede accesar directamente cualquier componente y perifrico de la computadora. Independencia del lenguaje: No depende de libreras o del lenguaje mismo para realizar una tarea especfica. Lenguajes como el Basic limitan al programador a lo que el lenguaje puede hacer. La mayora de las computadoras pueden ensamblar: Los recursos necesarios para ensamblar un programa son mucho menores que los compiladores o interpretes. El ensamblador generalmente es ms rpido ensamblando un programa que un compilador generando un archivo ejecutable.

Motivos para no utilizarlo

Dependencia del hardware: El cdigo se hace en extremo dependiente del microprocesador, de los dispositivos, de los controladores, etc. Mayor tiempo de codificacin: El nmero de lneas de un programa hecho en ensamblador es mayor a uno hecho en un lenguaje de alto nivel (por ejemplo: Funcin en C puede realizar varias decenas o centenas de instrucciones del microprocesador). Comprensin ms profunda de la computadora: Entender un lenguaje de alto nivel es generalmente ms sencillo que el ensamblador. Comprender ensamblador requiere conocimientos ms exactos sobre el funcionamiento interno de la computadora. Errores ms frecuentes en el programa: El evitar un error o encontrar alguno que ya exista es difcil.
Tipos de Ensambladores.

Ensambladores Cruzados: Se utilizan en una computadora que posee un procesador diferente al que tendrn las computadoras donde va a ejecutarse el programa objeto producido. Ensamblador Residente: son aquellos que permanecen en la memoria principal de la computadora y cargan, para su ejecucin, al programa objeto producido. Macroensambladores: Son ensambladores que permiten el uso de macroinstrucciones (macros). Debido a su potencia, normalmente son programas robustos que no permanecen en memoria una vez generado el programa objeto. 8

I.S.C. Leonel Vzquez Aguirre

Microensambladores: Generalmente, los procesadores utilizados en las computadoras tienen un repertorio fijo de instrucciones, es decir, que el intrprete de las mismas interpretaba de igual forma un determinado cdigo de operacin. Ensambladores de una fase: Estos ensambladores leen una lnea del programa fuente y la traducen directamente para producir una instruccin en lenguaje mquina o la ejecuta si se trata de una pseudoinstruccin. Ensambladores de dos fases: Los ensambladores de dos fases se denominan as debido a que realizan la traduccin en dos etapas. En la primera fase, leen el programa fuente y construyen una tabla de smbolos; de esta manera, en la segunda fase, vuelven a leer el programa fuente y pueden ir traduciendo totalmente, puesto que conocen la totalidad de los smbolos utilizados y las posiciones que se les ha asignado. Estos ensambladores son los ms utilizados en la actualidad.

I.S.C. Leonel Vzquez Aguirre

Diseo de lenguajes de programacin Un factor importante para un buen curso de programacin es el lenguaje de programacin que se va a utilizar, ya que el lenguaje usa los mismos procesos de pensamiento que el programador, o en otras palabras, el programador imprime sus ideas en su lenguaje de programacin, as que lgicamente la calidad del lenguaje re refleja en la calidad del programa resultante. En los ltimos aos algunos principios bsicos que son la base para los lenguajes de programacin buenos han comenzado a surgir. Descripcin del problema Sammet observa 167 tipos de idiomas y aunque los primeros lenguajes de programacin comenzaron hace 25 aos, y todava hasta hace poco haba poco inters hacia hacer nuevos lenguajes de programacin. Ninguna crtica se debe nivelar contra los diseadores del FORTRAN; tenan absolutamente bastante apuro ya que diseaban y ponan uno de los primeros idiomas de alto nivel en ejecucin. Ahora como todo, los errores anteriores han conducido a la creacin de mejores lenguajes de programacin, ya que en base a esos errores han servido de experiencia para corregir el diseo. Consideraciones preliminares En el diseo de un nuevo lenguaje, ciertas materias requieren pensarse bien antes de que cualquier consideracin se d a los detalles del diseo. Hay varias preguntas que debemos hacernos y la primera y ms importante es: es necesario disear otro lenguaje? Casi cualquier acercamiento alternativo ser ms simple y ms rpidamente que procurando la tarea difcil y desperdiciadora de tiempo de disear totalmente nuevos idiomas. Puede un lenguaje ya existente ser extendido? Es ms fcil disear una extensin a un lenguaje ya existente, incluso si la extensin implica un nuevo compilador, que disear un lenguaje nuevo. Si hace esto, y no se tiene cuidado de no disear una extensin muy larga y complicada, esta se convertir automticamente, en un lenguaje nuevo.

I.S.C. Leonel Vzquez Aguirre

10

Seria posible modificar un lenguaje existente, usando un microprocesador o algo similar? El poder de esta herramienta para tareas mas complejas, por ejemplo introducir las nuevas estructuras de datos, son limitadas y se les debe dar una consideracin importante para disear un nuevo lenguaje, simplemente para reducir al mnimo el trabajo y el tiempo. Cul es el propsito del lenguaje? Un lenguaje est a menudo especficamente diseado para un rea de uso, y entre ms sea especfico ese lenguaje, mejor ser para resolver problemas en esa rea. Ya resumido, es mejor disear un nuevo lenguaje completamente diferente que uno similar a otro ya existente. Fuentes de ideas La inspiracin para el diseo del lenguaje de programacin ha salido de varias fuentes en el pasado. Derivamos mucha de nuestra prctica actual en lenguajes de programacin de idiomas naturales. Muchas de nuestras construcciones se expresan de una forma que se basa en idiomas naturales. Metas y filosofas de diseo de lenguajes de programacin Cuando se disea un lenguaje de programacin, la atencin particular se debe dar a las metas del lenguaje. Un nmero de metas, como comunicacin humana, de la prevencin y de la deteccin de los errores importantes, utilidad, eficacia del programa, compilabilidad, eficacia, independencia de la mquina y simplicidad, se describen despus. Comunicacin humana Mientras que es importante comunicarse eficientemente con la computadora, la meta ms bsica de un lenguaje de programacin es comunicacin entre los humanos. Si un programa no se puede entender por los seres humanos, es difcil verificar y no puede ser soportado o modificado. Los lenguajes de programacin deben ser diseados con claridad ya que la legibilidad de programas es mucho ms importante a la larga que su escritura, ya que su sintaxis debe reflejar su semntica por que Si la sintaxis del lenguaje 11

I.S.C. Leonel Vzquez Aguirre

de programacin no refleja las manipulaciones realizadas, casi no hay posibilidades de entender el programa por lo confuso que resultara este. Prevencin y deteccin de errores Los programadores hacen errores por lo que se necesita asistir al programador en la tarea no slo de prevenir sino tambin de detectar, identificar y de corregir errores. Una ms de las funciones que un idioma de alto nivel realiza es hacer ciertos errores imposibles. Un buen ejemplo de esto es el viejo peligro del lenguaje ensamblador de la ramificacin en el centro de los datos. Dado correctamente un recopilador de la funcin, no hay ocasin cualesquiera que un programador del ALGOL confiar siempre esta equivocacin. Utilidad Lo que se recomienda o se desea es que las construcciones de un lenguaje sean fciles de aprender y de recordar. Una vez que los programadores estn al corriente del lenguaje, no debe ser necesario que consulten los manuales constantemente. El lenguaje debe ser tan simple y directo que la manera apropiada de hacer algo es razonable y evidente. Por otro lado, no debe haber muchas diversas maneras de hacer la misma cosa, pues entonces hace que el programador tenga que decidir cul es el mejor, siendo que se va a realizar la misma accin. Otro principio importante es la no sorpresa, tambin conocido como la regla de menor asombro que es cuando un programador que ha hecho el esfuerzo para aprender el idioma, tenga la facilidad de poder predecir la conducta de l. Programacin efectiva Un inters principal en la ingeniera de software es el registro de las decisiones hechas en el desarrollo de programa. El programa en s mismo es el mejor lugar para registrar esas decisiones. El lenguaje de programacin debera facilitar una declaracin clara con las intenciones del programador. Siempre que se pueda, el lenguaje deber permitir que los programadores declaren lo que quieran y tengan el compilador ponindolo en prctica. El lenguaje de alto nivel hace esto en cierta medida. Adems de la declaracin de decisiones claramente, es tambin deseable poder ser capaz de localizar cambios de decisin o de 12

I.S.C. Leonel Vzquez Aguirre

efecto. Tales cambios son inevitables; no debera ser necesario volver a escribir el programa entero cuando requieren un cambio. Tambin es deseable en un lenguaje de programacin apoyar la abstraccin, hay muchos mtodos diferentes, nuevos todava se desarrollan, y ningn mtodo es claramente superior a todos los otros. Un ltimo aspecto que se debe desalentar en un lenguaje de programacin son los trucos para programas, ya que son sumamente perjudiciales a la legibilidad y raras veces son justificados. Compilabilidad Un lenguaje debe ser compilable. Con otras palabras, debe ser tan simple como sea posible compilar. La complejidad en el trabajo puede llevarte al las dos fases del proceso de compilacin, el anlisis y la sntesis. La generacin de cdigo puede ser ms difcil si demasiados rasgos complejos del lenguaje son intilmente incluidos. Especialmente si son usados muy poco asi como and/or, pueden ser simulados por un simple constructor. Eficiencia Es tambin importante recordar que la eficiencia no es justamente la velocidad, el espacio, o el acceso. La ineficiencia de espacio en particular puede ser tan importante como la ineficiencia de tiempo, y se est volviendo ms importante por los recientes desarrollos en mini computadoras y microcomputadoras. La eficiencia debe considerarse en el contexto del ambiente total, recordando que las mquinas se hacen mas baratas y los programadores se hacen ms caros. Las diferencias en la eficiencia de 10 o 20 por ciento, o 30 hasta un 40 por ciento, pueden ser toleradas. Cuando la diferencia es un factor de 2 o de 10, la situacin es ya no tolerable por lo siguiente: 1. Por muy barato y rpido que el hardware es, es ms barato y ms rpido al correr un programa eficiente 2. La velocidad y el costo de 3. Los usuarios no deberan tener que sacrificar legibilidad para conseguir el valor de su dinero del hardware mejorado.

I.S.C. Leonel Vzquez Aguirre

13

Otra degradacin de eficiencia proviene de interpretaciones elaboradas de las interpretaciones elaboradas de aspecto inocente de trampas bobas que ejecutan increblemente ineficientemente. Un modo muy descuidado de solucionar el problema de eficiencia es el de consejo optimizacin para el compilador. Finalmente, las mejoras significantes en la eficiencia pueden ser obtenidas haciendo el lenguaje lo suficientemente simple que es fcil generar razonablemente cdigo eficiente. Independencia de la maquina Aunque una de los principales objetivos de los lenguajes de alto nivel fue la independencia de maquina, esta idea no ha sido enteramente comprendida. La mayora de los lenguajes actuales permanecen en la dependencia de maquina. Un rea de la extrema dependencia de maquina es la aritmtica de punto flotante. Problemas similares pasan con el conjunto de caracteres. Los modelos de bits representados a un carcter dado no son usualmente relevante. Pero la intercalacin de caracteres varia de una maquina a otra, esto hace la independencia de maquina sea muy difcil. Es usualmente seguro asumir que:

Los dgitos estn en orden El alfabeto maysculo esta en orden El alfabeto minsculo esta en orden El espacio en blanco precede a cualquier otro carcter Los caracteres que no se escriben tambin pueden causar problemas. Cabe

mencionar que la independencia de maquinas no es requerido para todos los lenguajes. En algunos lenguajes es inevitable tener cierto grado de dependencia, por ejemplo en lenguajes que intentan escribir sistemas operativos, pero para algunas aplicaciones orientadas a lenguajes es cuestionable si es necesario quitar la dependencias de maquinas. Simplicidad Es claro que la mayora de usuario prefiera un lenguaje simple, de hecho, un lenguaje que no puede ser simple generalmente falla en muchos otros aspectos. La simplicidad por s mismo no insina un buen lenguaje, El lenguaje Basic es
I.S.C. Leonel Vzquez Aguirre

14

seguramente una lengua simple, pero en particular bien no es diseado y debera ser evitado, de ser posible, para cualquier tipo de desarrollo de software serio. Uniformidad Se define como haciendo la misma cosa con la misma manera sin importar contexto. La uniformidad puede considerarse util, o intil, segn como se le aplica, un ejemplo posible de la uniformidad intil es la combinacin de las ideas de la expresin y de la declaracin en ALGOL 68. Si est adoptado como principio del diseo del lenguaje, puede ayudar en la reduccin del nmero de cosas que el programador tiene que pensar alrededor contemporneamente, puesto que no necesitan considerar contexto para saber actuar una caracterstica. Ortogonalidad La idea bsica de ortogonalidad es tener varios conceptos generales cada uno de los cuales funciona slo, sin el conocimiento de la estructura interna de los otros conceptos. Buenos ejemplos de esto en ALGOL 68 son los valores de entrada y operadores aritmticos Los valores de entrada primitivos producen un valor; pero no cuidan como son usados. Los operadores aritmticos combinan dos valores; pero no cuidan como son obtenidas las variables. Aunque ortogonalidad puede proporcionar mejoras considerables en la simplicidad, es dudoso que ortogonalidad sea til como un substituto de simplicidad. Generalizacin y especializacin Una manera global de describir generalizacin es si entendemos esto, despus entenderemos tambin todo lo similar a eso. Es una manera parecida de ortogonalidad, es una ayuda a la simplicidad, pero no un substituto de la simplicidad. Un caso de generalidad contra especializacin que menciona es que muchos lenguajes tienen funciones fijas que toman cualquier nmero de argumentos, mientras muy raras veces las funciones de usuario pueden hacer esto. Slo las funciones de entrada - salida y unas funciones especiales como MX y MIN realmente necesitan los nmeros variables de argumentos. En la mayor parte de los casos, los usuarios no necesitan listas

I.S.C. Leonel Vzquez Aguirre

15

de argumento de longitud de variables para sus propios procedimientos si el lenguaje ya proporciona estos casos especiales. Estructura de expresin Los componentes de una expresin, operadores y accesos de valor son demasiado peculiares a lenguas especficas para ser dadas con mucho tratamiento, pero algunos comentarios pueden estar sobre expresiones en general. El mtodo o norma de determinar la orden (el pedido) de evaluacin es basado en dos niveles: puesta entre parntesis explcito y encuadernacin de operador. La puesta entre parntesis explcita comprende ambos parntesis y la puesta entre parntesis total proporcionada por las fronteras de la expresin. La encuadernacin de operador es el aspecto ms familiar de orden (pedido) de evaluacin. Existen Tres sistema bsicos de encuadernacin: izquierdo-a-derecha, derecho-aizquierdo, y prioridad. De la izquierda a la derecha no es recomendable: la nica razn es por que esto es usado en todo (con frecuencia en ensambladores) es que esto es simple. Derecho-a-izquierdo es notable slo porque esto es usado en APL. Esto es usado principalmente debido a la amplia variedad de operadores, que lo hace difcil de intentar asignarles las preferencias a todas. Un acercamiento alternativo para una lengua, con muchos a operadores es de asignar prioridades a algunos operadores ms simples y requerir la puesta entre parntesis explcita del resto. Estructura de datos Se consideran cuatro aspectos de estructura de datos. Primero una discusin de la alternativa para declaraciones de datos es presentada. Despus, una descripcin de la variedad de los tipos de datos que estn disponibles en lenguajes de programacin hoy y esto debera ser considerada si un diseo de lenguaje nuevo y que es emprendido. El tema final trata con una discusin del alcance de variables en un lenguaje de programacin. Las declaraciones de una lengua son el medio por el que la informacin no especial es transportada al recopilador. Otro aspecto de la declaracin de constantes es que es til ser capaz de escribir una constante para cualquiera de los datos que teclea el programa. 16

I.S.C. Leonel Vzquez Aguirre

Por ejemplo, es debera ser posible para escribir una serie constante. Tal constante en gran parte eliminara la necesidad de inicializar series, como las series ms inicializadas son realmente constantes, y prevendran al programador de por casualidad destruir el contenido de tal constante. Asimismo esto es til, esencialmente como una abreviatura de forma, ser capaz de dar nombres a tipos y luego usar aquellos nombres en la declaracin de variables o en la construccin de tipos ms complejos. La sintaxis de constantes y declaraciones puede estar similar a este tipo, como en lo siguiente: CONST table_size = 437; TYPE big_array = ARRAY [1 TO 1000, 1 TO 1000] OF integer; Las semejanzas entre los dos pueden criar un pensamiento de tentacin: por qu no generalizar a ambos a una macro facilidad de parmetros? La razn bsica de evitar esto es que constantes de este tipo sean, las nicas cosas que seran declaradas. Si el diseador decide no permitir la inicializacin de variables, la sintaxis de declaraciones variables se hace bastante simple. Una sintaxis muy legible es usada por PASCAL: VAR a, b, c: integer; La legibilidad de esta forma se disminuye rpidamente si la lista de nombres variables es llenada de otras cosas como la inicializacin. Estructura de control Muchos de los centros de discusin es la inclusin del GOTO, esta instruccin es la mayor usada y la mejor en los constructores de alto nivel. Esta simple instruccin ha sido combinada con la instruccin de ALGOL inicio-fin.

I.S.C. Leonel Vzquez Aguirre

17

Traductores Traductor: Es una mquina terica que tiene como entrada un texto escrito en un lenguaje L1 y como salida un texto escrito en un lenguaje L2. Habitualmente se denomina a L1 lenguaje fuente y a L2 lenguaje objeto. Las tcnicas que se desarrollan en esta materia no slo son vlidas para la implementacin de compiladores, sino que son aplicables en general a todos los sistemas de procesamiento de lenguajes y de traduccin. Estos sistemas pueden ser de distintos tipos: Traductores de lenguaje natural: Seran los que tradujeran un lenguaje natural en otro (por ejemplo, espaol a ingls). Esto en la actualidad no se ha conseguido debido fundamentalmente a la ambigedad del lenguaje natural. Los mayores logros en la materia siempre trabajan con un subconjunto del lenguaje natural, limitando las construcciones sintcticas vlidas y/o el vocabulario. Este tema se aborda generalmente mediante tcnicas de inteligencia artificial. Compilador: Es un traductor que convierte un texto escrito en un lenguaje fuente de alto nivel en un programa objeto en cdigo mquina. Intrprete: Es un traductor que realiza la operacin de compilacin paso a paso. Para cada sentencia que compone el texto de entrada, se realiza una traduccin, ejecuta dicha sentencia y vuelve a iniciar el proceso con la sentencia siguiente. La principal ventaja del proceso de compilacin frente al de interpretacin es que los programas se ejecutan mucho ms rpidamente una vez compilados; por el contrario, es ms cmodo desarrollar un programa mediante un intrprete que mediante un compilador puesto que en el intrprete las fases de edicin y ejecucin estn ms integradas. La depuracin de los programas suele ser ms fcil en los intrpretes que en los compiladores puesto que el cdigo fuente est presente durante la ejecucin. Estas ventajas pueden incorporarse al compilador mediante la utilizacin de entornos de desarrollo y depuradores simblicos en tiempo de ejecucin. Preprocesadores: Procesan un texto fuente modificndolo en cierta forma previamente a la compilacin. Por ejemplo, muchos compiladores admiten un conjunto de macroinstrucciones ajenas al lenguaje en s que indican al compilador si tiene que incluir algn fichero externo, si ha de hacer o no un listado completo de la compilacin, etc... Conversores Fuente-Fuente: (LCP) Traducen un lenguaje fuente de alto nivel a otro (por ejemplo, PASCAL -> C). Una aplicacin interesante de la traduccin fuente-fuente es el desarrollo e implementacin de prototipos de nuevos lenguajes de programacin. As, por ejemplo, si se desea definir un lenguaje especializado puede implementarse rpidamente mediante su traduccin a un lenguaje convencional de alto nivel. Rutinas de anlisis de instrucciones: El conjunto de instrucciones del entorno de un sistema operativo constituye un lenguaje que debe ser analizado previamente para realizar las acciones oportunas. Igualmente, ciertos programas como editores de texto, sistemas de diseo asistido, etc..., utilizan instrucciones complejas que deben interpretarse adecuadamente. 18
I.S.C. Leonel Vzquez Aguirre

Ensambladores: Son compiladores cuyo lenguaje de entrada, llamado ensamblador, permite la traduccin de cada sentencia fuente a una instruccin en cdigo mquina. Compilador cruzado: Es el que genera un cdigo objeto ejecutable en una computadora distinta aquella en el que se realiza la compilacin. Compilacin-Montaje-Ejecucin: En las aplicaciones grandes es conveniente fragmentar el programa a realizar en mdulos que se compilan por separado, y una vez que estos estn compilados unirlos mediante un programa denominado montador para formar el mdulo ejecutable. El montador se encarga, a su vez, de incluir las libreras donde estn guardadas las funciones predefinidas de uso comn. Compilacin en una o varias pasadas: Se llama pasada a cada lectura que hace el compilador del texto fuente. Compilacin incremental. Este compilador acta de la siguiente manera. Compila un programa fuente. Caso de detectar errores al volver a compilar el programa corregido slo compila las modificaciones que se han hecho respecto al primero. Autocompilador: Es aqul que est escrito en el mismo lenguaje que se pretende compilar. Supongamos, por ejemplo, que queremos desarrollar la versin 2.0 de un compilador Pascal. Dicho compilador generar un cdigo mucho ms rpido y eficiente que el que generaba la versin anterior 1.0. Sin embargo, son ya muchas las mquinas (IBM 370, Serie 1, PDP 11, ...) que disponen del antiguo compilador, o que al menos tienen otro compilador Pascal. La mejor opcin consiste en escribir el nuevo compilador en Pascal, ya que as podr (el compilador) ser compilado en las distintas mquinas por los compiladores Pascal ya existentes. Metacompilador: Es un traductor que tiene como entrada la definicin de un lenguaje y como salida el compilador para dicho lenguaje. Decompilador: Es el que traduce cdigo mquina a lenguaje de alto nivel. Los decompiladores ms usuales son los desensambladores, que traducen un programa en lenguaje mquina a otro en ensamblador. Estructura de un compilador Un compilador es un programa, en el que pueden distinguirse dos subprogramas o fases principales: una fase de anlisis, en el cual se lee el programa fuente y se estudia la estructura y el significado del mismo; y otra fase de sntesis, en la que se genera el programa objeto. En un compilador pueden distinguirse, adems, algunas estructuras de datos comunes, la ms importante de las cuales es la tabla de smbolos, junto con las funciones de gestin de sta y de los dems elementos del compilador, y de una serie de rutinas auxiliares para deteccin de errores.

I.S.C. Leonel Vzquez Aguirre

19

El esquema general de un compilador podra ser el siguiente:

Esquema de un compilador Las funciones de estos mdulos son las siguientes: Analizador lxico: Las principales funciones que realiza son:

Identificar los smbolos. Eliminar los blancos, caracteres de fin de lnea, etc... 20

I.S.C. Leonel Vzquez Aguirre

Eliminar los comentarios que acompaan al programa fuente. Crear unos smbolos intermedios llamados tokens (Lexema, Gramema). Avisar de los errores que detecte.

Analizador sintctico: Comprueba que las sentencias que componen el texto fuente son correctas en el lenguaje, creando una representacin interna que corresponde a la sentencia analizada. De esta manera se garantiza que slo sern procesadas las sentencias que pertenezcan al lenguaje fuente. Durante el anlisis sintctico, as como en las dems etapas, se van mostrando los errores que se encuentran. Anlisis semntico: Se ocupa de analizar si la sentencia tiene algn significado. Se pueden encontrar sentencias que son sintcticamente correctas pero que no se pueden ejecutar porque carecen de sentido. En general, el anlisis semntico se hace a la par que el anlisis sintctico introduciendo en ste unas rutinas semnticas. Generador de cdigo intermedio: El cdigo intermedio es un cdigo abstracto independiente de la mquina para la que se generar el cdigo objeto. El cdigo intermedio ha de cumplir dos requisitos importantes: ser fcil de producir a partir del anlisis sintctico, y ser fcil de traducir al lenguaje objeto. Esta fase puede no existir si se genera directamente cdigo mquina, pero suele ser conveniente emplearla. Optimizador de cdigo intermedio: A partir de todo lo anterior crea un nuevo cdigo ms compacto y eficiente, eliminando por ejemplo sentencias que no se ejecutan nunca, simplificando expresiones aritmticas, etc... La profundidad con que se realiza esta optimizacin vara mucho de unos compiladores a otros. En el peor de los casos esta fase se suprime. Generador de cdigo objeto: A partir de los anlisis anteriores y de las tablas que estos anlisis van creando durante su ejecucin produce un cdigo o lenguaje objeto que es directamente ejecutable por la mquina. Es la fase final del compilador. Las instrucciones del cdigo intermedio se traducen una a una en cdigo mquina reubicable. Nota: Cada instruccin de cdigo intermedio puede dar lugar a ms de una de cdigo mquina. La tabla de smbolos: La tabla de smbolos es la estructura de datos usada por el compilador para asociar a cada smbolo del programa fuente (identificadores, constantes, etc...) un contenido semntico. Rutinas de errores: Estn incluidas en cada uno de los procesos de compilacin (anlisis lexico, sintctico y semntico), y se encargan de informar de los errores que encuentran en texto fuente. Sintaxis Todo lenguaje de programacin tiene reglas que describen la estructura sintctica de programas bien formados. En Pascal, por ejemplo, un programa se
I.S.C. Leonel Vzquez Aguirre

21

compone de bloques, un bloque de proposiciones, una proposicin de expresiones, una expresin de componentes lxicos, y as sucesivamente. Se puede describir la sintaxis de las construcciones de los lenguajes de programacin por medio de gramticas de contexto libre o notacin BNF ( Backus-Naur Form). Gramtica: es el conjunto de reglas que establece el correcto funcionamiento de la estructura lingstica de una determinada lengua, de las diversas partes de la oracin segn las normas de cada lengua. Dictamina que palabras son compatibles entre s y que oraciones estn bien formadas o son gramaticalmente correctas.

Las gramticas ofrecen ventajas significativas a los diseadores de lenguajes y a los desarrolladores de compiladores. Las gramticas son especificaciones sintcticas y precisas de lenguajes de programacin. A partir de una gramtica se puede generar automticamente un analizador sintctico. El proceso de construccin puede llevar a descubrir ambigedades. Una gramtica proporciona una estructura a un lenguaje de programacin, siendo ms fcil generar cdigo y detectar errores. Es ms fcil ampliar/modificar el lenguaje si est descrito con una gramtica.

Qu es el analizador sintctico? Es la fase del compilador que se encarga de revisar el programa fuente de entrada en base a una gramtica dada. Y en caso de que el programa fuente de entrada sea vlido, suministra el rbol sintctico que lo reconoce. En teora, se supone que la salida del analizador sintctico es alguna representacin del rbol sintctico que reconoce la secuencia de tokens suministrada por el analizador lxico. En la prctica, el analizador sintctico tambin hace:

Acceder a la tabla de smbolos (para hacer parte del trabajo del analizador semntico). Verificacin de tipos ( del analizador semntico). Generar cdigo intermedio. Generar errores cuando se producen.

En definitiva, realiza casi todas las operaciones de la compilacin. Este mtodo de trabajo da lugar a los mtodos de compilacin dirigidos por sintaxis. Manejo de errores sintcticos Si un compilador tuviera que procesar slo programas correctos, su diseo e implantacin se simplificaran mucho. Pero los programadores a menudo escriben programas incorrectos, y un buen compilador debera ayudar al programador a identificar y localizar errores. Es ms, considerar desde el principio el manejo de errores puede simplificar la estructura de un compilador y mejorar su respuesta a los errores.
I.S.C. Leonel Vzquez Aguirre

22

Los errores en la programacin pueden ser de los siguientes tipos:

Lxicos, producidos al escribir mal un identificador, una palabra clave o un operador. Sintcticos, por una expresin aritmtica o parntesis no equilibrados. Semnticos, como un operador aplicado a un operando incompatible. Lgicos, puede ser una llamada infinitamente recursiva.

El manejo de errores de sintaxis es el ms complicado desde el punto de vista de la creacin de compiladores. Nos interesa que cuando el compilador encuentre un error, se recupere y siga buscando errores. Por lo tanto el manejador de errores de un analizador sintctico debe tener como objetivos:

Indicar los errores de forma clara y precisa. Aclarar el tipo de error y su localizacin. Recuperarse del error, para poder seguir examinando la entrada. No ralentizar significativamente la compilacin.

Un buen compilador debe hacerse siempre teniendo tambin en mente los errores que se pueden producir; con ello se consigue: Simplificar la estructura del compilador. Mejorar la respuesta ante los errores. Tenemos varias estrategias para corregir errores, una vez detectados: Ignorar el problema (Panic mode ): Consiste en ignorar el resto de la entrada hasta llegar a una condicin de seguridad. Una condicin tal se produce cuando nos encontramos un token especial (por ejemplo un ; o un END).A partir de este punto se sigue analizando normalmente. Recuperacin a nivel de frase: Intenta recuperar el error una vez descubierto. En el caso anterior, por ejemplo, podra haber sido lo suficientemente inteligente como para insertar el token ; . Hay que tener cuidado con este mtodo, pues puede dar lugar a recuperaciones infinitas. Reglas de produccin adicionales para el control de errores: La gramtica se puede aumentar con las reglas que reconocen los errores ms comunes. En el caso anterior, se podra haber puesto algo como: sent_errnea sentencia_acabada sent_sin_acabar sent_sin_acabar sentencia_acabada ; sentencia ; sentencia

Lo cual nos da mayor control en ciertas circunstancias. Correccin Global: Dada una secuencia completa de tokens a ser reconocida, si hay algn error por el que no se puede reconocer, consiste en encontrar la secuencia completa ms parecida que s se pueda reconocer. Es decir, el analizador sintctico le 23

I.S.C. Leonel Vzquez Aguirre

pide toda la secuencia de tokens al lxico, y lo que hace es devolver lo ms parecido a la cadena de entrada pero sin errores, as como el rbol que lo reconoce. Tipo de gramtica que acepta un analizador sintctico Nosotros nos centraremos en el anlisis sintctico para lenguajes basados en gramticas formales, ya que de otra forma se hace muy difcil la comprensin del compilador, y se pueden corregir, quizs ms fcilmente, errores de muy difcil localizacin, como es la ambigedad en el reconocimiento de ciertas sentencias. libre: La gramtica que acepta el analizador sintctico es una gramtica de contexto

Gramtica: Herramienta que nos ayuda a representar la sintaxis.

Tipos de gramticas

Graficas :diagramas de sintaxis Formales: gramticas libres de contexto

Definicin formal de la Gramtica: Est representada por el cudruplo G (V, T, P, S) Donde: V = No terminales. T = Terminales. P = Reglas de Produccin. S = Axioma o Smbolo Inicial.

Ejemplo: Se considera la gramtica que reconoce las operaciones aritmticas. E E T T F F F En el que: V = {E, T, F} estn a la izquierda de la regla o sea que son smbolos no terminales.
I.S.C. Leonel Vzquez Aguirre

E+T T T*F F ID NUM (E)

24

T = {ID, NUM, ( ,) ,+ ,*} P = Son las siete reglas de produccin. S = Smbolo inicial. Podra ser cualquiera, en este caso es E. Derivaciones: La idea central es que se considera una produccin como una regla de reescritura, donde el no terminal de la izquierda es sustituido por la cadena del lado derecho de la produccin.
o

Derivacin por la izquierda: Derivacin donde solo el no terminal de ms a la izquierda de cualquier forma de frase se sustituye en cada paso. Derivacin por la derecha o Derivacin cannica: Derivacin donde el no Terminal ms a la derecha se sustituye en cada paso.

Ejemplo: Sea la gramtica E E E E E E E+E E-E E*E E/E E^E id

Constryase una derivacin por la izquierda y por la derecha para la siguiente frase: a*c+b ( id1 * id2 + id3) Derivacin ms a la izquierda: E E+E E*E+E id1 * E + E id1 * id2 + E id1 * id2 + id3

Derivacin ms a la derecha: E E+E E + id3 E * E + id3 E * id2 + id3 id1 * id2 + id3

rbol de derivacin: Las gramticas tienen la propiedad de que las derivaciones pueden ser representadas en forma arborescente. Es una representacin que se utiliza para describir el proceso de derivacin de dicha sentencia. Como nodos internos del rbol, se sitan los elementos no terminales de las reglas de produccin que vayamos aplicando, y tantos hijos como smbolos existan en la parte derecha de dichas reglas. Veamos un ejemplo: Sea la gramtica anterior: 25

I.S.C. Leonel Vzquez Aguirre

E T F

E+T | T T*F | F (E)|a|b

Supongamos que hay que reconocer: ( a + b ) * a + b Si el rbol puede construirse, es que la sentencia es correcta:

Ambigedad Una gramtica es ambigua si derivando de forma diferente con el mismo tipo de derivacin se llega al mismo resultado. Ejemplo: Considrese la gramtica E E E E si tenemos E+E E*E (E) id | num aux + cont + i

<ID><+><ID><+><ID>

I.S.C. Leonel Vzquez Aguirre

26

Forma Sentencial Es cualquier secuencia de terminales y no terminales obtenida mediante derivaciones a partir del smbolo inicial. Diagrama de sintaxis Nomenclatura: Smbolo No Terminal Smbolo Terminal Flujo de la informacin <Nombre> Nombre del diagrama.

I.S.C. Leonel Vzquez Aguirre

27

Gramaticas de contexto libre Tienen la forma : Donde : X Y

X =1, y siempre es un elemento No Terminal X ( vacio)

Estructura sintctica bsicas comunes a) Secuencial 1) <X > a 2) b X ab

<X > Y a

Yb

b)Alternativa o seleccin <X> a

a|b|c

I.S.C. Leonel Vzquez Aguirre

28

c)Repetitivos o ciclos <X> a ;

a, X

a;<X>

Tipos de Anlisis o Tcnicas de Anlisis Sintctico De la forma de construir el rbol sintctico se desprenden dos tipos o clases de analizadores sintcticos. Pueden ser descendentes o ascendente: Descendentes: Parten del smbolo inicial y van efectuando derivaciones a izquierda hasta obtener la secuencia de derivaciones que reconoce a la sentencia. Pueden ser:
o o o

Con retroceso. Con recursin. LL(1)

Ascendentes: Parten de la sentencia de entrada, y van aplicando reglas de produccin hacia atrs (desde el consecuente hasta el antecedente), hasta llegar al smbolo inicial. Pueden ser:
o o

Con retroceso. LR(1)

Analizador Sintctico VA TOKEN P.F ANALIZADOR LEXICO ANALIZADOR SINTACTICO

SALIDA

DAME TOKEN TABLA DE SIMBOLOS

OK ERROR SINTACTICO

Un analizador sintctico ( Parser ) es un programa que reconoce si una o varias cadenas de caracteres forman parte de un determinado lenguaje. Los lenguajes habitualmente reconocidos por los analizadores sintcticos son los lenguajes libres de 29
I.S.C. Leonel Vzquez Aguirre

contexto. Cabe notar que existe una justificacin formal que establece que los lenguajes libres de contexto son aquellos reconocibles por un autmata de pila, de modo que todo analizador sintctico que reconozca un lenguaje libre de contexto es equivalente en capacidad computacional a un autmata de pila. Mtodo predictivo o Anlisis sintctico LL(1) BUFFER DE ENTRADA. A + B * C $ OK ANALIZADOR SINTACTICO PREDICTIVO SALIDA

E $ PILA PREDICTIVA

ERROR SINTACTICO

MATRIZ PREDICTIVA

Los analizadores sintcticos LL(1) emplean una tabla para el anlisis sintctico. La primera L establece el hecho de que la cadena de caracteres de entrada se analiza de izquierda a derecha. La segunda L, que construye una derivacin de extremo izquierdo, y el 1 significa que puede utilizarse un smbolo hacia delante para crear la tabla. Un smbolo hacia delante significa que el siguiente smbolo de entrada es buscado . Los analizadores LL(1) son en esencia analizadores sintcticos descendentes recursivos controlados por la tabla. Un analizador sintctico LL(1) debe trabajar con una gramtica LL(1), y para ello es necesario dos definiciones preliminares. Construccin de las funciones PRIMEROS y SIGUIENTES Primeros._ Conjunto de elementos terminales que se encuentran al lado derecho de la produccin. Para lo cual se tiene la tres siguientes formulas. Su palabra lo dice es el primer elemento terminal que encontramos en el lado derecho de la produccin Si es cualquier cadena de smbolos gramaticales, se considera FIRST( ) como el conjunto de terminales que encabezan las cadenas derivadas de . Si = => , entonces
*

tambin est en FIRST( ).

Para calcular FIRST(X) para algn smbolo X de la gramtica, se aplican las siguientes reglas hasta que no se pueda aadir nada nuevo al conjunto FIRST:
I.S.C. Leonel Vzquez Aguirre

30

1. Si X es terminal, entonces FIRST(X) es {X}. 2. Si X es no terminal y existe la produccin X 4. Si X es no terminal y X , entonces aadir a FIRST(X).

Y Y .. . Y es una produccin entonces, para todo i


1 2 k 1 2 i-1

(con i variando desde 1 hasta k) tal que Y , Y , ..., Y sean todos no terminales y FIRST(Y ), FIRST(Y ), ..., FIRST(Y ) contengan todos , se aaden todos los smbolos no nulos de FIRST(Y ) a FIRST(X). Finalmente, si para j = 1, 2, ..., k (o sea, en todos), entonces se aade
i 1 2 i-1

est en FIRST(Y )
j

a FIRST(X).
1

Dicho de otra forma, lo anterior significa que todos los elementos de FIRST(Y ), excepto , pertenecen tambin a FIRST(X). Si Y no deriva , entonces ya ha terminado
1

el clculo de FIRST(X), pero en caso contrario, es decir, si Y = => , entonces todos los elementos de FIRST(Y ) excepto
2 i

pertenecen tambin a FIRST(X), y as sucesivamente. se aade a FIRST(X).

Finalmente, si todos los Y derivan , entonces Siguientes (Follow).

Se define FOLLOW(A), para el no terminal A, como el conjunto de terminales a que pueden aparecer inmediatamente a la derecha de A en alguna forma sentencial, es decir, el conjunto de terminales a tal que haya una derivacin de la forma S= => Aa para algn y . Si A puede ser el smbolo de ms a la derecha en alguna forma sentencial, entonces $ est en FOLLOW(A). Para calcular FOLLOW(A) para un smbolo no terminal A, se aplican las siguientes reglas hasta que no se pueda aadir nada ms al conjunto FOLLOW. 1. $ est en FOLLOW(S), siendo S el axioma de G. 2. Si existe una produccin A excepto , est en FOLLOW(B). B , entonces todo lo que est en FIRST( ),
*

3. Si existe la produccin A existe una produccin A FOLLOW(B).

B y FIRST( ) contiene (es decir, = => ), o bien si B, entonces todo lo que est en FOLLOW(A) est en

Construyendo la tabla de anlisis sintctico LL(1) La tabla se construye utilizando el algoritmo siguiente: Algoritmo:
I.S.C. Leonel Vzquez Aguirre

31

Para toda produccin A

a en la gramtica.

1. Si puede derivar una cadena de caracteres comenzando con a, es decir, para toda a en FIRST( ) Tabla [ A, a ] = A 2. Si puede derivar la cadena vaca, , entonces para toda b que puede seguir una cadena derivada de A, es decir, para toda b en FOLLOW(A), Tabla [ A, b ] = A Las entradas no definidas de la tabla se establecen como errores, y si cualquier entrada se define ms de una vez, entonces la gramtica no es LL(1). Algoritmo anlisis sintctico LL(1) El anlisis sintctico LL(1) es esencialmente un anlisis sintctico descendente recursivo controlado por la tabla . Algoritmo: 1. Introduzca EOF en el tope de la pila predictiva. 2. Introduzca el smbolo de Inicio en el tope de la pila predictiva. 3. Repita mientras la pila no est vaca: Caso el tope de la pila es: Smbolo terminal: Si el smbolo de entrada coincide con el tope de la pila, Entonces pida otro token, saque el tope de la pila, sino marque error. Smbolo No terminal: Utilice el no terminal y el smbolo de entrada actual para hallar la produccin correcta en la tabla. Saque el tope de la pila, inserte el lado derecho de la produccin en la pila, 1ero. El smbolo del extremo derecho (el smbolo del extremo izquierdo en el tope de la pila). Termina repite 4. Si la cadena de entrada ha terminado, entonces se acepta como vlida sintcticamente, sino se marca error de sintxis.

I.S.C. Leonel Vzquez Aguirre

32

Ejemplo: Dada la siguiente gramtica de Contexto Libre obtener primeros, siguientes, la matriz predictiva y hacer una corrida de escritorio.
1.

2.
3. 4.

5. 6. 7. 8.

E E E T T T F F

TE +TE FT *FT ID (E)

FIRST(conjunto de smbolos terminales con los cuales empieza una derivacin) F(E)= F(T)= { Id,( } F(T)= F(F)= { Id,( } F(E )= { ,+ } F(T ) ={ ,+ } F(F)={ Id,( } FOLLOWS(se encuentra inmediatamente ala derecha de una derivacin) Nota: dentro del siguiente no debe de haber vacio FO(E)= { $,) } FO(T)= { +, $, ) } FO(E )= { $, ) } FO(T ) ={ +, $, ) } FO(F)={ *,+, $, ) }

Tabla LL(1) (Predictiva) T/V E E T T F Id + 0 2 3 4 6 * ( 0 3 7 ) 1 4 $ 1 4

I.S.C. Leonel Vzquez Aguirre

33

Corrida de escritorio Pila predictiva $E $E` T $E` T` F $E` T` id $E` T` $E` $E` T+ $E` T $E` T` F $E` T` id $E` T` $E` T` F* $E` T` F $E` T` id $E` T` $E` $ entrada a+b*c$ A A A + + + B B B * * C C $ $ $ salida 0 E-->TE` 3 T-->FT` 6 F-->id 5 T` --> 2` -->+TE` 3 T-->FT` 6 F-->id 4 T` -->*FT` 6 F-->id 5 T` --> 1 E` -->

I.S.C. Leonel Vzquez Aguirre

34

La Tabla de Smbolos Estructura de la Tabla de Smbolos La tabla de smbolos es la estructura de datos usada por el compilador para asociar a cada smbolo del programa fuente (identificadores, constantes, etc...) un contenido semntico. Est formada por registros que en general tienen una longitud fija. Los campos que tienen estos registros varan dependiendo del lenguaje a compilar y de la estructura del compilador. En general, estos campos pueden contener la informacin misma para la que estn destinados, o bien un puntero a posiciones de memoria en las que se guarda esta informacin. Algunos de los campos que pueden definirse para los registros de la tabla de smbolos son: Nombre del smbolo. En la tabla de smbolo deben aparecer todos los lexemas correspondientes a los smbolos usados en el texto fuente, los smbolos sern todos aquellos trozos de cdigo que representen identificadores, constantes numricas, o cualquier otro elemento que el lenguaje sea capaz de manejar. Para el almacenamiento en la tabla de smbolos de los lexemas pueden utilizarse las tcnicas: Interna. El campo de la tabla de smbolos se define como de tipo cadena de caracteres de dimensin fija (por ejemplo 16). El inconveniente de esta tcnica es que la longitud mxima de los identificadores esta restringida al valor fijado Externa. El campo de la tabla de smbolos contiene un puntero a una zona de memoria en la que se almacena el lexema. Direccin en memoria. En este campo se almacenan las direcciones de memoria en la que se guardarn los valores de las variables correspondientes a cada smbolo, durante la ejecucin del programa. Normalmente el dato que interesa almacenar aqu no es la direccin absoluta, sino la relativa a una determinada zona de memoria de las que el programa usar en su ejecucin. Por ejemplo, la direccin relativa al comienzo de la memoria esttica, la direccin relativa al comienzo del registro de activacin correspondiente al procedimiento, etc... Es importante recordar que la tabla de smbolos no contiene los valores de las variables, sino simplemente la direccin en la que se almacenarn cuando el programa objeto se ejecute. Esto es obvio, ya que la tabla de smbolos es una estructura de datos que se utiliza en la fase de compilacin, y por lo general, desaparece una vez terminada la compilacin, no incorporndose al programa objeto. Por otra parte, slo durante la ejecucin del programa es cuando se utilizan los valores de las variables. Excepcionalmente, cuando queremos disponer de un depurador simblico durante la fase de ejecucin, no tenemos ms remedio que incorporar la tabla de smbolos al programa objeto. Esto ocurre tambin en los intrpretes, ya que compilacin 35
I.S.C. Leonel Vzquez Aguirre

y ejecucin se alternan. Tipos. En este campo se almacenar el cdigo correspondiente al tipo de datos representado por el smbolo, o bien un puntero a la estructura de datos correspondiente. Nmero de lnea de la declaracin o nmeros de lnea en los que se usa el smbolo. Esta es una informacin auxiliar que ser de utilidad para producir un listado de referencias cruzadas que ayuden a la depuracin de los programas. Puede utilizarse una lista encadenada de nmeros de lneas, siendo la cabeza de esta lista la lnea en donde aparece la declaracin del smbolo. Funcionamiento de la Tabla de Smbolos dentro del Compilador. La funcin de la tabla de smbolos dentro del compilador y su interaccin con los distintos mdulos que lo forman, puede representarse mediante el siguiente esquema:

La tabla de smbolos puede inicializarse con cierta informacin sobre smbolos especiales en el lenguaje, como son las palabras reservadas, las funciones de librera, las constantes predefinidas, etc. El analizador lxico va leyendo el programa fuente y construyendo lexemas, creando nuevas entradas en la tabla conforme va encontrando nuevos smbolos, para lo cual evidentemente ha de comprobar que el smbolo no figure ya en la tabla. El analizador sintctico trabaja solamente con la estructura formal subyacente, y a nivel de tokens ya formados en la fase anterior. El analizador sintctico sirve de soporte o armazn de un conjunto de rutinas de anlisis semntico. Parte de estas rutinas pueden completar la informacin de la tabla de smbolos sobre el tipo de un determinado lexema, etc. El generador de cdigo usa la informacin de la tabla para referenciar el cdigo a las posiciones adecuadas de memoria. Las siguientes fases del compilador y del cargador, en general, no necesitan usar la tabla de smbolos. 36

I.S.C. Leonel Vzquez Aguirre

Una vez ms conviene recordar que es el compilador el que usa la tabla de smbolos y no el programa compilado. La tabla de smbolos no est presente durante la ejecucin del programa (salvo en el caso de que se incluya un depurador simblico o en los intrpretes). Operaciones con la Tabla de Smbolos La tabla de smbolos funciona como una estructura de base de datos en la que el campo clave es el lexema correspondiente al smbolo. Sobre la estructura de datos "Tabla de smbolos" pueden hacerse por lo general las siguientes operaciones: Insertar: un registro nuevo y comprobar que no existe otro con el mismo nombre. Buscar: Localizar el contenido de la tabla de smbolos asociado a un determinado lexema. Modificar: la informacin contenida en un registro. En general, slo se aade informacin. La operacin de modificar siempre con lleva una operacin de bsqueda previa. En lenguajes con estructura de bloque se incluyen dos operaciones ms : Nuevo_Bloque: Inicio de un Nuevo Bloque. Fin_Bloque: Terminacin del mbito de un bloque. Las inserciones se realizan cuando se procesa la zona de declaracin de variables. En general, las operaciones ms frecuentes son las de consulta, que se realizan antes de cada insercin (para ver si esta declarado el smbolo), y cada vez que aparece el smbolo en el programa fuente. En las tablas de smbolos no suelen borrarse registros durante su funcionamiento, salvo en los lenguajes con estructura de bloques, en los que al finalizar el anlisis de un bloque deben desalojarse de la tabla todos los smbolos locales al mismo. Organizacin de la Tabla de Smbolos La organizacin de la tabla de smbolos es un problema convencional de programacin y algunos de los mtodos que pueden emplearse son: Tablas no ordenadas. (Arrays, Listas, etc.) En general, son mtodos muy poco eficientes, pero fciles de programar. Tablas ordenadas. (Arrays ordenados, Listas ordenadas, rboles binarios, rboles AVL, Tablas Hash, etc.). Debido a las caractersticas especficas de funcionamiento de las tablas de smbolos, los mtodos ms eficientes son los rboles AVL y las tablas Hash. Sin embargo, el manejo de las tablas de smbolos en los lenguajes con estructura 37

I.S.C. Leonel Vzquez Aguirre

de bloques tienen unas caractersticas propias, y sugieren el empleo de tcnicas especficas de organizacin para hacer ms eficaces las operaciones de bsqueda y desalojo de registros una vez finalizada la compilacin de un bloque. Un lenguaje con estructura de bloques es aquel que est compuesto por mdulos o trozos de cdigo cuya ejecucin es secuencial. Estos mdulos a su vez pueden contener en su secuencia de instrucciones llamadas a otros mdulos, que a su vez pueden llamar a otros submdulos y as sucesivamente. En general, un smbolo declarado en un mdulo es accesible dentro del mdulo en el que est definido y en todos los mdulos o submdulos que contiene. Si uno de estos submdulos define un smbolo con el mismo lexema, esta definicin se superpone a la existente anteriormente, asociando el lexema a la definicin ms reciente. Al conjunto de reglas semnticas que permiten deducir que definicin corresponde a cada uno de los lexemas se denomina reglas de mbito, y son una caracterstica propia de cada lenguaje. Anlisis Semntico Introduccin Los programas que se compilan no son solamente cadenas de smbolos sin ningn significado que pueden ser aceptadas como correctas o no por una mquina abstracta. El lenguaje no es ms que el vehculo por el cual se intenta transmitir una serie de instrucciones a un procesador para que ste las ejecute produciendo unos resultados. Por ello, la tarea del compilador requiere la extraccin del contenido semntico incluido en las distintas sentencias del programa. Simultneamente existen ciertos aspectos relativos a la correccin de un programa que no se pueden expresar claramente mediante el lenguaje de programacin, y otros para los que resultara muy engorroso hacerlo. Por todo ello, se hace necesario dotar al compilador de una serie de rutinas auxiliares que permitan captar todo aquello que no se ha expresado mediante la sintaxis del lenguaje y todo aquello que hace descender a nuestro lenguaje de programacin de las alturas de una mquina abstracta hasta el nivel de una computadora real. A todas estas rutinas auxiliares se las denomina genricamente anlisis semntico. El anlisis semntico, a diferencia de otras fases, no se realiza claramente diferenciado del resto de las tareas que lleva a cabo el compilador, ms bien podra decirse que el anlisis semntico completa las dos fases anteriores de anlisis lxico y sintctico incorporando ciertas comprobaciones que no pueden asimilarse al mero reconocimiento de una cadena dentro de un lenguaje. Desde el punto de vista del compilador, las rutinas de anlisis semntico pueden ser de dos tipos:

Estticas: Son aquellas que se realizan durante la compilacin del programa. Dinmicas: Son aquellas que el compilador incorpora al programa traducido. Para ello, el compilador debe aadir en el cdigo objeto ciertas instrucciones que se ejecutarn simultneamente a la ejecucin del programa. 38

I.S.C. Leonel Vzquez Aguirre

Entre las comprobaciones de tipo esttico podemos destacar las comprobaciones de tipos, que se estudian a continuacin con mayor detenimiento. Con ellas se pretende garantizar que los dominios de todas las variables que se emplean en un programa son los adecuados en cada caso, o de no serlo, realizar las oportunas modificaciones para garantizar la compatibilidad. Otras comprobaciones estticas son las de unicidad. Muchos lenguajes de programacin exigen que los identificadores que utilizan sean declarados previamente de forma unvoca. Sin embargo, aunque parece trivial, no est incluido en la sintaxis de contexto libre de un lenguaje el que una variable determinada se defina solamente una vez. Por tanto, se debe incluir esta comprobacin entre las rutinas semnticas. Por lo general esta tarea se realiza junto con la construccin de la tabla de smbolos que se estudiar ms adelante. Otros tipos de comprobaciones llevadas a cabo durante la fase de compilacin seran las referentes al control de flujo en determinados lenguajes, as por ejemplo en BASIC debemos asegurarnos de que las sentencias FOR y NEXT estn debidamente anidadas. Tngase en cuenta que este lenguaje no tiene estructura de bloques y que sintcticamente podran quedar sentencias FOR sin su correspondiente sentencia NEXT o viceversa. Entre las comprobaciones dinmicas, las ms evidentes son aquellas que dependen del estado de la mquina en el momento de la ejecucin del programa, como por ejemplo, los fallos en la apertura de ficheros, errores de dispositivos, etc...; pero tambin hay otras que dependen del propio programa como son los errores numricos, divisiones por cero, desbordamiento numrico (overflow), etc., o los errores de direccionamiento de memoria como pudieran ser el desbordamiento de la pila (stack) o del montn (heap) y los errores por exceso o defecto en el acceso a una variable indexada. En algunos casos podra pensarse que la comprobacin puede realizarse en tiempo de compilacin y de hecho, en algunos casos as es. Sin embargo, en el caso general, hay ciertas comprobaciones que deben hacerse necesariamente durante la ejecucin del programa. Por ejemplo, si el ndice de una matriz no se corresponde con una constante, sino con una variable o una expresin, ser necesario evaluar esta expresin para saber si ha habido desbordamiento. Esta evaluacin slo se efecta en tiempo de ejecucin, ya que, el valor que tomarn las variables, es desconocido durante la compilacin. Sistemas de Tipos: Expresiones de Tipos Se entiende por sistema de tipos de un lenguaje de programacin todas aquellas reglas que permiten asignar tipos a las distintas partes de un programa y verificar su correccin. En concreto formarn el sistema de tipos las definiciones y reglas que permiten comprobar cul es el dominio asignado a una variable, y en qu contextos puede ser usada. Cada lenguaje tiene un sistema de tipos propio, aunque a veces puede variar de una a otra implementacin. Un sistema de tipos incluye tanto comprobaciones estticas como dinmicas, segn si se realizan en tiempo de compilacin o en tiempo de ejecucin. Como es lgico, mientras ms comprobaciones puedan realizarse en la fase de compilacin, menos tendrn que realizarse durante la ejecucin, con la consiguiente mejora en la eficiencia del programa objeto. En la prctica ningn lenguaje es tan
I.S.C. Leonel Vzquez Aguirre

39

fuertemente estructurado que permita una completa comprobacin esttica de tipos. Para abordar el sistema de tipos de un lenguaje de programacin imperativo del estilo de C, PASCAL, MODULA, ADA etc... se define una estructura asociada a cada segmento de cdigo de un lenguaje a la que se denomina expresin de tipo. Cada lenguaje de programacin requerir unas expresiones de tipos adecuadas a sus caractersticas. A continuacin, a modo de ejemplo, se definen las expresiones de tipos ms comunes usadas en un lenguaje imperativo similar a C o PASCAL:

Tipos simples: Son expresiones de tipos los tipos simples del lenguaje, y algunos tipos especiales: integer real char boolean void error Los cuatro primeros son los tipos de datos simples ms comunes en los lenguajes de programacin, los dos ltimos son tipos simples especiales que usaremos para su atribucin a diversas partes de un programa, a fin de homogenizar el tratamiento de todo el conjunto mediante el mtodo de las expresiones de tipos. Tomando el lenguaje C como ejemplo, el segmento de cdigo al que est asociada la expresin de tipos integer es aquella en que aparece la palabra reservada int, etc..

Identificadores: Son expresiones de tipos los identificadores de tipos definidos en el propio programa, es decir los identificadores correspondientes a los tipos definidos por el usuario. Por ejemplo supongamos que en PASCAL definimos el tipo : type caracter = char; entonces, se puede considerar que el identificador caracter es una expresin de tipos, al igual que lo son los tipos simples definidos en el punto anterior. En principio, no todos los sistemas de tipos incluyen identificadores de tipos, como se ver, esto depende de si existen o no tipos definidos por el usuario y del criterio de equivalencia de tipos que se fije en el lenguaje.

Constructores de tipos: Permiten formar tipos complejos a partir de otros ms simples. La semntica de cada lenguaje tiene asociada unos constructores de tipos propios. En general, en los lenguajes de programacin se definen los siguientes constructores:
o

I.S.C. Leonel Vzquez Aguirre

Matrices: Si T es una expresin de tipos, entonces array(R,T) es tambin 40

una expresin de tipos que representa a una matriz de rango R de elementos de tipo T. Ejemplos: Sea el segmento de cdigo C : char a[10] o el segmento de cdigo PASCAL : a:array[0..9] of char A ambos segmentos de cdigo debe asignrseles la expresin de tipos: array(0-9,char)

Productos: Sea T1 y T2 expresiones de tipos, T1 x T2 es una expresin de tipos que representa al producto cartesiano de los tipos T1 y T2. A fin de simplificar consideraremos que el constructor u operador de tipos x es asociativo por la izquierda. Ms adelante se vern ejemplos de esta definicin. Registros: Sea un registro formado por los campos u1, u2 ... uN, siendo cada uno de ellos de los tipos T1,T2 ... TN, entonces, la expresin de tipos asociada al conjunto es: record ( (u1:T1) x (u2:T2) x ... x (uN:TN) ) Ejemplos: Sea el segmento de cdigo C: struct { }

char nombre[30]; float dni;

O el segmento de cdigo PASCAL : record nombre:array[0.29] of char; dni:real; end; en ambos casos se asigna a estos segmentos de cdigo la expresin de tipos: record ( (nombre:array(0-9,char)) x (dni:real) )
o

I.S.C. Leonel Vzquez Aguirre

Punteros: Si T es una expresin de tipos, entonces pointer(T) es una expresin de tipos que representa a un puntero a una posicin de memoria ocupada por un dato de tipo T. 41

Ejemplo: Sean los segmentos de cdigo C y PASCAL char *p; p:^char

en ambos casos se les asigna la expresin de tipos: pointer(char)


o

Funciones: Sean T1,T2 ... TN, las expresiones de tipos asociadas a los segmentos de cdigo correspondientes a los argumentos de una funcin, y sea T el tipo devuelto por la funcin. Entonces, la expresin de tipos asociada a la funcin es: ((T1xT2 x... xTN) -> T ) Por simplicidad, supondremos en lo sucesivo que el constructor u operador de tipos x tiene mayor prioridad que el constructor ->.

La anterior lista de constructores de tipos no es exhaustiva, segn el lenguaje se definen los constructores de tipos necesarios. Por ejemplo, pudiera ser que un lenguaje incluyera un constructor de tipo para definir pilas (de elementos de tipo T), o ficheros (de registros de tipo T), etc. Variables: En algunos casos, podramos considerar tambin la existencia de variables dentro de las expresiones de tipos, es decir expresiones de tipos que funcionan como variables que toman un valor dentro del dominio de las expresiones de tipos definidas en los puntos 1-3. Esto es de utilidad al tratar el polimorfismo. Sistemas de Tipos: Equivalencia de Tipos Una caracterstica importante del sistema de tipos de un lenguaje de programacin es sin duda el conjunto de reglas que permiten decir que dos tipos son iguales. Es sta una caracterstica que queda en muchos casos sin definir en las especificaciones del lenguaje, dando por ello lugar a diferentes interpretaciones por parte de los realizadores del compilador. Esto se traduce en la prctica en diferencias que afectan a la compatibilidad entre diferentes dialectos de un mismo lenguaje. Existen principalmente tres modalidades de equivalencia entre tipos: 1.Equivalencia estructural: Dos tipos son estructuralmente equivalentes si y slo si son el mismo tipo bsico, o estn formados por la aplicacin de un mismo constructor a tipos estructuralmente equivalentes. Ejemplo: Supongamos que en un programa PASCAL tenemos las siguientes definiciones: type tipovector = array [0..9] of integer; tipomatriz = array [0..9] of array [0..9] of integer;
I.S.C. Leonel Vzquez Aguirre

42

var vector : array [0..9] of integer; arreglo : array [0..9,0..9] of integer; matriz : tipomatriz; vecvec1 : array [0..9] of tipovector; vecvec2 : array [0..9] of tipovector; Los tipos asociados a las variables arreglo, matriz, vecvec1 y vecvec2 son estructuralmente equivalentes, ya que, su expresin de tipos es en todos los casos: array(0-9,array(0-9,int)) Sin embargo, la variable vector, no es estructuralmente equivalente a las anteriores, porque la expresin de tipos que le corresponde es: array(0-9,int) 2.Equivalencia nominal: Se dice que dos tipos son nominalmente equivalentes cuando son estructuralmente equivalentes considerando como tipos bsicos e indivisibles a los identificadores de tipos. Es decir, la equivalencia nominal funciona igual que la equivalencia estructural siempre y cuando no existan tipos definidos por el programador, o bien, si interpretamos que en este caso los tipos del usuario pasan a formar parte del repertorio de tipos bsicos. Ejemplo: En el ejemplo anterior los tipos correspondientes a las variables vecvec1 y vecvec2 son nominalmente equivalentes, porque en ambos casos podemos considerar la expresin de tipos: array(0-9,tipovector) Sin embargo, la expresin de tipos de la variable arreglo es: array(0-9,array(0-9,int)) y, por tanto, no puede considerarse nominalmente equivalente a las anteriores. 3.Equivalencia funcional: Dos tipos se consideran equivalentes funcionalmente cuando pueden emplearse indistintamente en un mismo contexto. Ejemplo: Supongamos el programa en lenguaje C : int mat_chica[10], mat_grande[40]; void ordena (mat,n) int mat[],n; { ..... }
I.S.C. Leonel Vzquez Aguirre

43

ordena (mat_chica,10); ordena (mat_grande,40); En este contexto los tipos de las variables mat_chica y mat_grande son funcionalmente equivalentes. El sistema de tipos del lenguaje C no es tan estricto como el de PASCAL o MODULA, ello se debe en parte a la utilizacin de la equivalencia funcional y a las conversiones automticas de tipos que emplea. No debe confundirse la equivalencia funcional con la compatibilidad de tipos. Existe una diferencia sutil entre dos tipos compatibles y dos tipos funcionalmente equivalentes. En el primer caso, el compilador realiza sobre uno de ellos una transformacin interna para que ambos se transformen en tipos equivalentes; en el caso de la equivalencia funcional no se realiza ninguna modificacin interna, sino que se relaja el criterio de equivalencia. Sistemas de Tipos: Representacin de una Expresin de Tipos Si queremos analizar los diferentes tipos que intervienen dentro de un programa, ser necesario que el compilador cuente con alguna estructura interna que le permita manejar cmodamente las expresiones de tipos. La representacin interna que el compilador tiene de la expresin de tipos asociada a un determinado trozo de cdigo debe ser fcilmente manipulable, ya que su creacin se realizar conforme se hace la lectura del programa fuente. Por otra parte, debe permitir comparar fcilmente las expresiones asignadas a distintos trozos de cdigo, especialmente a los identificadores de variables, esta comparacin depender de la modalidad de equivalencia que se aplique, por lo que la representacin que se elija tambin debe ser acorde a esta caracterstica. Las representaciones internas ms habituales son las siguientes:

Codificadas: Se usan cuando las expresiones de tipos son sencillas. Consisten en asignar un nmero natural a cada una de las expresiones de tipos que puedan construirse en el lenguaje. Esta asignacin debe hacerse mediante una funcin inyectiva a fin de que sirva como comprobacin de equivalencia. Por ejemplo, supongamos un lenguaje en el que se han definido los tipos simples: char, integer, y real, no se consideran identificadores de tipos y los nicos constructores de tipos que se utilizan son: pointer(T) puntero a una variable de array(T) matriz uni- o pluridimensional a elementos function(T) funcin que devuelve un valor de tipo T. Se asignan los siguientes cdigos: char --------- 1 integer ------ 2 real --------- 3 pointer ------ 4 array -------- 5 tipo de tipo T. T.

I.S.C. Leonel Vzquez Aguirre

44

function ----- 6 Todas las expresiones de tipos pueden codificarse mediante el siguiente algoritmo recursivo:
o

Si una expresin de tipos corresponde a un tipo simple se le asigna el cdigo correspondiente al tipo simple. Si una expresin de tipos es compuesta, estar formada por un constructor aplicado a un tipo T. Este tipo T estar representado por un nmero natural N. El nmero natural que se asigna a la expresin de tipos compuesta se halla mediante la frmula: 4*N + nmero asignado al constructor de tipos

Es fcil demostrar que la funcin as construida es inyectiva, es decir, a cada expresin de tipos le asigna un nico nmero natural, y por consiguiente para saber si dos expresiones de tipos son iguales basta comparar los cdigos correspondientes.

rboles: Internamente el compilador puede emplear estructuras de datos de tipo arborescente para representar las estructuras de tipos. En general, basta con emplear rboles binarios, pero esto depende del sistema de tipos que se haya definido en el lenguaje. En las representaciones mediante rboles los tipos compuestos dan lugar a la aparicin de un nodo en el que se sita el constructor de tipos, (es decir el operando), y una o ms ramas que apuntan al rbol que contiene las estructuras de tipos de los operadores de la expresin. Ejemplo: Para el sistema de tipos genrico definido en la primer parte, la expresin de tipos: (real x (char x char) --> pointer(integer)) puede representarse mediante el rbol:

I.S.C. Leonel Vzquez Aguirre

45

Al disear el compilador debe tenerse en cuenta el modo de equivalencia de tipos que se desea, as como la estructura de datos que debe contener la expresin de tipos. Una opcin consiste en almacenar en un campo de la tabla de smbolos una estructura de datos distinta para cada elemento del programa. Otra alternativa consiste en almacenar una macro-estructura de forma arborescente comn a todos los smbolos del programa y a la que apunta el campo correspondiente a cada uno de los smbolos de la tabla de smbolos del compilador. Ejemplo: Para el programa PASCAL del ejemplo del epgrafe anterior, supuesto que se define un sistema de tipos con equivalencia estructural y sin comprobacin de rangos de las matrices, se obtiene la siguiente representacin compacta de las estructuras de tipos de las variables que intervienen en el programa:

I.S.C. Leonel Vzquez Aguirre

46

El mismo ejemplo en el caso de que el sistema requiera equivalencia nominal de tipos puede utilizar la siguiente estructura de datos:

En cualquier caso para comprobar que dos tipos son iguales basta verificar que es igual la direccin a la que apuntan los punteros del campo de la tabla de smbolos correspondiente al tipo.

DAGS: Son una forma abreviada de representar los rboles. Se obtienen cuando varias ramas de un rbol sealan a la misma subestructura. Todo lo dicho para representaciones arbreas es vlido para representaciones mediante DAGS. La principal ventaja que presentan es que son representaciones ms compactas, por tanto, ocupan menos memoria y la comprobacin de equivalencia se efecta con mayor rapidez. Por contra, su construccin es algo ms complicada. Ejemplo: La expresin de tipos del ejemplo del apartado anterior puede representarse mediante el siguiente DAG:

I.S.C. Leonel Vzquez Aguirre

47

Generacin de Cdigo Intermedio Habitualmente los compiladores generan un cdigo intermedio entre el cdigo fuente y el lenguaje mquina propiamente dicho. Este cdigo puede considerarse como el lenguaje de una mquina abstracta que el diseador del compilador define. El cdigo intermedio slo requiere para ser considerado como tal, que pueda ser traducido fcilmente al cdigo de la mquina real. La definicin de este cdigo intermedio se hace de forma que tenga las siguientes ventajas:

Evitar las limitaciones propias de una mquina real en cuanto a nmero de registros, alineacin de datos, etc... Facilitar la optimizacin de cdigo al ser ms flexible en cuanto a direccionamiento del cdigo de la mquina real. Facilitar la portabilidad del compilador. En general, cuando se requiere que un compilador de un lenguaje funcione sobre mquinas diferentes, la alternativa a construir un compilador distinto para cada mquina consiste en traducir el cdigo objeto a un cdigo intermedio, independiente de cualquier mquina y a continuacin escribir un traductor de cdigo intermedio a cdigo mquina para cada una de las mquinas que se desee. A la primera parte de este proceso se le denomina parte frontal del compilador (front end), mientras que la segunda es la parte posterior del compilador (back end). Evidentemente la ventaja de este proceso es que la parte posterior es ms sencilla de realizar que la parte frontal y, por consiguiente, el trabajo total a realizar es menor.

El inconveniente principal que puede achacarse a la generacin de cdigo intermedio es que puede producir programas menos eficientes al no aprovechar algunas de las instrucciones especficas de la mquina objeto. Este inconveniente puede soslayarse si existe una adecuada optimizacin de cdigo mquina propiamente dicho. Tipos y Representaciones del Cdigo Intermedio Existen muchas formas de cdigo intermedio, de hecho, el diseador del compilador puede definir la mquina abstracta que considere ms adecuada al lenguaje fuente o a la clase de mquinas a las que va destinado. Las representaciones que ms se emplean son:

rboles semnticos y grafos acclicos dirigidos Cdigo de tres direcciones. Cudruplos, Tercetos o Tercetos indirectos.

Existen algunas mquinas abstractas o lenguajes intermedios ya clsicos en generacin de cdigo que se han definido para la compilacin de lenguajes concretos, como el cdigo P, que se utiliza en la compilacin de Pascal, o la mquina de Warren para la compilacin del lenguaje Prolog.

I.S.C. Leonel Vzquez Aguirre

48

rboles semnticos y grafos acclicos dirigidos Una forma de representar el cdigo generado por un compilador es mediante una estructura de tipos arborescente, a la que se denomina rbol semntico a fin de diferenciarlo del rbol sintctico o rbol del anlisis sintctico que representa la estructura gramatical.

As, por ejemplo, para la representacin de la sentencia: a:=b*-c+b*-c, se tiene: En el rbol semntico los nodos son ocupados por operadores y sus operandos se obtienen evaluando las operaciones de sus nodos descendientes. Pueden usarse grafos acclicos dirigidos (DAG) para obtener una representacin condensada de los rboles semnticos. Cuando se encuentran subestructuras idnticas dentro de un mismo rbol basta usar mltiples referencias a la misma subestructura, sin necesidad de duplicar la subestructura repetida. Esta representacin consigue por si misma una mejora considerable en el tamao y la eficiencia del cdigo generado. Cdigo de tres direcciones La representacin mediante cdigo de tres direcciones se basa en un conjunto de instrucciones capaces de manejar un mximo de tres direcciones de memoria. En general dos de estas direcciones corresponden a los argumentos y la tercera al resultado. Una instruccin de tres direcciones por ejemplo puede consistir en sumar el contenido de dos direcciones y situar el resultado en una tercera direccin, o saltar a una determinada direccin si el valor contenido en una direccin es mayor que el contenido 49
I.S.C. Leonel Vzquez Aguirre

en otra, etc. instruccin +:= if.>.goto arg1 x a arg2 y b resultado z L

Fig 2. Ejemplos de cdigo de tres direcciones Usando un adecuado conjunto de instrucciones de este tipo, cualquier sentencia o conjunto de sentencias del cdigo fuente, puede traducirse por una secuencia de instrucciones de tres direcciones. Por ejemplo, la sentencia a:=b*(c+d) puede traducirse como la secuencia de instrucciones de tres direcciones: instr. (100) +:= (101) *:= arg1 c b arg2 d t1 rest. t1 a

En donde las letras minsculas representan las direcciones de memoria asociadas a cada una de las variables del programa, y t1 representa una direccin de memoria temporal (auxiliar) que permite realizar los clculos. Para mayor comodidad se escribe el cdigo auxiliar con la notacin infija: (100) t1 := c + d (101) a := b * t1 Esta representacin es equivalente a la representacin mediante rbol semntico o mediante grafo acclico dirigido que se ha visto anteriormente, siempre que se limite la ramificacin del rbol a un mximo de dos descendientes. Para obtener la representacin en cdigo de tres direcciones basta con recorrer el rbol ascendentemente de izquierda a derecha y generar una variable temporal para cada nodo intermedio del rbol, resumiendo mediante ella la estructura descendiente. As, por ejemplo, a partir de los grafos de la figura 1b y 1c, se obtienen las siguientes secuencias de cdigo intermedio: (100) (101) (102) (103) (104) (105) t1 := - c t2 := b * t1 t3 := - c t4 := b * t3 t5 := t2 + t4 a := t5 (100) (101) (102) (103) t1 := - c t2 := b * t1 t3 := t2 + t2 a := t3

Fig 3a. Cdigo corresp. fig. 1b

Fig 3b. Cdigo corresp. fig. 1c

I.S.C. Leonel Vzquez Aguirre

50

El conjunto de instrucciones de tres direcciones del cdigo intermedio debe definirse adecuadamente segn el lenguaje a compilar, y teniendo en cuenta que cada instruccin del mismo debe convertirse fcilmente en una o varias instrucciones del cdigo mquina. No cabe por tanto definir un lenguaje intermedio universal, pero a ttulo de ejemplo se va a estudiar el tipo de instrucciones que pueden usarse:

Instrucciones de asignacin: directa: de un operando: de dos operandos: a := b a := opunario b a := b opbinario c

Saltos en ejecucin: incondicionales: condicionales: etiquetas: goto L if a opcond b then goto L label L

Definicin de parmetros y llamada a procedimientos: definicin de parmetros: llamada: param X call p, n

Por lo general se usan conjuntamente varias instrucciones de tres direcciones para realizar la llamada a un procedimiento o funcin. Primero se definen las variables que van a usarse como parmetros y luego se transfiere el control mediante una instruccin de llamada, indicando la direccin del procedimiento y el nmero de parmetros.

Operaciones de direccionamiento indirecto: en el argumento: en el resultado: x := y [i] x [i] := y

Operaciones con punteros: asignacin de direccin: asignacin de valor: asignacin indirecta: x := & y x := * y * x := y

I.S.C. Leonel Vzquez Aguirre

La representacin que se ha utilizado hasta el momento se llama de cuartetos o cudruplos, ya que utiliza cuatro campos para almacenar cada instruccin. Existe otra tcnica que permite representar el cdigo de tres direcciones mediante solo tres campos. Para ello se mantiene la secuencia de instrucciones de cdigo intermedio en un vector. Ya que toda instruccin lleva aparejada una direccin como resultado, cuando sea necesario aludir a la direccin del resultado se har mediante el nmero correspondiente a la posicin del vector en la que se calcul. Por tanto, esta representacin slo almacena los campos correspondientes a la instruccin y a los dos argumentos y se llama de tercetos. As, representando mediante tercetos el cdigo del ejemplo anterior se obtiene: 51

instr. arg1 (100) --:= c (101) *:= b (102) --:= c (103) *:= b (104) +:= (101) (105) := a

arg2 (100) (102) (103) (104)

El uso de tercetos tiene una evidente ventaja sobre los cuartetos, ya que no es necesario generar un sinfn de variables temporales. Adems es ms compacta y por tanto ocupa menos espacio en memoria. Por contra, la representacin mediante tercetos es ms complicada y puede dificultar la optimizacin de cdigo, ya que cualquier variacin en la secuencia de instrucciones obliga a un reposicionamiento de las referencias. Este problema puede solventarse usando la representacin de tercetos indirectos, que consiste en utilizar un vector auxiliar para almacenar la secuencia de instrucciones. Por ejemplo, el cdigo anterior se representara mediante dos vectores, el primero de ellos contiene la secuencia de instrucciones y el segundo las instrucciones: referencia direccin (1) (100) (2) (101) (3) (102) (4) (103) (5) (104) (6) (105) (100) (101) (102) (103) (104) (105) instr. arg1 --:= c *:= b --:= c *:= b +:= (101) := a arg2 (100) (102) (103) (104)

De esta forma, para eliminar el cdigo duplicado en la fase de optimizacin basta con eliminar estas instrucciones de la tabla de referencias y modificar solamente la instruccin que se optimiza: referencia direccin (1) (100) (2) (101) (3) (104) (4) (105) (100) (101) (104) (105) instr. arg1 --:= c *:= b +:= (101) := a arg2 (100) (101) (104)

Otros tipos de codigo intermedio Notacin polaca

Este tipo de cdigo intermedio sirve para pasar de una notacin de infijo a una notacin de postfijo. A:=b+c*d Esta instruccin convertida a notacin polaca queda: A b c d * + :=
I.S.C. Leonel Vzquez Aguirre

52

Algoritmo de ejecucin: I=1 Repetir

Si vector_polaco[i]=variable entonces Push pila_de_ejecucion(variable) Si no Si vector_polaco[1]=operador entonces Pop pila_de_ejecucion elemento_1 Pop pila_de_ejecucion elemento_2 Push pila_de_ejecucion(elemento_1 operador elemento_2) Si no .(*diversas operaciones ajenas a expresiones*) I=i+1 Hasta fin de vector_polaco Triplos

Este tipo de cdigo utiliza instrucciones con un formato de tres campos Codigo de operacin Operando1 Operando2

A:=b + c * d Esta instruccin convertida a triplos queda: * c d + b := a Algoritmo de ejecucin: Para cada triplo hacer Si esta explicito el operando_1 y el operando_2 entonces Push pila_de_ejecucion(operando_1 operador operando_2) Si solo esta explicito el operando_1 entonces Push pila_de_ejecucion(pop pila_de_ejecucion operador operando_1) .(*diversas operaciones*) Si no esta explicito ningn operando entonces Push pila_de_ejecucion (pop pila_de_ejecucion operador pop pila_de_ejecucion) Cudruplos

Este tipo de cdigo utiliza instrucciones con un formato de cuatro campos Cdigo operacin de Operador 1 Operador 2 Resultado 53

I.S.C. Leonel Vzquez Aguirre

A := b + c * d Esta instruccin convertida a cudruplos queda: * c d t1 + b t1 t2 := t2 a Donde t1 y t2 son direcciones temporales Para cada cudruplo hacer Si el operador es binario entonces Resultado := operando 1 operador operando 2 ..(*diversas operaciones*) Cdigo p

La maquina p es una maquina emulada por software ideada por niklaus wirth (creador de pascal) y preparada para ejecutar cdigo p. El formato de las instrucciones en p es el siguiente: Cdigo de operacin Operando

Si se omite el operando, el cdigo de operacin hace referencia al top y al top-1 de la pila de ejecucin. A := b + c * d Esta instruccin convertida a cdigo p queda: Carga b Carga c Carga d Multiplica Suma Almacena a Comparacin de los diversos mtodos expuestos Los mtodos se ordenan de menor a mayor por: En cuanto a la cantidad de memoria que requieren para su almacenamiento: Notacin polaca Cdigo p Triplos Cudruplos

En cuanto a la velocidad de su ejecucin : 54

I.S.C. Leonel Vzquez Aguirre

Cudruplos Triplos, cdigo p Notacin polaca

Si se desea convertir el cdigo intermedio a cdigo objeto:


Cudruplos Triplos, cdigo p Notacin polaca

El lector debe efectuar la decisin sobre el tipo de cdigo que le conviene generar. Generacin de cdigo intermedio para expresiones aritmticas Aunque sintcticamente, es totalmente equivalente colocar todos los operadores en un mismo nivel o jerarqua, dado que la generacin de cdigo intermedio ser controlada por el anlisis sintctico, entonces nos convendr separar los diagramas y as mismo la gramtica, por prioridad de los operadores. Evaluacin de una expresin.- una expresin puede ser evaluada de derecha a izquierda, o de izquierda a derecha, a esto se le llama asociativa derecha e izquierda, respectivamente. Asociatividad derecha: 3+2+5 3+ 7 10 Este tipo de asociatividad no es vlida ni para resta ni para la divisin. Asociatividad izquierda: 3+2+5 5+5 10

Este tipo de asociatividad es vlida para todas las operaciones.

I.S.C. Leonel Vzquez Aguirre

55

Acciones de generacin de cdigo

1.-escribir en el vector polaco, la direccin de la variable generada por el lxico 2.-push pila_de_operadores (+) 3.- push pila_de_operadores (*) 4.-si el tope de la pila de operadores = + entonces vector polaco (direccin actual ) = pop pila_de_operadores 5.- si el tope de la pila de operadores = * entonces vector polaco (direccin actual ) = pop pila_de_operadores 6.-push pila_de_operadores (marca de fondo falso) 7.-pop pila_de operadores se quita la marca de fondo falso. Acciones de generacin de cdigo en la gramtica Si convertimos directamente los diagramas de sintaxis del subconjunto de expresiones aritmticas a una gramtica se obtiene: E E T T F F T+E T F*T F id (E)

Si el mtodo de reconocimiento sintctico que vamos a seguir es un mtodo topdown, entonces la gramtica se deber transformar y quedara de la siguiente manera: E E E T T T F F T E +TE FT *FT id (E)

Colocando las acciones de generacin de cdigo sobre esta gramtica obtendremos: E E E T


I.S.C. Leonel Vzquez Aguirre

T E +{accin 2}T{accin 4}E FT 56

T T F F

*{accin 3}F{accin 5}T id{accin 1} ( {accin 6}E ){accin 7}

1.-escribir en el vector polaco, la direccin de la variable generada por el lxico 2.-push pila_de_operadores (+) 3.- push pila_de_operadores (*) 4.-si el tope de la pila de operadores = + entonces vector polaco (direccin actual ) = pop pila_de_operadores 5.- si el tope de la pila de operadores = * entonces vector polaco (direccin actual ) = pop pila_de_operadores 6.-push pila_de_operadores (marca de fondo falso) 7.-pop pila_de operadores se quita la marca de fondo falso. Generacin de cudruplos En esta, solo se utiliza la asociatividad izquierda, ya que es la nica valida para todas las operaciones. Para generar cudruplos se requiere: Pila de operadores Pila de operandos Avail de direcciones temporales Espacio para almacenar los cudruplos generados

Acciones de generacin de cdigo para expresiones completas, incluyendo operadores booleanos

I.S.C. Leonel Vzquez Aguirre

57

1.-push pila_de_operandos(direccin de la variable) 2.-push pila de operadores (operador) 3.-push pila de operadores (operador) 4.-si el top de la pila de operadores = + , or entonces generar cudruplo : operador operando_1 operando_2 resultado donde: operando_2=pop pila_de_operandos operando_1=pop pila_de_operandos resultado=temporal obtenido del avail 5.- si el top de la pila de operadores = *, and entonces generar cudruplo : operador operando_1 operando_2 resultado donde: operando_2=pop pila_de_operandos operando_1=pop pila_de_operandos resultado=temporal obtenido del avail si alguno de los operandos corresponda a un temporal entonces regresarlo al avail push pila-de_operandos(resultado) pop pila_de_operadores. 6.-push pila_de_operadores (marca de fondo falso) 7.-pop pila_de_operadores..se quita la marca de fondo falso 8.-push pila_de_operadores(operador) 9.-generar cudruplo: generar cudruplo : operador operando_1 operando_2 resultado donde: operando_2=pop pila_de_operandos operando_1=pop pila_de_operandos resultado=temporal obtenido del avail si alguno de los operandos corresponda a un temporal entonces regresarlo al avail push pila-de_operandos(resultado) pop pila_de_operadores.

I.S.C. Leonel Vzquez Aguirre

58

Anlisis semntico Para efectuarlo se revisara la combinacin de tipos a los que pertenezcan las variables y constantes que acten como operandos. Para incluir los tipos de las variables en la tabla de smbolos podemos efectuar las siguientes acciones:

1. Push pila-de-operandos (direccin de la variable) 2. Poner el tipo a todas las variables que se metieron a la pila de operandos, y sacarlas de la pila. Reglas semnticas Utilizaremos como referencia las reglas semnticas de las expresiones en pascal, para esto construiremos una tabla donde: E= entero s= string Op1 E E R R C C S S B Op2 E R E R C S C S B *,+,E R R R X X X X X r= real b= booleano / R R R R X X X X X Div mod E X X X X X X X X c= carcter x= error semantico Relac B B B B B B B B B And or X X X X X X X X B

En esta tabla se omitieron un conjunto de combinaciones que con cualquier operacin produce error, ej. Entero con string. Ya que un traductor es una autmata, es conveniente que el anlisis semntico tambin se automatice. Las claves para automatizar el anlisis semntico son: Utilizar acciones para la verificacin semntica Escoger una estructura de datos que permita accesarla directamente 59

I.S.C. Leonel Vzquez Aguirre

Para el anlisis semntico no se recomienda usar la tabla mostrada ya que su acceso no es automtico y le faltan muchas combinaciones. Acciones para verificacin semntica Utilizaremos como ejemplo un subconjunto de expresiones aritmticas. Estas acciones se debern de aadir a las acciones de generacin de cdigo, y no modifican nada de lo ya visto en generacin de cdigo. Para realizar la verificacin semntica se requerir una pila de tipos.

1. push pila-de-tipos(tipo de la variable) 2. , 3. No llevan accin semntica 4. Si tipos del top y top-1 de la pila de tipos son permitidos en la operacin a generar entonces Pop pila-de-tipos; pop pila-de-tipos Push pila-de-tipos (resultado de la operacin) si no marcar error semntico, y aplicar accin correctiva que podra ser: Pop pila-de-tipos; pop pila-de-tipos Push pila-de-tipos (posible resultado de la operacin) 5. igual a 4 6. , 7. No llevan accin semntica Generacin de cdigo para estatutos Estatutos condicionales Para los estatutos que traduciremos a partir de esta seccin, se requerir de una nueva pila, la pila de saltos, adems introduciremos una nueva instruccin en cdigo intermedio, la instruccin goto que aparecer en varias modalidades: Goto (*incondicional*) Gotofalso operando (*que efecta un salto si el operando tiene un valor falso*) Gotoverdadero operando (*que efecta un salto si el operando tiene un valor verdadero*) Generalmente, al generar un goto, aun no sabemos a que direccin saltara, entonces quedara pendiente por rellenar; para efectuar esta funcin se utiliza: 60

I.S.C. Leonel Vzquez Aguirre

Rellenar (direccin a rellenar, valor con que se rellena) Adems utilizaremos un contador que llamaremos cont que contendr la direccin del siguiente cudruplo a generar, al inicio de la generacin de cdigo, cont tendr un valor de 1.

Estatuto if_then_else

1. Aux=pop pila-de-tipos Si aux diferente de booleano entonces error semntico. Sino Generar gotofalso pop pila-de-operandos_____ Push pila-de-saltos(cont-1) 2. Generar goto________ Rellenar (pila-de-saltos, cont) Push pila-de-saltos (cont -1) 3. Rellenar (pop pila-de-saltos,cont) Estatuto case

1. Verificar que el tipo de la expresin sea ordinal (entero, char, bool, ) Meter una marca de fondo falso en la pilasaltos 2. Verificar que la expresin ordinal (que debe se un dato simple) tenga el mismo tipo que exp (el tope actual de la pila o) Cte= pop de la pila o, exp =pop de la pilao Generar el cuadruplo: =, exp, cte, tk Generar el cuadruplo: gotov, tk,____ Sacar el temporal tk de la pilao Meter nuevamente exp a la pilao Meter cont-1 en la pilasaltos 3. Verificar que la expresin ordinal (que debe ser un dato simple) tenga el mismo tipo que exp (el tope actual de la pilao).
I.S.C. Leonel Vzquez Aguirre

61

4.

5.

6. 7.

Cte=pop de la pilao, exp= pop de la pilao Meter nuevamente exp a la pilao Generar el cuadruplo: =, exp, cte, tk Verificar que la expresin ordinal (que debe ser un dato simple) tenga el mismo tipo que Cte=pop de la pilao, exp= pop de la pilao Generar el cuadruplo: <=, aexp, cte,tj Sacar el temporal tj de la pilao Sacar el temporal tk de la pilao Generar el cudruplo: and,tj,tk,tn Generar el cudruplo: gotov, tn______ Meter cont_1 en la pilasaltos Mientras no se encuentre la marca de fondo falso de pilasaltos, sacar y: Rellenar todos los gotov con cont+1 Generar un goto,___ Guardar cont-1 en la pilasaltos Rellenar el goto que esta en el tope de la pilasaltos con cont+1 Generar un goto,___ Guardar cont-1 en la pilasaltos Sacar el tope de la pilao (es exp). Rellenar cada uno con cont

Estatutos de repeticion Estatuto repeat

1.-push pila_de_saltos (cont) 2.-generar gotofalso pop pila_de_operandos pop pila_de_saltos Estatuto for

I.S.C. Leonel Vzquez Aguirre

1.-guardar el identificador(direccin ) en la pila de operandos(pilao) , verificar semntica. 2.-exp1= pop de pilao( es la variable que contiene el resultado de la expresin) 62

Id= tope de la pila de operandos(pilao) (sin sacarlo). Generar el cudruplo: (:=, exp1, , id) 3.-obtener una variable temporal (tf) /* ver nota*/ Exp2=pop de la pilao (es la variable que contiene el resultado de la expresin2) Obtener otra variable temporal (tx). Generar los cudruplos: (:= , exp2, , tf) (<=, id , tf, tx) /*sin liberar a tf*/ (gotof, tx, ) liberar la variable temporal tx. Meter en la pilasaltos direccin cont-2 (#de cudruplo del <=) 4.- id=pop de pilao Generar cudruplo: (+, id, 1 , id) Retorno=pop de pilasaltos Generar cudruplo: (goto, retorno) Rellena (retorno+1, cont) /*corresponde al gotof*/ Libera la variable temporal tf. ** nota : se utiliza la variable temporal tf para dejar el resultado de exp2, porque si exp2 fuera una variable simple (ej: n), se podra alterar su valor dentro de algn estatuto del for y pudiera ocacionar problemas. (ej: provocar un ciclo infinito). Generacin de cdigo para variables dimensionadas El usuario podr declarar variables de cualquier dimensin (si el lenguaje de programacin lo permite), y es prcticamente imposible que el traductor haya contemplado dentro de su definicin estructuras de datos para aceptar cualquier dimensin de cualquier orden. As que lo que puede hacer el traductor es convertir los arreglos (a traducir) de cualquier dimensin en arreglos de una dimensin. Dada una declaracion : Id-dim: array [limite inferior1..limite superior1, limite superior2,,limite inferior n.. Limite superior n] of tipo. Formula: Id-dim[s1,s2 .sn]= s1*m1+s2*m2+ .+s(n-1)*m(n-1)+sn + k + base Donde: M1=d2*d3* .dn M2=d3*d4* .dn . M(n-1)=dn K=-(limite inferior *m1 + limite inferior2 * m2+ + limite inferior n) Una posible estructura de datos para efectuar la declaracin y almacenarla en la tabla de smbolos seria:
I.S.C. Leonel Vzquez Aguirre

inferior2..limite

63

Tabla de simbolos Nombre Li1 ls1 M1 Li2 ls2 M2 Lin S lis

Adems se deber de almacenar la base relacionada con el nombre del id. Acciones para la declaracin de variables dimensionadas

1.-guardar id en pilo 2.-indicar en la tabla de variables que todas las variables que estn en la pila son dimensionadas. 3.-obtener un campo para la descripcin de la dimensin. Ligar todos los identificadores de la pila con este campo Dim=1, r=1 4.-almacenar, en el campo de descripcin, la constante en li dim. 5.-almacenar en el campo de descripcin, la constante en ls dim. R=(ls dim li dim + 1) * r 6.-dim = dim+1 Obtener un nuevo campo para la siguiente descripcin. Ligar el campo anterior con el nuevo campo 7.- ligar el ltimo campo con el nil Regresar el primer campo de descripcin Dim =1, suma =0, aux=r A: m dim =r / (ls dim li dim+1) R=m dim Suma=suma +li dim * m dim Dim=dim+1 (obtener siguiente dimensin ) Si siguiente dimensin diferente de nil entonces Regresar a a K=suma Almacenar k **k es la cte. De la formula 8.-repetir Identificador=pop pilao Almacenar base y tipo en el identificador Base=base + aux Hasta que la pila quede vacia.

I.S.C. Leonel Vzquez Aguirre

64

Acciones de generacin de cdigo para el acceso a una variable dimensionada

1.-push pilao (id) 2.-id =pop pilao Verificar que id corresponda a una variable dimensionada Dim=1 Push piladimensionadas (id, dim) Obtener primer campo de descripcin de id Push poper( marco de fondo falso) 3.-generar cudruplo: Verificar tope-pilao li dim ls dim Nota: no se saca el top de la pila de operandos Si siguiente apuntador es diferente de nil entonces Aux= pop pilao T= temporal del avail Generar * aux m dim t Push pilao (t) Si dim > 1 entonces Aux2= pop pilao Aux1= pop pilao T=temporal de avail Generar cudruplo + aux1 aux2 t Push pilao 4.-dim= dim +1 Actualizar dim en piladimensionadas Obtener siguiente campo de descripcin 5.-aux1= pop pilao T = temporal del avail Generar cudruplos: + aux1 k t **k es la cte. Obtenida en la formula + t base t Push pilao((t)) **considerar que (t) contiene el desplazamiento definitivo Pop poper(* eliminar marca*) Pop piladimensionadas

I.S.C. Leonel Vzquez Aguirre

65

Nota: se debe aadir e estas acciones la verificacin semntica para reconocer si coincide la cantidad de dimensiones declarada, con la referida. -cada vez que se saca un temporal de la pila de operandos, se debe regresar al avail. Generacin de cdigo para mdulos A continuacin se definen las acciones de generacin de cdigo que se deben llevar a cabo para los mdulos.

Acciones para la definicin de un procedimiento 1. 2. 3. 4. 5. 6. Dar de alta al nombre en el directorio del procedimientos, verificar su semntica Ligar cada parmetro a la tabla de parmetros del directorio de proc s Dar de alta el tipo de los parmetros Dar de alta, en el dir. De proc s, el numero de parmetros declarados. Dar de alta, en el dir. De proc s, el numero de variables locales definidas Dar de alta, en el dir. De proc s, el numero de cudruplo (cont) en el que inicia el procedimiento. 7. Liberar la tabla de variables locales del procedimiento Generar una accin de retorno

Acciones para la llamada a un procedimiento

I.S.C. Leonel Vzquez Aguirre

1. Verificar que e procedimiento exista como tal en el dir de proc s 2. Generar accin: era tamao (expresin del registro de activacin, segn de variables). Inicializar contador de parmetros (k) en 1 Apuntar al primer parmetro, dentro de la tabla de parmetros de se procedimiento. 66

3. Argumento= pop de pilaoperandos, tipoarg= pop de pilatipos Verificar el tipo del argumento contra el del parmetro k Generar parametro, argumento, parmetro k (*se suponen parmetros por valor*) 4. K0k+1, apuntar al siguiente parmetro 5. Verificar que el ultimo parmetro apunte a nulo. (para congruencia en # de parmetros). 6. Generar gosub, nombre-proc, dir. De inicio. Acciones para funciones y parmetros por referencia Es responsabilidad del usuario extrapolar lo hecho para procedimientos, de tal forma que se pueda generar cdigo para funciones, ali como disear las acciones de cdigo necesarias pata soportar parmetros por referencia. Ejecucin de algunos cdigos de operacin especiales para procedimientos Era tamao: salvar la base local actual. Actualizar la base local. Generar e espacio de trabajo para las variables locales y los parmetros del procedimiento. Gosub nombre-proc,dir. De inicio: meter la direccin de retorno en la pila de ejecucin. Transferir el control de ejecucin a la direccin de inicio del procedimiento. Retorno: actualizar base local. Destruir el registro de activacin del proc. Recuperar la direccin de retorno y transferir el control de ejecucin.

I.S.C. Leonel Vzquez Aguirre

67

Optimizacin de Cdigo Intermedio Las optimizaciones se realizan en base al alcance ofrecido por el compilador. La optimizacin va a depender del lenguaje de programacin y es directamente proporcional al tiempo de compilacin; es decir, entre ms optimizacin mayor tiempo de compilacin. Como el tiempo de optimizacin es gran consumidor de tiempo (dado que tiene que recorrer todo el rbol de posibles soluciones para el proceso de optimizacin) la optimizacin se deja hasta la fase de prueba final. La optimizacin es un proceso que tiende a minimizar o maximizar alguna variable de rendimiento, generalmente tiempo, espacio, procesador, etc. Desafortunadamente no existen optimizadores que hagan un programa ms rpido y que ocupe menor espacio. La optimizacin se realiza reestructurando el cdigo de tal forma que el nuevo cdigo generado tenga mayores beneficios. La mayora de los compiladores tienen una optimizacin baja, se necesita de compiladores especiales para realmente optimizar el cdigo. Cada optimizacin est basada en una funcin de coste y en una transformacin que preserve el significado del programa. Mediante la funcin de coste se evala la mejora que se ha obtenido con esa optimizacin y si compensa con el esfuerzo que el compilador realiza para poder llevarla a cabo. Los criterios ms comunes que se suelen emplear son el ahorro en el tamao del cdigo, la reduccin del tiempo de ejecucin y la mejora de las necesidades del espacio para los datos del programa. Tipos de optimizacin Optimizacin Local La optimizacin local se realiza sobre mdulos del programa. En la mayora de las ocasiones a travs de funciones, mtodos, procedimientos, clases, etc. La caracterstica de las optimizaciones locales es que slo se ven reflejados en dichas secciones. La optimizacin local sirve cuando un bloque de programa o seccin es crtico, por ejemplo: la E/S, la concurrencia, la rapidez y confiabilidad de un conjunto de instrucciones. Como el espacio de soluciones es ms pequeo la optimizacin local es ms rpida. Las sentencias de un bloque bsico constituyen una unidad sobre la cual se aplican las optimizaciones locales.

I.S.C. Leonel Vzquez Aguirre

68

Estas optimizaciones se pueden dividir en: A) Optimizaciones que no modifican la estructura. Eliminacin de sub-expresiones comunes. Eliminacin de cdigo muerto. Renombrar variables temporales. Intercambio de sentencias independientes adyacentes. B) Transformaciones algebraicas. Son aquellas transformaciones que simplifican expresiones y/o reemplazan operaciones costosas de la mquina por otras menos costosas. Adems de este tipo de optimizaciones locales a un bloque bsico, existe otro tipo de optimizaciones an ms locales, pues su mbito se reduce a una breve secuencia de instrucciones. A este tipo de optimizacin local se le llama optimizacin peephole, e intenta mejorar el rendimiento del programa por medio de reemplazar esa breve secuencia de instrucciones objeto por otra secuencia ms corta y/o ms rpida. Hay varios tipos de optimizacin peephole, siendo los ms usuales los siguientes: Eliminacin de instrucciones redundantes. Optimizaciones en el flujo de control. Simplificaciones algebraicas. Uso de instrucciones mquina especficas. Debido a la naturaleza de este tipo de optimizacin, su salida es susceptible de ser optimizada de nuevo, con lo que sern necesarias varias pasadas para lograr la mxima optimizacin. Optimizacin de Bucles Los ciclos son una de las partes ms esenciales en el rendimiento de un programa, dado que realizan acciones repetitivas y si dichas acciones estn mal realizadas, el problema se hace N veces ms grande. La mayora de las optimizaciones sobre ciclos tratan de encontrar elementos que no deben repetirse. Habitualmente, un programa pasa la mayor parte del tiempo de la ejecucin en un trozo de cdigo pequeo. A este fenmeno se le conoce como la regla 90-10, queriendo decir que el 90% del tiempo es pasado en el 10% del cdigo. Este 10% del cdigo suele estar constituido por bucles, y de ah la importancia de una correcta optimizacin del cdigo que forma parte de los bucles. Las principales optimizaciones que se realizan en los bucles son las siguientes: Movimiento de cdigo. Eliminacin de variables inducidas. Sustitucin de variables costosas por otras menos costosas. Aunque tambin se suelen aplicar (aunque con menor importancia): 4. Expansin de cdigo (loop unrolling). 5. Unin de bucles (loop jamming).
I.S.C. Leonel Vzquez Aguirre

69

El problema de la optimizacin en ciclos, y en general, radica en que es muy difcil saber el uso exacto de algunas instrucciones. As que no todo cdigo de proceso puede ser optimizado. Optimizacin Global La optimizacin global se da con respecto a todo el cdigo. Este tipo de optimizacin es ms lenta pero mejora el desempeo general de todo programa. En algunos casos es mejor mantener variables globales para agilizar los procesos pero consume ms memoria. Algunas optimizaciones incluyen utilizar como variables registros del CPU, utilizando instrucciones en ensamblador. Las tcnicas de optimizacin global se basan en el anlisis global de flujo de datos. Este anlisis se realiza para el cdigo de todo el programa, es decir, a lo largo de los distintos bloques bsicos que forman el cdigo del programa. Suponiendo que tenemos la informacin que nos proporciona este anlisis, hay dos tipos de optimizaciones importantes que se realizan: la localizacin y la asignacin global de registros para las variables, y las optimizaciones que se realizan en los bucles. Cuando el compilador realiza optimizaciones globales, hay problemas ms difciles que el depurador simblico debe resolver y a menudo no hay manera de encontrar el valor correcto de una variable en un punto. Dos transformaciones importantes que no ocasionan problemas significativos son la eliminacin de variables de induccin y la eliminacin de subexpresiones comunes globales. Optimizacin de Mirilla La optimizacin de mirilla trata de estructurar de manera eficiente el flujo del programa, sobre todo en instrucciones de bifurcacin como son las decisiones, ciclos y saltos de rutinas. La idea es tener los saltos lo ms cerca de las llamadas, siendo el salto lo ms pequeo posible. Una estrategia de generacin de cdigo proposicin a proposicin produce cdigo objeto que contiene instrucciones redundantes y construcciones subptimas. La calidad de dicho cdigo objeto se puede mejorar aplicando transformaciones optimizadoras al programa objeto. Muchas transformaciones simples pueden mejorar significativamente el tiempo de ejecucin o las exigencias de espacio del programa objeto, de manera que es importante saber que tipo de transformaciones son tiles en la prctica. Una tcnica sencilla pero efectiva para mejorar localmente el cdigo objeto es la optimizacin mediante mirilla, un mtodo para intentar mejorar el rendimiento del proyecto objeto examinando una secuencia corta de instrucciones objeto (llamada mirilla) y sustituyendo estas instrucciones por una secuencia ms corta o ms rpida, si es posible. Aunque se estudia la optimizacin mediante mirilla como una tcnica para mejorar la calidad del cdigo objeto, la tcnica tambin se puede aplicar directamente 70

I.S.C. Leonel Vzquez Aguirre

despus de la generacin de cdigo intermedio para mejorar la representacin intermedia. La mirilla es una ventana pequea que se mueve en el programa objeto. No hace falta que el cdigo dentro de la mirilla sea contiguo, aunque algunas aplicaciones exigen que lo sea. Una caracterstica de la optimizacin mediante mirilla es que cada mejora puede brindar oportunidades para mejoras adicionales. Costos Los costos son el factor ms importante a tomar en cuenta a la hora de optimizar ya que en ocasiones la mejora obtenida puede verse no reflejada en el programa final pero si ser perjudicial para el equipo de desarrollo. La optimizacin de una pequea mejora tal vez tenga una pequea ganancia en tiempo o en espacio pero sale muy costosa en tiempo en generarla. Pero en cambio si esa optimizacin se hace por ejemplo en un ciclo, la mejora obtenida puede ser N veces mayor por lo cual el costo se minimiza y es benfico la mejora. Costo de Ejecucin Los costos de ejecucin son aquellos que vienen implcitos al ejecutar el programa. En algunos programas se tiene un mnimo para ejecutar el programa, por lo que el espacio y la velocidad de los microprocesadores son elementos que se deben optimizar para tener un mercado potencial ms amplio. Las aplicaciones multimedia como los videojuegos tienen un costo de ejecucin alto por lo cual la optimizacin de su desempeo es crtico, la gran mayora de las veces requieren de procesadores rpidos (tarjetas de video) o de mucha memoria. Otro tipo de aplicaciones que deben optimizarse son las aplicaciones para dispositivos mviles. Los dispositivos mviles tienen recursos ms limitados que un dispositivo de cmputo convencional razn por la cual, el mejor uso de memoria y otros recursos de hardware tiene mayor rendimiento. Criterios para mejorar el Cdigo La mejor manera de optimizar el cdigo es hacer ver a los programadores que optimicen su cdigo desde el inicio, el problema radica en que el costo podra ser muy grande ya que tendra que codificar ms y/o hacer su cdigo ms legible. Muchos de estos criterios pueden modificarse con directivas del compilador desde el cdigo o de manera externa. Este proceso lo realizan algunas herramientas del sistema como los ofuscadores para cdigo mvil y cdigo para dispositivos mviles. Dicho de una manera sencilla, las mejores transformaciones de programas son las que producen el mayor beneficio con el menor esfuerzo. Las transformaciones realizadas por un compilador optimizador tienen varias propiedades. 71

I.S.C. Leonel Vzquez Aguirre

Primero, una transformacin debe preservar el significado de los programas. Es decir, una optimizacin no debe cambiar el resultado producido por un programa para una entrada dada, o causar un error que no estuviera presente en la versin original del programa fuente. Segundo, una transformacin debe acelerar los programas en una cantidad mensurable. En ocasiones interesa reducir el espacio que ocupa el cdigo compilado, aunque el tamao del cdigo tiene menos importancia que la que tena antes. No toda transformacin consigue mejorar todo programa y, ocasionalmente, una optimizacin puede ralentizar ligeramente un programa, mientras en general mejore las cosas. Tercero, una transformacin debe valer la pena. No tiene sentido que el escritor de un compilador haga el esfuerzo intelectual de aplicar una transformacin que mejore el cdigo y que el compilador gaste el tiempo adicional compilando programas fuente si este esfuerzo no es recompensado cuando se ejecutan los programas objeto. Algunas transformaciones solo se pueden aplicar despus de un anlisis detallado y que lleva su tiempo del programa fuente, de modo que tiene poco sentido aplicarlas a programas que solo se ejecutarn pocas veces. Solo cuando el programa en cuestin toma una fraccin significativa de los ciclos de la mquina, la calidad mejorada del cdigo justifica el tiempo empleado en ejecutar un compilador optimizador sobre el programa. Herramientas para el anlisis del flujo de datos Para que el compilador pueda realizar la mayora de las optimizaciones, es necesario que posea informacin de todo el programa, para poder determinar si una variable est viva, o si dos subexpresiones son comunes, o si una variable se puede sustituir por un valor constante, etc. En funcin de que el anlisis se realice hacia adelante (de la primera instruccin de cdigo a la ltima) o hacia atrs, y en funcin del tipo de operador que aparece (operador unin u operador interseccin), se presentan cuatro tipos de problemas en el anlisis del flujo de datos: Existen algunas herramientas que permiten el anlisis de los flujos de datos, entre ellas tenemos los depuradores y desensambladores. La optimizacin al igual que la programacin es un arte y no se ha podido sistematizar del todo. Existen grandes similitudes entre los distintos problemas de flujo de datos. Kildall aplic una herramienta para simplificar la implantacin de problemas de flujo de datos y la utiliz para varios proyectos de compiladores. No ha sido muy utilizada, porque la cantidad de trabajo ahorrada por el sistema no es tan grande como la ahorrada por herramientas como los generadores de analizadores sintcticos. Sin embargo, hay que saber lo que se puede hacer no solo porque sugiere una simplificacin a los implantadores de compiladores optimizadores, sino tambin porque ayuda a unificar las distintas ideas sobre optimizacin de cdigo. Si solo se considera el tipo iterativo de solucin a los problemas de flujo de datos, entonces no importa la direccin del flujo; se puede invertir la direccin de las
I.S.C. Leonel Vzquez Aguirre

72

aristas y hacer algunos ajustes menores para el nodo inicial y entonces considerar un problema de propagacin hacia atrs como si fuera hacia adelante.

I.S.C. Leonel Vzquez Aguirre

73

Referencias Bibliogrficas.

Donovan, Jhon, J. System Programming, El ateneo. Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman, Compiladores:Principios, Tcnicas y Herramientas. Addison Wesley Iberoamericana. Fischer, LeBlanc. Crafting a compiler with C.Cummings Publishing Company, Inc Jean-Paul Tremblay, Paul G. Sorenson, The Theory and Practice of Compiler Writing, McGraw-Hill Book Company Karen A. Lemone, Fundamentos de compiladores: como traducir al lenguaje de computadora.

I.S.C. Leonel Vzquez Aguirre

74

Anda mungkin juga menyukai