Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Tabla de contenido
1. Utilidad de una tabla de smbolos ................................................................................ 3 2. Estructura lgica de una tabla de smbolos.................................................................. 4 3. Operaciones fundamentales en la tabla de smbolos .................................................. 7 4. Estructuras de datos subyacentes ................................................................................ 8 4.1. Listas o tablas sin orden ........................................................................................ 9 4.2. Listas o tablas con orden ..................................................................................... 10 4.3. rboles binarios ................................................................................................... 10 4.4. Tablas hash .......................................................................................................... 11 4.5. Cmo abordar el problema de los mbitos ......................................................... 13 4.6. Tablas de smbolos locales .................................................................................. 16 5. Contenido de la tabla de smbolos ............................................................................. 16 6. Referencias ................................................................................................................. 17
Pgina 2 de 17
La tabla de smbolos es una estructura de datos temporal en el caso de los compiladores, ya que una vez generado el cdigo objeto se descarta al no tener mayor utilidad. Algunos compiladores, no obstante, tienen la capacidad para incluirla en el ejecutable a fin de facilitar la depuracin, de forma que el programador puede ir inspeccionando las variables por su nombre, no como posiciones de memoria, y de igual forma identificar los procedimientos a los que se invoca, argumentos de entrada y salida, etc. Los intrpretes emplean la tabla de smbolos durante todo el proceso de traduccin y ejecucin del programa, lo cual les permite, por ejemplo, saber qu valor contiene una cierta variable en cada momento y actuar sobre ste. Puede afirmarse, por tanto, que la tabla de smbolos es una de las piezas fundamentales en el funcionamiento de los compiladores, de ah que sea importante su estudio en una asignatura como Procesadores de lenguajes: operaciones que contempla, estructura de datos subyacente, evolucin a lo largo de las distintas fases de compilacin, etc. Dado que la tabla de smbolos es una estructura de datos relativamente compleja, en la que se contemplan varios tipos de operaciones posibles, lo habitual es que exista una lgica Pgina 3 de 17
independiente encargada de gestionarla. En el diagrama siguiente se han representado los distintos pasos de un hipottico compilador y cules seran las operaciones que solicitaran al gestor de la tabla de smbolos:
La primera columna contendra el identificador de cada smbolo: nombre de variable, funcin, etc. En la segunda habra una lista de atributos mediante los que se obtiene informacin sobre el smbolo: categora (variable, funcin, constante ...), tipo, direccin de almacenamiento, lista de argumentos, tipo de retorno, etc. Pgina 4 de 17
Dependiendo de las caractersticas de cada lenguaje, el compilador puede almacenar directamente en la tabla de smbolos la secuencia de caracteres que componen cada identificador o, en su lugar, mantener una estructura paralela. En el primer caso es obvio que existe una longitud mxima para los identificadores, adems de un desperdicio de espacio. Hasta no hace mucho haba compiladores, recuerdo algunos de la firma Borland, en los que los identificadores podan tener cualquier longitud pero solamente eran significativos los primeros 32 caracteres, de forma que dos nombres de variable que coincidiesen en esos 32 caracteres (no era habitual) el compilador los identificaba como un mismo smbolo. Supongo que esa limitacin se deba a que en la tabla de smbolos de estos compiladores los identificadores se almacenaban en la propia tabla, donde haba reservados 32 bytes para tal fin. La alternativa, utilizada por la mayora de los compiladores actuales, consiste en guardar los identificadores en una estructura paralela, almacenando en la propia tabla de smbolos nicamente un puntero a la posicin del identificador y su longitud, por ejemplo: Smbolo $00,4 $04,8 $12,6 ...
00 E 01 d 02 a 03 d 04 D 05 i 06 f 07 F 08 e 09 c
Atributos Var, Entero, $02 Func, Entero,2,Fecha,Fecha Var, Cadena, $06 ...
10 h 11 a 12 N 13 o 14 m 15 b 16 r 17 e 18 19
Otra posibilidad sera almacenar como smbolo en la tabla principal solamente el puntero, sin especificar la longitud, usando un delimitador de final de cadena en la tabla donde se guarden los identificadores. Sera el caso representado en el siguiente diagrama:
Pgina 5 de 17
En cuanto a los atributos se refiere, cada smbolo tendr unos u otros dependiendo de la categora a que pertenezca: variable, constante, procedimiento, etc. Algunos habituales son: Categora: Determina a qu pertenece el smbolo asociado. Tipo: En el caso de las variables, establece el tipo de informacin que contendrn. Si el smbolo es una funcin, este mismo atributo indicara el tipo de valor devuelto. Argumentos: Para los procedimientos, funciones o mtodos indicar la lista de parmetros que precisa y sus tipos. Posicin: Para las variables es habitual contar con una posicin relativa de memoria asociada. Los intrpretes utilizarn dicha posicin a medida que ejecuten el cdigo para leer y modificar el valor. Valor: Si el smbolo es una constante este atributo contendra el valor que representa. mbito: En los lenguajes orientados a objetos y con estructura de bloques pueden existir mltiples smbolos con el mismo identificador en mbitos distintos, sirviendo este atributo para determinar el mbito a que pertenecen.
Otros atributos que podran existir sera la indicacin de si una variable es un array o matriz, el nmero de dimensiones con que cuenta y el nmero de elementos de cada dimensin, si los parmetros de entrada a un procedimiento tienen asociado un valor por defecto y cul es ste, si un cierto mtodo de un objeto est vinculado estticamente o de forma dinmica (late-binding) en cuyo caso hay que buscar durante la ejecucin en las vtable de mtodos virtuales, etc. En los lenguajes ms evolucionados, con orientacin a objetos y a componentes como pueden ser C# o Java, la tabla de smbolos llega a ser una estructura realmente compleja por la cantidad de atributos que pueden existir. Esto no implica, sin embargo, unas necesidades de almacenamiento muy grandes en la tabla de smbolos, ya que suelen utilizarse paquetes de bits en los que se resume toda la informacin asociada a cada identificador. Con los primeros dos o tres bits se indica la categora y, dependiendo de sta, los paquetes siguientes tienen un significado u otro. Mientras que el identificador asociado a un smbolo es algo que no cambia a lo largo del proceso de compilacin, la lista de atributos asociados a dicho smbolo s que puede experimentar alteraciones a medida que se va pasando de una fase a otra dentro de ese proceso. Asimismo en determinados casos pueden existir en la tabla mltiples copias de un mismo conjunto de smbolos en diferentes mbitos o niveles. Es lo que ocurre, por ejemplo, si el lenguaje tiene una estructura de bloques como la de C, en la que un mismo identificador puede hacer referencia a distintos smbolos en un cierto punto del programa dependiendo del contexto.
Pgina 6 de 17
Pgina 7 de 17
Sobre estas premisas, en los puntos siguientes se analiza la idoneidad de algunas estructuras de datos tpicas para su uso como tabla de smbolos en un compilador. Pgina 8 de 17
Teniendo en cuenta que una tabla de smbolos contendr habitualmente varios cientos de claves, incluso miles en los programas grandes, salta a la vista que esta estructura de datos no resulta adecuada salvo en los casos ms sencillos (con programas muy pequeos), ya que cada bsqueda empleara mucho tiempo y el proceso de compilacin se dilatara considerablemente en el tiempo. Otro problema que plantea el uso de una lista/vector es el mantenimiento de claves duplicadas si el lenguaje lo permitiese, ya que por la simplicidad de la estructura la nica forma de saber qu smbolos son vlidos en el mbito actual sera recorriendo toda la lista de forma secuencial, buscando las distintas coincidencias de la clave, a fin de leer un atributo que identificara el bloque al que pertenece el smbolo.
Pgina 9 de 17
El principal problema de este tipo de estructura es que la insercin y el borrado son operaciones que implicarn un trabajo adicional para mantener ese orden, lo cual traslada el problema de la falta de eficiencia de un punto a otro. Si con la lista sin ordenar la operacin ms lenta era la bsqueda, que afecta a todas las fases pero especialmente al anlisis semntico y la generacin de cdigo, con la lista ordenada es la fase de anlisis lxico la perjudicada al emplearse un tiempo muy superior en la insercin. En cuanto al mantenimiento de claves duplicadas necesaria para los lenguajes con bloques o mbitos, el problema sigue siendo exactamente el mismo que en el caso anterior pero con un problema aadido. Al realizar una bsqueda binaria es posible que smbolos con la misma clave, con el mismo identificador, hayan quedado tanto delante como detrs de la posicin en que se ha encontrado, lo obligara a efectuar comprobaciones adicionales y un recorrido secuencial en ambos sentidos.
La clave de cada nodo del rbol sera el identificador de un smbolo, pudiendo mantenerse el conjunto de atributos en la propia estructura de rbol o bien en una tabla independiente, de forma que en el rbol estaran solamente las claves y un puntero a la informacin del smbolo asociado. Para los lenguajes con bloques o mbitos el rbol no es la mejor opcin. Aunque pueden seguirse una serie de reglas que permitan la duplicidad de claves, la bsqueda de los smbolos del nivel ms interno y la eliminacin de los smbolos cuando un mbito deja de existir, algunas de las operaciones requerirn que se recorra el rbol completo, desde la raz hasta las hojas, perdiendo la eficiencia que se obtendra en un principio.
Pgina 11 de 17
La variable T sera un array que contendra una serie de nmeros aleatorios, su tamao depender de las necesidades de cada caso, y las ventajas del algoritmo es que pequeas variaciones de entrada producen grandes diferencias en el resultado, de forma que smbolos con identificadores parecidos no provocaran colisiones. El uso de una tabla hash, por tanto, resultar mucho ms eficiente a la hora de realizar bsquedas en la tabla de smbolos, as como a la hora de insertar, actualizar o borrar. El tiempo no depender del tamao de la tabla, es decir, de lo grande que sea el programa a compilar, sino que se mantendr aproximadamente constante. Existen, no obstante, dos dificultades a superar: las colisiones y el tamao de la tabla hash.
Una colisin se produce cuando dos claves distintas producen el mismo resultado al ser procesadas por la funcin de hashing, por lo que les correspondera la misma posicin en la tabla. Obviamente no pueden almacenarse dos smbolos en una misma posicin, por lo que se recurre a diferentes tcnicas para solventar el problema. Una de ellas consiste en definir cada posicin de la tabla hash no como un elemento, sino como una cubeta en la que pueden almacenarse varios elementos. Para evitar el desbordamiento de las cubetas pueden implementarse de forma que sean extensibles o bien enlazables. Es obvio que cuanto mejor sea el algoritmo de hashing menor ser la posibilidad de que se produzcan colisiones, pero dicha circunstancia es algo que debe tenerse en cuenta. El otro aspecto importante es el tamao de la tabla ya que, a diferencia de lo que ocurre con otras estructuras de datos, ese tamao suele influir en el algoritmo que determina la posicin de los smbolos y, adems, no es fcil cambiarlo. En el caso ms extremo al crecer el nmero de elementos almacenados podra tener que recurrirse al rehashing tras adecuar el Pgina 12 de 17
tamao de la tabla, redistribuyendo nuevamente todos los smbolos lo cual no es deseable por la influencia que dicha operacin tendra en la eficiencia global. Tanto para la gestin de colisiones como para determinar cul es el tamao adecuado de la tabla y facilitar su crecimiento, si fuese preciso, habra que recurrir a las tcnicas y algoritmos que se estudian en la asignatura Estructuras de datos I, correspondiente al segundo curso de esta titulacin, y en cuyos detalles creo innecesario profundizar aqu.
Pgina 13 de 17
En el diagrama siguiente se ha representado cul sera el estado de la pila de tablas de smbolos en cuatro momentos del procesamiento de un programa sencillo. Entre las lneas 5 y 7, por ejemplo, si se hiciese referencia al smbolo a se encontrara en la tabla de smbolos 2, correspondiente al bloque interno de la sentencia if, mientras que si se hace referencia a b, al no encontrarse en dicha tabla de smbolos se continuara buscando en la siguiente, segn el orden en que se han apilado.
Para los compiladores de mltiples pasadas, que tienen que mantener la tabla de smbolos completa durante las distintas fases hasta que se genere el cdigo final, en lugar de una pila habra que utilizar una lista o vector de tablas hash, asociando a cada uno de los mbitos un nmero nico que actuara como ndice. En ningn momento se eliminaran elementos de ese vector, sino que dependiendo del bloque que est analizndose se recurrira a uno u otro para buscar los smbolos en la tabla hash correspondiente. Una pila simple de enteros, mantenida en paralelo, permitir saber al compilador qu mbitos estn abiertos y, por tanto, tiene que examinar, as como el orden en que tiene que recorrerlos, a la hora de encontrar un smbolo. El mayor inconveniente de estas implementaciones es que, en ambos casos, para localizar un determinado smbolo es necesario recorrer varias estructuras de datos: por una parte la pila o lista de tablas hash y, por otra, cada una de las tablas correspondientes a los mbitos abiertos en ese instante. La alternativa es mantener todos los smbolos de todos los mbitos, incluyendo los que cuentan con identificadores duplicados, en una misma estructura de datos. Lgicamente ser preciso agregar un atributo que permita dilucidar a qu mbito pertenece cada uno de ellos. Una posibilidad, asumiendo que se utiliza una tabla hash como estructura de datos subyacente, sera asociar a cada posicin de la tabla no un elemento sino una lista invertida de elementos. La funcin de hashing generara siempre la misma posicin para los identificadores duplicados, posicin en la que estara la lista con los distintos smbolos de forma que el primero correspondera al mbito actual, el siguiente al mbito abierto contenedor del actual y as sucesivamente. En el diagrama de la pgina siguiente se ha representado cul sera el estado de esa tabla hash en los mismos cuatro momentos del diagrama anterior (con pilas de tablas de smbolos), tomando como base el mismo programa. Entre las lneas 5 y 7 puede verse cmo al Pgina 14 de 17
resolver la referencia para el smbolo a se tendra acceso directamente a la informacin que corresponde al mbito 2. Al cerrarse ese bloque de cdigo se eliminan sus smbolos de la tabla, recuperando el acceso al mismo smbolo del mbito previo. No es necesario, por tanto, ir recuperando tablas de smbolos de una pila y despus buscar en ellas los identificadores, dando como resultado operaciones ms eficientes.
Es un mecanismo rpido para determinar la existencia de un smbolo y obtener sus atributos, si bien habra que complementarlo con algn algoritmo que determinase qu mbito es el actual y qu mbitos hay abiertos en cada momento. En compiladores de una sola pasada el procedimiento sera simple, ya que al entrar en un bloque se agregaran los smbolos al principio de la lista de los elementos respectivos de la tabla hash, mientras que al salir de un bloque se procedera a eliminarlos. Es una tcnica similar a la de la pila de tablas hash descrita antes, pero en este caso las pilas se encontraran como elementos de la tabla y no a la inversa. Para los compiladores multipasada sera preciso mantener una estructura independiente, como la pila de nmeros de mbito abiertos citada anteriormente, para conocer cules de los smbolos de la tabla seran vlidos en cada fase del proceso.
Pgina 15 de 17
Pgina 16 de 17
Tipos de datos: Los identificadores que representan a los tipos de datos bsicos del lenguaje, los intrnsecos, tambin pueden formar parte del contenido inicial de la tabla de smbolos.
Tambin es posible, dependiendo del lenguaje, que a la tabla de smbolos vayan aadindose durante el anlisis del cdigo fuente smbolos generados por el propio compilador. En Java y C#, por ejemplo, es posible definir mtodos annimos asociados a un cierto evento, bloques de cdigo para los que el programador no establece explcitamente un nombre. El compilador, sin embargo, s genera un identificador que se agrega a la tabla de smbolos. Por ltimo, mencionar que los compiladores de ciertos lenguajes, como es el caso de C++, modifican los identificadores que el programador crea para ciertos elementos, como las funciones y mtodos, segn el nmero y tipo de los parmetros. Es una tcnica conocida como name mangling o decorado de smbolos y que hace posible lo que se conoce como sobrecarga, de forma que pueden tenerse varios mtodos con el mismo nombre siempre que la lista de argumentos difiera. En la tabla de smbolos realmente cada mtodo aparecera como un smbolo distinto, aadindose al identificador original una serie de caracteres adicionales que lo diferencian del resto de versiones sobrecargadas.
6. Referencias
Para la realizacin de este trabajo he recurrido a los apuntes de la propia asignatura Procesadores de lenguajes I, el libro Compiladores e intrpretes de Manuel Alfonseca et al., la pgina del profesor Alfonseca sobre compiladores en http://arantxa.ii.uam.es/~alfonsec, el libro Compiladores del profesor Sergio Glvez Rojas y los apuntes sobre hashing de la asignatura Estructuras de datos I.
Pgina 17 de 17