fundamentos teóricos de la informática -...

26
Fundamentos teóricos de la informática

Upload: ngodan

Post on 18-Sep-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

Fundamentos teóricos de la informática

Page 2: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica
Page 3: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

Prácticas de la asignatura Procesadores de Lenguaje con la herramienta ANTLR

José A. Troyano, Francisco J. Galán, Vicente Carrillo,

Fernando Enríquez, Enrique J. García Depto. de Lenguajes y Sistemas Informáticos

Universidad de Sevilla 41012 Sevilla

e-mail: [email protected]

Resumen En este artículo presentamos una experiencia docente en las prácticas de laboratorio de la asignatura troncal Procesadores de Lenguaje. Es muy común en este tipo de asignaturas el uso de herramientas para generar analizadores léxicos y sintácticos a partir de expresiones regulares y gramáticas, respectivamente; pero no está tan extendido el uso de herramientas para dar cobertura a las últimas etapas de un procesador de lenguaje, las que se encargan de procesar los aspectos semánticos del mismo. Nosotros hemos utilizado ANTLR, una herramienta que da soporte a todas las etapas del procesamiento de lenguajes formales, permitiendo generar analizadores léxicos, sintácticos, árboles de sintaxis abstracta y analizadores de árboles para el procesamiento semántico.

1. Introducción Tradicionalmente las herramientas utilizadas en las asignaturas dedicadas a los procesadores de lenguaje (los clásicos compiladores) no cubren todos los contenidos presentados en la teoría. Mientras que para las primeras etapas (front-end) se suelen utilizar herramientas basadas en gramáticas y expresiones regulares, para la última parte (back-end) es difícil encontrar herramientas que den soporte a los problemas que allí se plantean. Las etapas del análisis léxico y sintáctico de los lenguajes suelen estar resueltas por herramientas descendientes de la pareja clásica lex y yacc, como las implementaciones en C/C++ flex y bison, o más recientemente, las correspondientes herramientas en Java jflex y cup. Tanto el

problema léxico como el sintáctico están perfectamente estudiados y hay soluciones satisfactorias para ellos desde hace muchos años, lo que hace que existan herramientas muy bien acabadas y de uso muy extendido. Una vez que el alumno comprende los conceptos de gramática y expresión regular, y ha aplicado las herramientas a unos pocos ejemplos, estos problemas empiezan a dejar de tener secretos y el diseño de sus soluciones se puede considerar casi metódico. Esto plantea una situación ideal desde el punto de vista docente, con problemas bien fundamentados teóricamente, herramientas consolidadas, y una metodología para construir la solución. Sin embargo, la situación no es tan ideal para las etapas dedicadas al procesamiento semántico de los lenguajes. Por un lado, los problemas a resolver no son tan parecidos cuando pasamos de un lenguaje a otro. Por ejemplo, no es muy distinto escribir las gramáticas de un lenguaje imperativo y de un lenguaje declarativo, pero implementar sendos intérpretes plantea problemas muy diferentes. La otra diferencia está en las herramientas, en el caso del tratamiento semántico no existe ningún referente ampliamente usado y, salvo el limitado apoyo que supone el uso de gramáticas con atributos durante el reconocimiento sintáctico, la situación está muy cercana al "búsquese usted la vida". Esta diferencia queda incluso patente en la bibliografía clásica de la materia, textos como [1] ó [8] dejan completamente cerrado el problema sintáctico con especificaciones basadas en gramáticas, mientras que en los capítulos dedicados al tratamiento semántico exponen los conceptos y la hora de presentar la solución lo hacen directamente apoyándose en la implementación con un pseudocódigo o lenguaje de programación. El caso de [2] se sale un poco de la norma al presentar de una manera más profunda

Page 4: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

324 Fundamentos teóricos de la informática

conceptos como gramáticas abstractas y gramáticas con atributos evaluadas sobre árboles, pero no lo complementa con una herramienta que dé apoyo a toda la metodología. Nuestra propuesta académica para esta situación pasa por el uso de la herramienta ANTLR [4] en todas las etapas del procesamiento de lenguajes. Esta herramienta, que viene desarrollándose desde principios de los 90 con su antecesor PCCTS, ha alcanzado un grado de madurez tal que la hace una alternativa competitiva a las herramientas clásicas para la etapa sintáctica, con el valor añadido de disponer de una notación basada en gramáticas de árboles que permite especificar procesos como el análisis semántico o la generación de código de una manera abstracta y compacta. En el resto de este trabajo presentaremos nuestra propuesta de diseño de prácticas que va acompañada del material docente correspondiente, publicado en [3] y que ha sido utilizado con muy buenos resultados en la asignatura Procesadores de Lenguaje I (en adelante PL1) durante el curso 2004-05. 2. Marco académico Esta sección está dedicada a presentar el contexto académico en el que se encuadra la asignatura PL1 para la que se han diseñado las prácticas de laboratorio que constituyen el núcleo de este trabajo. En primer lugar nos dedicaremos a resumir brevemente las asignaturas previas y posteriores a PL1, para luego introducir el programa de la asignatura. 2.1. Asignaturas previas y posteriores En nuestro plan de estudios de Ingeniería Informática disponemos de cuatro asignaturas cuatrimestrales dedicadas al procesamiento de lenguajes: Lenguajes Formales y Autómatas (LFA), Ampliación de Lenguajes Formales y Autómatas (ALFA) y Procesadores de Lenguaje I y II (PL1 y PL2). Las asignaturas LFA y ALFA están relacionadas con la materia troncal Teoría de Autómatas y Lenguajes Formales y en ellas se introducen los fundamentos teóricos de los lenguajes tanto desde el punto de vista generativo (gramáticas y expresiones regulares) como del

reconocimiento (autómatas). Se imparten, respectivamente, en segundo y tercer curso. La asignatura PL2 es la continuación natural de PL1. Tras haber presentado en PL1 los conceptos, las técnicas y las herramientas utilizadas en el desarrollo de procesadores de lenguaje, en PL2 se aplican a la implementación de compiladores. Se tratan las etapas de comprobación de semántica estática, optimización de código y generación de código para la máquina virtual de Java. 2.2. Programa de la asignatura PL1 Dentro de este esquema, la asignatura PL1 tiene como objetivo principal establecer un puente entre los conceptos teóricos y las aplicaciones sencillas desarrolladas en las asignaturas LFA y ALFA por un lado, y el enfoque más ingenieril y aplicado de PL2. En este sentido, los dos objetivos principales de la asignatura son 1) explicar cómo se implementan las herramientas que generan analizadores sintácticos y 2) presentar las herramientas que permiten abordar la implementación de un procesador de lenguaje. Los temas en los que se organiza la asignatura son los siguientes: 1. Análisis léxico 2. Análisis sintáctico descendente 3. Análisis sintáctico ascendente 4. Recuperación de errores en analizadores

descendentes 5. Gramáticas con atributos 6. Sintaxis abstracta y árboles de sintaxis

abstracta 7. Gramáticas para árboles Los cuatro primeros temas se dedican al análisis léxico y sintáctico, haciendo especial hincapié en los analizadores descendentes porque es el modelo que implementa ANTLR. El tema cinco presenta una primera aproximación a la semántica a través de las gramáticas con atributos evaluadas al vuelo (junto al análisis sintáctico). Por último los temas 6 y 7 presentan los árboles de sintaxis abstracta, su relación con las gramáticas abstractas y la especificación de recorridos a través de gramáticas para árboles.

Page 5: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 325

3. Características básicas de ANTLR En esta sección resumiremos los elementos más destacables de la herramienta ANTLR. Será una exposición muy breve, se pueden encontrar descripciones más completas en el manual de la herramienta [6] así como en los enunciados de las prácticas disponibles en la página de la asignatura. Casi todos los ejemplos utilizados para presentar la herramienta se corresponderán con el conocido lenguaje de la calculadora aritmética, que admitirá entradas como la siguiente: (1+2)*3; 2+5*4; 6/5/2; A pesar de ser un lenguaje extremadamente simple, sirve perfectamente para ilustrar las capacidades descriptivas de ANTLR. Se pueden encontrar soluciones a problemas más complicados en el material distribuido a los alumnos [3]. En estos ejemplos se ve que, siguiendo unas mínimas pautas de disciplina a la hora de escribir las especificaciones, se pueden conseguir descripciones muy abstractas y compactas incluso para problemas complejos. 3.1. Analizadores léxicos Seguramente la especificación de analizadores léxicos sea el punto menos fuerte de la herramienta ANTLR. No porque no resuelva bien este problema, sino por la decisión de utilizar el esquema recursivo descendente, en lugar de autómatas finitos, para su implementación. Esta decisión de los autores de la herramienta se debe a que se plantean como una virtud la homogeneidad de modelos de reconocimiento entre distintos analizadores (léxico, sintáctico e incluso de árboles), aplicando en todos los casos el modelo recursivo descendente. En la práctica esto supone algunas molestias, ya que hay que resolver algunos casos de ambigüedad a nivel léxico que no habrían aparecido con autómatas finitos. En el siguiente fragmento se muestra la especificación léxica para el lenguaje ejemplo: class Analex extends Lexer; BLANCO: (' '|'\t'|"\r\n") {$setType(Token.SKIP);}; protected DIGITO : ('0'..'9');

NUMERO: (DIGITO)+ ('.'(DIGITO)+)?; OPERADOR: '+'|'-'|'/'|'*'; PARENTESIS: '('|')'; SEPARADOR: ';'; Cada regla se corresponde con un token, salvo que estén protegidas o que explícitamente se indique que dicho token debe ignorarse y no transmitirse al analizador sintáctico, lo que se hace produciendo el token SKIP. Las reglas protegidas sirven de reglas auxiliares y no compiten con las demás en el intento de construir tokens a partir de los caracteres de entrada. Por lo demás las reglas son bastante claras, pudiéndose utilizar los operadores clásicos de las expresiones regulares (+,?,* y |). 3.2. Analizadores sintácticos Desde el punto de vista de la notación, la aportación más interesante de ANTLR en la descripción de gramáticas es el uso de la notación EBNF. Gracias a esta notación, las partes derechas de las reglas pueden incluir, además de símbolos, los operadores clásicos de las expresiones regulares lo que la hace mucho más expresiva. He aquí la especificación sintáctica del lenguaje ejemplo: class Anasint extends Parser; instrucciones : (exp ";")* ; exp : exp_mult (("+"|"-") exp_mult)* ; exp_mult : exp_base (("*"|"/") exp_base)* ; exp_base : NUMERO | "(" exp ")" ; Con toda seguridad, la parte menos clara de la especificación anterior es la dedicada a definir el símbolo exp. Se ha hecho de esta forma porque, al generar ANTLR analizadores descendentes, no permite describir gramáticas recursivas por la izquierda (esta limitación es parte de la condición LL(1)), que son imprescindibles para especificar la gramática de las expresiones de una forma más declarativa. Precisamente por ser un problema sintáctico complejo, el de las expresiones ha sido muy bien estudiado y existen soluciones asentadas

Page 6: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

326 Fundamentos teóricos de la informática

para distintos tipos de reconocedores (ascendentes, descendentes y de precedencia). Por decirlo de alguna forma, la solución es más compleja que en el resto de los elementos que suelen aparecer en los lenguajes pero no la tenemos que ''diseñar'', la podemos ''reutilizar''. A partir de la gramática, ANTLR implementa un reconocedor recursivo en el que para cada símbolo no terminal existe un método encargado de reconocer el lenguaje asociado a dicho símbolo. 3.3. Soluciones al problema LL Desde el punto de vista teórico, de los dos modelos básicos de reconocimiento sintáctico, el descendente y el ascendente, es éste último el más potente. El modelo descendente genera analizadores más naturales, pero es más exigente con respecto a las gramáticas que puede procesar. Deben satisfacer la condición LL(1), lo que en ocasiones obliga a transformar las gramáticas. ANTLR propone dos soluciones a este problema: la implementación de analizadores LL(k) y los predicados sintácticos. La primera no es una solución nueva y está ampliamente estudiada en la bibliografía [2] y para una presentación más formal [7]. La única aportación de la herramienta en este sentido es adaptarse a cada caso y no generar consultas de alcance k salvo si es estrictamente necesario. La solución de los predicados sintácticos sí es bastante original y permite resolver de forma puntual conflictos de la gramática aunque no puedan resolverse con gramáticas LL(k), siendo k fijo. He aquí un ejemplo de predicado sintáctico: instr : (ID ":=") => asigna | llamada ; asigna : ID ":=" exp ";" ; llamada : ID "(" exp ")" ";" ; La gramática anterior describe dos instrucciones de un determinado lenguaje, la asignación y la llamada a un procedimiento. Ambas comienzan con el símbolo ID, por lo que la gramática no es LL(1), sin embargo ANTLR no tendría ningún problema en procesar esta gramática gracias al predicado sintáctico (ID ":=")=> que aparece antes del símbolo asigna. Con él se indica que es necesario comprobar la aparición de los símbolos ID y

":=" antes de decidir que la instrucción que se está reconociendo es una asignación. Los predicados sintácticos pueden incluir cualquier expresión EBNF, de manera que si incluyesen una expresión con los operadores * ó + estarían estableciendo una condición que requeriría de un número variable de símbolos para su validación, superando así en poder descriptivo a las gramáticas LL(k). 3.4. Gramáticas con atributos La implementación de los analizadores a través del modelo recursivo hace muy fácil la integración de los atributos en la gramática. Dado que cada símbolo tiene asociado un método que lo implementa, los atributos heredados se modelan como parámetros de entrada del método y los sintetizados se resuelven a través del valor de retorno asociado al método. Esta idea, junto con una sintaxis apropiada para declarar los atributos en el seno de una regla es lo único que necesita ANTLR para implementar las gramáticas con atributos. En el siguiente ejemplo se muestra cómo evaluar expresiones aritméticas haciendo uso del atributo sintetizado [int res] de los símbolos exp, exp_mult y exp_base: class Anasint extends Parser; instrucciones {int e;} : (e=exp ";" {System.out.println(e);})* ; exp returns [int res=0] {int e;} : res=exp_mult (("+" e=exp_mult {res=res+e;}) |("-" e=exp_mult {res=res-e;}) )* ; exp_mult returns [int res=0] {int e;} : res=exp_base (("*" e=exp_base {res=res*e;}) |("/" e=exp_base {res=res/e;}) )* ; exception catch [ArithmeticException ae] {res = 0;} exp_base returns [int res=0] {int e;}

Page 7: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 327

: n:NUMERO {res= Integer. parseInt(n.getText());} | "(" e=exp ")" {res = e;} ; Hay dos tipos de informaciones que se pueden recuperar de los símbolos, un objeto de la clase Token para el caso de los símbolos terminales a través de una etiqueta (como n:NUMERO) o un atributo sintetizado por un símbolo no terminal (como e=exp). Por su parte, la notación usada para los atributos heredados es muy similar a la que habitualmente se usa para declarar parámetros de métodos, cambiando los paréntesis por corchetes. Por ejemplo, si tuviésemos expresiones con variables que heredasen un contexto del que extraer los valores de dichas variables lo podríamos describir así: exp_let returns [int res] {Hashtable ctx;} : "(" LET ctx=vars IN res=exp[ctx] ")" ; exp [Hashtable ctx] returns [int res] {int e;} : res=exp_mult[ctx] ("+" e=exp_mult[ctx] {res=res+e;})* ; El símbolo vars sintetiza el atributo ctx que a su vez es heredado por el símbolo exp, que lo utiliza para calcular el valor de la expresión. 3.5. Árboles de sintaxis abstracta Los árboles de sintaxis abstracta son un recurso muy útil en el procesamiento de lenguajes ya que proporcionan una flexibilidad de cálculo que no puede ser alcanzada con gramáticas con atributos evaluadas junto al análisis sintáctico (como las de bison). Cuando nos enfrentamos al procesamiento de lenguajes complejos, los árboles pasan de ser útiles a imprescindibles, ya que proporcionan una representación intermedia del lenguaje que es compartida por todas las etapas del procesador y que ayuda a la implementación modular del mismo. En [2] se puede encontrar una exposición

más extensa de las ventajas del uso de los árboles de sintaxis abstracta. ANTLR proporciona una manera bastante compacta de anotar gramáticas para construir árboles de sintaxis abstracta. Para empezar, construye automáticamente nodos para todos los símbolos procesados durante el análisis sintáctico. Posteriormente estos nodos son descartados (!) o elegidos como raíces (^) simplemente anotándolos con el operador correspondiente. Así para nuestro ejemplo la gramática quedaría así: class Anasint extends Parser; options {buildAST = true;} tokens {INSTRS;} instrucciones : (exp ";"!)* {##=#(#[INSTRS,"INSTRS"],##);} ; exp : exp_mult (("+"^|"-"^) exp_mult)* ; exp_mult : exp_base (("*"^|"/"^) exp_base)* ; exp_base : NUMERO | "("! exp ")"! ; En los casos en los que el árbol propuesto de forma automática no nos sirva, como en el caso de la lista de expresiones (exp ";"!)* en la que no hay ningún símbolo que pueda servir de raíz de la lista, se puede indicar explícitamente cómo se desea que se construya el árbol. Para ello se pueden utilizar, entre otras, las operaciones de construcción de nodos #[...], de construcción de árboles #(...) y de acceso al árbol construido de forma automática con el atributo predefinido ##. En cualquier caso, lo que es innegable es que la especificación es extremadamente simple y expresiva. Así, en nuestro ejemplo, con sólo una instrucción y la inserción de siete operadores, se consigue describir lo que de otra manera nos habría costado decenas de líneas de código. 3.6. Gramáticas para árboles Una vez que el árbol de sintaxis abstracta ha sido construido, las últimas etapas del procesador de lenguaje consisten básicamente en recorridos de dicho árbol para calcular atributos, transformar el

Page 8: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

328 Fundamentos teóricos de la informática

árbol o generar el resultado final. Todos estos recorridos pueden ser expresados mediante gramáticas para árboles. Estas gramáticas son similares a las gramáticas independientes del contexto salvo que en la parte derecha de las reglas en lugar de secuencias de símbolos aparecen patrones de árboles. La siguiente especificación describe la manera en la que se recorren los árboles del lenguaje ejemplo para calcular los valores de las expresiones: class Evaluador extends TreeParser; instrucciones {int e;} : #(INSTRS (e=exp {System.out.println(e);})*) ; exp returns [int res=0] {int e1=0, e2=0;} : n:NUMERO {res= Integer. parseInt(n.getText());} | #("+" e1=exp e2=exp) {res = e1 + e2;} | #("-" e1=exp e2=exp) {res = e1 - e2;} | #("*" e1=exp e2=exp) {res = e1 * e2;} | #("/" e1=exp e2=exp) {res = e1 / e2;} ; exception catch [ArithmeticException ae] {res = 0;} Los patrones son expresiones de la forma #(<raíz> <hijo1>...<hijon>) que describen árboles si todos los hijos son símbolos terminales, o familias de árboles si alguno de los hijos es un no terminal. Además, los patrones pueden estar anidados, por lo que las consultas sobre la estructura de los árboles se pueden realizar a distintos niveles. El resto de los elementos de las gramáticas para árboles tienen el mismo significado que en las gramáticas para texto, pudiéndose definir tanto atributos heredados como sintetizados, así como incluir acciones en cualquier punto de la parte derecha de la regla.

4. Diseño de las prácticas En esta sección presentaremos la manera en la que hemos diseñado las prácticas de la asignatura Procesadores de Lenguaje 1. Dado que el lenguaje con el que están más familiarizados nuestros alumnos cuando llegan a la asignatura es Java, éste ha sido elegido de entre las distintas alternativas de generación que proporciona ANTLR. El conjunto de herramientas necesarias para el desarrollo de las prácticas se completa con Eclipse [5], un entorno de desarrollo configurable para el que ANTLR tiene definido un plugin que permite, entre otras cosas, colorear los ficheros de gramática y compilarlos automáticamente. Tanto Eclipse como ANTLR son de libre distribución y están implementados en Java, por lo que los alumnos pueden instalarlos en casa con el sistema operativo que prefieran. 4.1. Metodología y planificación Hay siete prácticas para todo el cuatrimestre, las dos primeras dedicadas a introducir las herramientas ANTLR y Eclipse, y las cinco siguientes dedicadas a profundizar en las características principales de ANTLR. Los enunciados de las prácticas son documentos de seis o siete páginas en los que se describen los aspectos de las herramientas con los que se va a trabajar durante la sesión de práctica. Aunque no pretenden ser un sustitutivo de los correspondientes manuales, la idea es que los alumnos los utilicen como referente a la hora de aprender el manejo de las herramientas y que recurran a los manuales para buscar cuestiones más específicas y teniendo una idea bastante clara de lo que quieren encontrar. En cada práctica se plantearán varios ejercicios en los que los alumnos tendrán que utilizar los elementos presentados en el enunciado. La dificultad de los ejercicios será variable, el primero será bastante simple y se proporcionará su solución. De hecho, dicha solución habrá sido presentada a lo largo del enunciado para ilustrar con ejemplos la explicación. De esta forma la primera tarea encomendada a los alumnos será compilar y ejecutar los fuentes presentes en el enunciado y comprobar su correcto funcionamiento. A partir de este primer ejercicio los siguientes irán aumentando en complejidad.

Page 9: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 329

El material se completa con la solución de uno de estos ejercicios complejos, que tiene como objetivo mostrar a los alumnos cierto ''estilo de especificación'' que asegure descripciones legibles incluso para problemas complejos. Las prácticas son: 1. Instalación y prueba de Eclipse 2. Instalación y prueba de ANTLR 3. Análisis léxico 4. Análisis sintáctico 5. Gramáticas con atributos evaluadas al vuelo 6. Construcción de árboles de sintaxis abstracta 7. Recorridos de árboles mediante gramáticas

para árboles Los enunciados, junto con las soluciones a los ejercicios seleccionados, los enlaces a las páginas de las herramientas y demás material de utilidad están publicados en la página de la asignatura [3]. 4.2. Resultados de la experiencia docente La asignatura PL1 es cuatrimestral y se imparte en el primer cuatrimestre. El curso 2004-05 ha sido el primero en el que hemos utilizado ANTLR en las prácticas de PL1 y podemos catalogar como muy satisfactoria esta primera experiencia. Hasta ahora utilizábamos flex y bison para generar analizadores léxicos y sintácticos, y un par de herramientas desarrolladas en nuestro departamento para la construcción de árboles de sintaxis abstracta y la especificación de recorridos. Todas ellas generaban programas en C y éste era por tanto el lenguaje elegido para desarrollar los procesadores de lenguajes. La mayor ventaja del uso de ANTLR es sin duda la homogeneidad. No es lo mismo trabajar con distintas herramientas que han sido implementadas por separado, y que por tanto plantean muchos problemas de coordinación entre ellas, que trabajar con una herramienta integral en la que las interfaces entre módulos están totalmente resueltas. La otra gran ventaja es la cobertura, ANTLR cubre todas las etapas de un procesador de lenguaje, llenando de forma natural el hueco que queda cuando sólo se usan herramientas que dan soporte a las etapas léxica y sintáctica. Todo esto hace que resulte bastante cómodo especificar un procesador de lenguaje con esta herramienta incluso en los casos más complejos, y prueba de ello es que ANTLR está

siendo cada vez más usada en el desarrollo de estas aplicaciones. Según afirman los propios autores, la media de descargas mensual es de 5000, una cifra nada desdeñable teniendo en cuenta que el colectivo de usuarios que demanda este tipo de herramientas es relativamente reducido. Tras haber puesto en marcha por primera vez este conjunto de prácticas y observar cómo los alumnos han ido asimilando los distintos elementos de teoría y laboratorio nuestras principales sensaciones son las siguientes: - El uso del modelo descendente para los

analizadores resulta un poco chocante en la etapa de análisis léxico, pero después se acepta con naturalidad ya que pasar al análisis sintáctico y de árboles sólo requiere la adaptación de la misma idea a problemas distintos.

- Los predicados sintácticos son considerados como una idea interesante y ayudan al alumno a reflexionar sobre la ambigüedad en el análisis sintáctico.

- La equivalencia entre símbolo (en la gramática) y método (en el modelo de ejecución Java) hace que comprender los conceptos de atributos sintetizados y heredados sea más sencillo ya que en todo momento el símil del parámetro como atributo les sirve de referencia.

- La notación para especificar la construcción de árboles de sintaxis abstracta es asimilada con facilidad y hasta cierto punto sorprende como con tan poco se puede expresar tanto.

En lo tocante al lenguaje de desarrollo, el uso de Java ha supuesto una mejora importantísima, especialmente en cuanto al tiempo empleado en la depuración de programas. En nuestra experiencia anterior con el lenguaje C comprobábamos constantemente que la combinación de un lenguaje poco disciplinado como C, con la generación automática de programas suponía una mezcla explosiva. Encontrar un error en alguno de los fuentes en juego era una tarea ardua. La disciplina a la que Java obliga ha aliviado considerablemente esta tarea. Tanto la integración de todas las etapas (léxica, sintáctica y semántica) en una única herramienta como el uso de Java como lenguaje de desarrollo han facilitado considerablemente el desarrollo de un procesador de lenguaje. La

Page 10: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

330 Fundamentos teóricos de la informática

impresión que nos queda tras un cuatrimestre de experiencia es que los alumnos deben dedicar menos tiempo a resolver los problemas que le plantea el entorno de desarrollo y pueden por tanto dedicar más tiempo a lo realmente importante: comprender el lenguaje que se pretende procesar y especificar el reconocedor de la manera más abstracta y segura posible. En una encuesta realizada a los alumnos al final del cuatrimestre, el uso de la herramienta ANTLR en el laboratorio ha sido muy bien valorado, este dato es especialmente importante teniendo en cuenta que en encuestas de años anteriores este aspecto era de los que más críticas recibía. Una opinión que nos interesaba conocer especialmente era la de los alumnos repetidores. Ellos conocen el método antiguo y pueden comparar de una forma, en ciertos aspectos, más objetiva que los profesores que promueven el cambio. En este sentido, los alumnos repetidores consultados opinan que la asignatura ha ganado con la nueva herramienta, excepto que para ellos dicho cambio les ha supuesto tener que ''estudiarse'' algo nuevo. 5. Conclusiones En este trabajo hemos presentado nuestra experiencia docente con el uso de ANTLR como herramienta para las prácticas de laboratorio de la asignatura Procesadores de Lenguaje. Después de un curso con ella estamos en condiciones de aconsejar su uso por todas las razones que hemos ido desgranando a lo largo del artículo. Tras utilizar varias herramientas relacionadas con el procesamiento de lenguajes, en ANTLR hemos encontrado muchas soluciones que la convierten en una alternativa muy seria para la construcción de compiladores. Nuestra experiencia aún está a la mitad, ya que en nuestro plan de estudios la materia troncal Procesadores de Lenguaje está distribuida en dos asignaturas: PL1 y PL2. Evidentemente el cambio de herramienta también afecta a PL2, en ella profundizaremos en el proceso de compilación de lenguajes de programación y estudiaremos en detalle cada una de sus etapas. Desde la comprobación de la semántica estática a la generación de código que se realizará sobre la máquina virtual de Java con la ayuda del ensamblador simbólico Jamaica. Cuando en julio

de 2005 finalicemos el segundo parcial habremos completado la primera iteración de nuestra experiencia. Pero por ahora, y tras haber finalizado PL1, podemos decir que el camino elegido no nos está defraudando. Bibliografía [1] Aho, A.V., Sethi, R., Ullman, J.D.,

Compiladores. Principios, técnicas y herramientas, Addison-Wesley, 1990.

[2] Fischer, C.N., LeBlanc, R.J., Crafting a Compiler with C, Benjamin Cummings, 1991.

[3] Página de la asignatura Procesadores de Lenguaje 1, http://www.lsi.us.es/~troyano /mat_pl1.htm

[4] Página oficial de la herramienta ANTLR, http://www.antlr.org/

[5] Página oficial del entorno de trabajo Eclipse, http://www.eclipse.org/

[6] Parr, T., ANTLR Reference Manual. 2.7.4, disponible en http://www.antlr.org/, 2004.

[7] Sippu, S., Soisalon-Soininen, E., Parsing Theory, Springer Verlag, 1988.

[8] Watt, D.A., Brown, D.F., Programming Language Processors in Java, Prentice Hall, 2000.

Page 11: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

Autómatas de Pila y Máquinas de Turing Estructurados

Jairo RochaDpto. Ciencias Matemáticas e Informática

Universidad de las Islas Baleares07122 Palma de Mallorca

e-mail: [email protected]

Resumen

Presentamos una sintaxis simple para definir máqui-nas de Turing y autómatas de pila indeterministas queevita el uso de estados y, en su lugar, usa listas de ins-trucciones y bucles. De esta manera, una máquina deTuring es un programa con instrucciones y bucles es-tructurados, y un autómata de pila es un programacon una pila. Se persiguen dos objetivos: el primero,acercar los resultados de autómatas a la práctica dia-ria de programación estructurada y, el segundo, sim-plificar las demostraciones apelando a la intuición delestudiante conocedor del potencial de la programa-ción moderna.

El resultado es un mayor interés y confianza de losestudiantes en el estudio de estos temas.

1. Introducción

Aunque el concepto de máquina que ejecuta transi-ciones de estados y datos es la esencia de la infor-mática, la sintaxis del lenguaje de programación quedefinió Turing no se ha utilizado nunca en los progra-mas reales. El motivo por el cual hay que esforzarsepara encontrar libros de texto de autómatas formalesque no usen máquinas de Turing es por su formalis-mo simple que hace las demostraciones más cortasque si se hicieran sobre lenguajes más complejos.

El hecho de que los programas de ordenador seanel pan de cada día de los informáticos tiene diver-sas consecuencias sobre la forma como se deberíanpresentar los contenidos sobre programas formales.Primero, el objetivo no es desarrollar estrategias deprogramación en el lenguaje de las máquinas de Tu-ring, o, en cualquier otro lenguaje, porque el interésno es la programación. Segundo, con el fin de notener que escribir programas largos, complicados ypoco interesantes para resolver ciertos problemas decodificación, traducción o ejecución de programas,simplemente se dice que es posible construir progra-mas formales que los resuelvan y se le pide al lector

que se lo crea, todo basándose en sus conocimientosde programación. En tercer lugar, una vez introduci-da una sintaxis de los programas formales, se amplía,con el fin de que el estudiante pueda usar un forma-lismo de más alto nivel (justificándolo pero sin de-mostrar en detalle su equivalencia). Y, finalmente, lapresentación será guiada por el objetivo de establecerlímites de la computación y no por la programaciónen sí misma.

Por lo tanto, evitando demostraciones tediosas quese saben ciertas también empíricamente porque ha-cen que los ordenadores funcionen cada día, y usan-do una sintaxis moderna, es posible dar unos conte-nidos teóricos fácilmente asimilables a los programasde hoy en día en el área de indecidibilidad.

De igual forma, el uso de estados en los autómatasde pila recuerda el uso de losgo-to s y se hace ne-cesario un esfuerzo para traducir un autómata de pilaa un programa moderno. Por lo tanto, se sugiere de-finir autómatas de pila como programas restringidosy con una pila, heredando así, el estructuralismo delos programas, y acercando los teoremas y los resul-tados a su uso concreto. Este enfoque junto con otrossobre la enseñanza de la verificación de autómatas sedesarrolla en nuestro libro [4].

2. Programas formales

Comenzamos definiendo un programa simple comouna secuencia de instrucciones que actúan sobre unpuntero de un vector de símbolos que tiene tantasposiciones como sea necesario (que corresponde alcabezal de la cinta semi-infinita de una máquina deTuring).

2.1. Definiciones

Las instrucciones fundamentales son leer el con-tenido del vector en la posición del puntero, escribiren esta posición y cambiar la posición del punteroa izquierda o a derecha. Las instrucciones compues-

Page 12: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

332 Fundamentos teóricos de la informática

tas son el condicional y la repetición de instruccio-nes que dependen de una condición. Las condicionesson la igualdad de símbolos o su negación. En unaejecución, el vector contiene al principio la entradacompuesta por símbolos y termina con la marca delfin de entrada que es el símbolo distinguido# quenunca forma parte de la entrada y al que llamaremosblanco. Estos símbolos más otros símbolos predefi-nidos pueden aparecer en el programa. Además delvector de símbolos, hay una variable que contiene unsímbolo y que se utiliza para dar el resultado del reco-nocimiento: un 1 significa que la palabra de entradaes aceptada.

Definición 1 (Programa). SeaΣ un alfabeto (de en-trada) finito que no contiene# y Γ un alfabeto (delvector) finito de símbolos que contieneΣ∪{0, 1, #}.Un programaP esP = (Σ, Γ, p) dondep es unacadena producida por la gramática(V, T, R, Prog),donde

V = {Prog, Bloq, Ins, Cd, Cdno, Rept,Term, Exp}

T = {acepta, der, ent, escr, izq, haga, fin,fmientras, fsi, id, leer, mientras,programa, si, si_no, σ, ‘, ‘, (, ), =, /=}

y el conjunto de produccionesR es

Prog → programa id Bloc finBloq → Ins Bloq | λ

Ins → der(ent) | izq(ent) | Cd | Rept| escr(ent,T erm)| escr(acepta,T erm)

Cd → si Exp ent Bloq Cdno fsi

Cdno → si_no Bloq | λRept → mientras Exp haga

Bloq fmientras

Exp → Term = Term | Term /= TermTerm → σ | leer(ent) | leer(acepta)

donde id puede ser reemplazada por una palabracualquiera que identifica el programa yσ por un ele-mento del alfabeto del vector,Γ.

La anterior definición hace uso de las gramáticas,lo que da un factor de motivación adicional a otrotema de la asignatura.

A cada instrucción básica, a cada condición deuna instrucción condicional o de repetición, a cadasi_no, fsi, fmientras y fin se le puede

asignar un único número de orden en la lista de ins-trucciones del programa. Este número determina laposiciónde una única instrucción en el programa.

Ejemplo 2. Consideremos el programa con alfabe-tos Σ = {0, 1} y Γ = {0, 1, #} y las siguientesinstrucciones con las correspondientes posiciones:

programa pordos(ent)1 mientras leer(ent) /= # haga2 der(ent)3 fmientras4 izq(ent)5 mientras leer(ent) /= # haga6 si leer(ent) = 0 ent7 der(ent)8 escr(ent,0)9 si_no10 der(ent)11 escr(ent,1)12 fsi13 izq(ent)14 izq(ent)15 fmientras16 der(ent)17 escr(ent,0)18 escr(acepta,1)19 fin

El programa anterior multiplica un número binariopor 2 si los bits menos significativos están a la iz-quierda.

En un momento determinado de la ejecución deun programa, la posición de la próxima instrucción aejecutar es conocida. La instrucción cambia la posi-ción del puntero, el contenido del vector, el contenidode la variable o una expresión es evaluada. Después,la posición de la próxima instrucción queda conoci-da y el proceso se repite. En resumen, la estructurade datos y las instrucciones básicas de los programasson las mismas que las de una máquina de Turing.

Definición 3 (Configuración). Una configuraciónde un programaP sobre un alfabetoΓ es una cuá-drupla

(n, α, i, r) ∈ N× Γ∗ × N× Γ

formada por la posiciónn en el programa, el conte-nidoα del vector, la posicióni del puntero y el valorr de la variableacepta .

Page 13: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 333

En general, identificamos todas las configuracio-nes que sólo difieren en el número de blancos a laderecha de la segunda componente. La posición cerodel vector siempre tiene un# que no se puede cam-biar.

Dado el programaP y la configuración(n, α, i, r)es posible saber la próxima instrucción a ejecutar, elcontenido no blanco del vector, la posición del pun-tero en el vector y el valor de la variableacepta . Ladescripción intuitiva de lasemánticade un programadada antes se traduce formalmente en una relación`de derivación directa sobre el conjunto de sus confi-guraciones, que no daremos aquí por falta de espacio.Indicamos con∗ la clausura reflexiva y transitiva de` y, cuandoC ∗ C′, decimos queC′ derivadeC.

Ejemplo 4. Consideremos el programa con alfabetoΓ = {0, 1, #} y las siguientes instrucciones con lascorrespondientes posiciones:

programa ceroporuno(ent)1 mientras leer(ent)=1 haga2 der(ent)3 fmientras4 mientras leer(ent)=# haga5 der(ent)6 fmientras7 si leer(ent)=0 ent8 escr(ent,1)9 der(ent)10 fsi11 mientras leer(ent)=1 haga12 der(ent)13 fmientras14 si leer(ent)=# ent15 izq(ent)16 fsi17 mientras leer(ent)=1 haga18 izq(ent)19 fmientras20 si leer(ent)=# ent21 der(ent)22 escr(acepta,1)23 fsi24 fin

Entonces, por ejemplo, tenemos la derivación si-

guiente:

(1, 11, 1, 0) ` (2, 11, 1, 0) ` (3, 11, 2, 0)` (1, 11, 2, 0) ` (2, 11, 2, 0)` (3, 11, 3, 0) ` (1, 11, 3, 0)` (4, 111, 3, 0) ` (5, 111, 3, 0)` (6, 111, 4, 0) ` (4, 111, 4, 0)` (5, 111, 4, 0) ` · · ·

que nunca se para.

Con esta definición de derivación podemos definirun programa que siempre se para, un lenguaje acep-tado por un programa y lenguajes decidibles y semi-decidibles.

2.2. Programas multivector

Con el fin de facilitar el uso de los programas, enesta sección se introduce un formalismo más ampliopara los programas que permite programar con másfacilidad: losprogramas multivector.

Como su nombre lo indica, los programas multi-vector disponen de diversos vectores con un rangotan amplio como se necesite, cada uno de los cualestiene su propio puntero de lectura-escritura. El pro-grama puede leer cada uno de los vectores, escribirun símbolo en cada uno de los vectores y desplazar elpuntero de cada vector de forma independiente. Poresto, el primer parámetro de las operaciones sobrevectores es variable e identifica al vector.

Además, el programa tendrá un encabezado de laforma

programa nomprog(ent1,ent2,...,entm)v1,v2,...,vn:vector

en el queent1 es el nombre del vector donde se en-cuentra la primera palabra de entrada,ent2 , dondese encuentra la segunda, etc., yv1, v2, ... sonnombres de vectores internos. El número de vecto-res del programa es el número de vectores de entradamás el número de vectores internos.

Ejemplo 5. Queremos dar un programa que, reci-biendo una palabraw ∈ {0, 1}∗ como entrada, escri-ba la palabraww en el mismo vector de entrada.

Con este fin, usaremos un programa de dos vec-tores que primero copiará el contenido del vector deentrada al segundo vector, y a continuación copiaráel contenido del segundo vector a la derecha del con-tenido del primer vector. El programa es

Page 14: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

334 Fundamentos teóricos de la informática

programa doblar(w)v: vectormientras leer(w) /= # haga

escr(v,leer(w))der(w)der(v)

fmientrasizq(v)mientras leer(v) /= # haga

izq(v)fmientrasder(v)mientras leer(v) /= # haga

escr(w,leer(v))der(w)der(v)

fmientrasizq(w)mientras leer(w) /= # haga

izq(w)fmientrasder(w)escr(acepta,1)fin

Notemos que esta solución es conceptualmentemás simple que la que podríamos dar si únicamen-te usáramos un vector.

Ejemplo 6. Más adelante usaremos el siguiente pro-grama que copia el contenido del primer vector al se-gundo y aparca los punteros (los deja en la primeracasilla):

programa copiar(ent,copia)mientras leer(ent) \= # haga

escr(copia,leer(ent))der(ent)der(copia)

fmientrasizq(ent)izq(copia)mientras leer(ent) \= # haga

izq(ent)izq(copia)

fmientrasder(ent)der(copia)

fin

Como antes, se puede definir una configuración

que en este caso contendrá información sobre cadauno de los vectores.

Para demostrar que los lenguajes que pueden serreconocidos con programas multivector son exacta-mente los semidecidibles, es necesario traducir unprograma en uno de sólo un vector. La traducciónconsiste en definir una estructura de datos sobre unvector que pueda simular el conjunto finito de vecto-res de un programa multivector dado. Aquí preferi-mos apelar a la intuición y la experiencia en progra-mación de los estudiantes, y pedirles que se conven-zan que es posible definir instrucciones para un vec-tor que traduzca cada instrucción del programa mul-tivector en instrucciones sobre el vector único (estatraducción es automática). Además, el nuevo progra-ma de un solo vector debería dejar, al detenerse, uncontenido sobre el vector análogo al que deja el pro-grama original sobre el primer vector y los dos debe-rían aceptar exactamente las mismas entradas.

La demostración formal de la equivalencia entrelos programas multivector y los de un único vector esmuy larga, y además creemos que poco interesante,así que se omite. De hecho, se trata de un ejercicio derepresentación de una estructura en otra más simple,y con seguridad el estudiante tendrá la oportunidadde hacer ejercicios de este tipo más útiles en un cursode estructuras de datos. El estudiante interesado queno quiera, o no pueda, hacer la demostración rigurosasolo, la encontrará en muchos libros de autómatas ylenguajes formales como, por ejemplo, [1].

2.3. Programas que llaman otros programas

Para simplificar los programas, se utiliza de mane-ra sistemática un recurso que nos permite el uso deun programa dentro de otro programa. Este recursose corresponde, en el mundo de los lenguajes de pro-gramación, a cuando una rutina llama otra rutina; ladefinición dada aquí es más simple.

Suponemos que el programa llamadorutina2en la posiciónn tiene la llamada

rutina1(v1,...,ve)

dondev1,...,ve son vectores diferentes del pro-gramarutina2 . Si el programarutina1 tiene elencabezado

programa rutina1(x1,...,xe)y1,...,ym:vector

Page 15: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 335

entonces lallamada es una abreviación de insertaren la posiciónn todas las instrucciones (excepto elfin ) del programarutina1 donde previamentese han reemplazado todas las ocurrencias de cadauna de las variablesx1,...,xe por v1,...,ve ,respectivamente; además, todas las ocurrencias dey1,...,ym han de ser reemplazadas por otro nom-bre si hay un vector derutina2 que tenga el mis-mo nombre; finalmente, se supone que los vectoresy1,...,ym (con su nuevo nombre si es necesario)son declarados al programarutina2 .

En realidad, con la definición anterior, las llama-das no se hacen en el momento de la ejecución sinoque representan un cambio en el programa que hacela llamada. Además, todos los vectores de entrada delprograma llamado son compartidos por los dos pro-gramas; la variableacepta también es compartida.

2.4. Lenguajes indecidibles

En esta sección mostraremos cómo se definen pro-blemas que no se pueden resolver mediante progra-mas.

Como escribiremos programas que tienen por en-trada otros programas y ésta tiene que ser una pala-bra binaria, tenemos que codificar los programas me-diante palabras sobre{0, 1}; notemos que los pro-gramas de ordenador reales ya están codificados enbinario usando el código ASCII. Se usa aquí lamis-macodificación y<P> denota la palabra binaria querepresenta el programaP .

La codificación de expresiones por números fue unconcepto muy nuevo hace setenta años, y tiene el ori-gen en la codificación de Gödel de fórmulas aritméti-cas por medio de números. Hoy en día, no sólo es quelos programas son códigos, sino que estamos acos-tumbrados a que toda la información sobre personas,aviones, relaciones, imágenes, sonido, etc., esté co-dificada por números, porque de otra manera no sepodría introducir en el ordenador.

Tenemos el resultado siguiente que muestra que noes posible decidir si la ejecución de un programa da-do sobre una palabra dada se parará o no, es decir, elproblema de la parada no es decidible.

Teorema 7. El lenguaje de la parada

Lp = {<P> &w | P se para con entradaw}

no es decidible.

Demostración.La idea de la demostración es supo-ner que existe un programa llamadosePara(p,w)(el primer parámetro son los bits antes del &, y elsegundo, los de después) que se para siempre y reco-noce el lenguaje de la parada, y emplearlo para cons-truir un programaprueba(q) con el queseParano funciona bien, lo que nos lleva a una contradic-ción.

Sea, entonces, el siguiente programa:

programa prueba(q)q1:vector

copiar(q,q1)sePara(q,q1)si leer(acepta) \= 1 llavors

escr(acepta,1)si_no

mientras 0 = 0 hagafmientras

fsifi

Suponemos que llamamosprueba(<prueba>)(es decir,q contiene<prueba > como entrada).

Si sePara(<prueba>,<prueba>) aceptala entrada, entoncesprueba(<prueba>) en-tra en un bucle infinito. Esto quiere decir queprueba(<prueba>) no se para y, por tanto,sePara(<prueba>,<prueba>) no debería ha-ber aceptado.

Entonces, sePara(<prueba>,<prueba>)no acepta la entrada pero se pa-ra ya que siempre se para; entonces,prueba(<prueba>) acepta la entrada y separa, ysePara(<prueba>,<prueba>) deberíahaber aceptado.

Como ambos casos son imposibles, el programasePara(p,w) no puede existir.

3. Autómatas de pila

Informalmente, un autómata de pila es un progra-ma con un vector,ent , para la entrada y un vector,pila , para hacer operaciones con una pila. Los autó-matas de pila son programas restringidos: primero, elvector de entrada no puede ser cambiado ni el punte-ro se puede mover a la izquierda; segundo, el vectorde la pila puede ser manipulado únicamente con lasoperaciones definidas más adelante que impiden leerlas casillas a la derecha del puntero.

Page 16: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

336 Fundamentos teóricos de la informática

Los tres objetivos para enseñar autómatas de pi-la son: aprender a usar autómatas de pila, hacer unreconocedor de programas que es útil a la hora de es-cribir programas queinterpretanprogramas (progra-mas universales), y ver más aplicaciones prácticas deprogramas.

Definición 8 (Autómata de pila). Un autómata depila se define como un programa de dos vectores, elde entrada y el de la pila. Se permiten solamente dosoperaciones en el vector de entrada:leer(ent) yder(ent) ; en el vectorpila se permiten las ope-raciones básicas siguientes:

vacía() que tiene el valor 1 si la pila está vacía,

cima() que tiene el valor de la cima de la pila y no lamodifica,

desapila() que desapila el símbolo de la cima de lapila, y

apila_pal(w) que añade a la pila cada uno de lossímbolos de una palabraw de manera ordena-da, dejando el primer símbolo de la palabra a lacima de la pila.

Además, permitimos dos extensiones de la sintaxis:

1. las condiciones de las instruccionessi ymientras pueden contener operadores deconjunción y disyunción, y

2. pueden haber otras variables de una sola casi-lla que se pueden leer y modificar de la mismaforma que la variableacepta .

Las últimas extensiones nos permiten escribir pro-gramas más claros aunque se podrían simular usandola variableacepta con muchos símbolos que repre-senten otras variables y resultados de combinacionesde expresiones.

Si la pila es vacía, el valor decima es # ydesapila no tiene ningún efecto.

Las configuraciones de programas nos permitenrepresentar los contenidos de la entrada y de la pila.Por ejemplo, con la instrucciónapila_pal( abc)tenemos el paso

(n, abcde, 3, Zfg, 3, 0) `(n + 1, abcde, 3, Zfgcba, 6, 0)

ya que la cima de la pila la representamos a la posi-ción más a la derecha. Es decir,cima() tiene el va-lor a en la última configuración, ydesapila() de-riva a la configuración(n+2, abcde, 3, Zfgcb, 5, 0).

Ejemplo 9. Consideremos el lenguaje

L2 = {w2wt | w ∈ {0, 1}∗}.Un programa con pila para este lenguaje es:

programa rec_w2wt(ent)pila:vector

escr(acepta,1)mientras leer(ent) /= 2

y leer(ent) /= # hagaapila_pal(leer(ent))der(ent)

fmientrassi leer(ent) = 2 ent

der(ent)si_no

escr(acepta,0)fsimientras vacía() = 0

y cima()=leer(ent) hagadesapila()der(ent)

fmientrassi vacía() = 0 o leer(ent) \= # ent

escr(acepta,0)fsi

fin

Con el fin de definir automatas de pila no determi-nistas definimos instrucciones no deterministas, unaintroducción sencilla al paralelismo.

Definición 10 (Programa indeterminista). Un pro-grama no determinista, o indeterminista, es un pro-grama donde se permiten instrucciones de la forma

paraleloS1 con S2 con ... con Sm

fparalelo

dondeS1, S2, ..., Sm son instrucciones.

La semántica intuitiva es que una configuraciónque ejecuta un bloque de paralelismo se convierte enm configuraciones independientes, todas iguales endatos excepto por la posición de la siguiente instruc-ción a ejecutar: cada configuracióni-ésima comienza

Page 17: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 337

en la posición deSi. Estas configuraciones generanm hilos de ejecución independientes y, como es cos-tumbre en los autómatas indeterministas, si uno deellos acepta la entrada se dice que el programa acep-ta la entrada.

Ejemplo 11. Queremos construir un programa queimplemente un autómata de pila para el lenguaje dela gramática

P → pBfB → IB | λI → i | sBg | mBh.

Esta gramática es una simplificación de la de los blo-ques de un programa y por eso tiene gran importan-cia.

Usando el teorema que permite construir un autó-mata de pila no determinista para una gramática[4, 1], tenemos el siguiente programa indeterminis-ta. Una variable de la cima de la pila se reemplazapor cada una de sus posibles partes derechas. Hay unhilo de ejecución diferente e independiente para cadaposible reemplazo.

programa recProg(ent)pila:vector

1 escr(acepta,1)2 apilar(P)3 mientras vacía()=0 y

leer(acepta)=1 haga4 si cima() = P ent5 desapila()6 apila_pal(pBf)7 si_no8 si cim() = B ent9 desapila()10 paralelo11 apila_pal(IB)12 con13 fparalelo14 si_no15 si cim() = I ent16 desapila()17 paralelo18 apila_pal(i)19 con20 apila_pal(sBg)21 con22 apila_pal(mBh)23 fparalelo

24 si_no25 si cim() en {p,f,i,s,g,m,h} ent26 si cim() = leer(ent) ent27 desapila()28 der(ent)29 si_no30 escr(acepta,0)31 fsi32 fsi fsi fsi fsi36 fmientras37 si leer(ent) /= # ent38 escr(acepta,0)39 fsi40 fin

donde elcon de la instrucción 12 no hace nada por-que no se tiene que apilar nada en el casoB → λ.

Aunque es una solución simple y directa basadaen la gramática es muy ineficiente por el no deter-minismo. En este caso, es posible dar un programadeterminista que reconozca el lenguaje haciendo unaobservación importante: si se puede determinar cuálde las producciones se tiene que usar, en función dela entrada, entonces el bloque de paralelismo se pue-de evitar.

Nos detenemos aquí esperando haber convencidoal lector de la facilidad de entendimiento que progra-mas como los anteriores tienen, en contraste con lastransiciones con estados las cuales requieren un do-ble esfuerzo.

4. Discusión

Hemos definido equivalentes estructurados de má-quinas de Turing, máquinas de Turing indeterminis-tas y autómatas de pila. Las definiciones permiten darlos mismos resultados teóricos tradicionales pero conla motivación adicional de ser más accesibles para losestudiantes debido a su carácter estructurado.

Estos conceptos se han usado por primera vez enuna asignatura de autómatas y lenguajes formales deeste año. El resultado ha sido interesante: los estu-diantes están activos y atentos a hacer comentariossobre mejoras al programa o dudas sobre las ope-raciones; los estudiantes se sienten seguros al hacerpreguntas y comentarios. Además, durante la demos-tración de la indecidibilidad del problema de la para-da, la contradicción les parecía que se debía a una es-pecie de error de programación superable porque no

Page 18: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

338 Fundamentos teóricos de la informática

veían ningún problema en los programas. Es decir,la demostración es tan directa que salta a la vista lacontradicción y choca con la intuición. Todo esto encontraste con su pasividad tradicional cuando se hacela demostración numerando máquinas de Turing.

Existen varios libros y artículos que hacen inten-tos de dar versiones para programadores de la teoríade la computación. Por ejemplo, Morales y otros [3]definen un máquina contadora estructurada; el pro-blema de usar enteros positivos como estructura bá-sica es que el manejo de programas codificados noes natural; además, no es un buen modelo en teoríade la complejidad porque no es natural suponer queen un paso se puede incrementar un entero. El me-jor libro con un enfoque moderno es, sin duda, el deJones [2]; no es, sin embargo, un libro de autómatassino de teoría de la computación en cuyo prefacio seapuesta por un enfoque a la computabilidad que evi-te las máquinas de Turing y las funciones primitivasrecursivas y use las estructuras de datos modernas deLisp y la semántica denotacional de programas.

No conocemos ningún libro que presente los resul-tados teóricos de autómatas de pila con un enfoqueestructurado como el dado aquí.

Referencias

[1] J. Hopcroft, R. Motwani, J. Ullman.Introduc-ción a la teoría de autómatas, lenguajes y com-putación.Addison-Wesley, 2002.

[2] N. Jones.Computability and Complexity: Froma Programming Perspective, MIT press, 1997.

[3] R. Morales et al.Una alternativa docente a lamáquina de Turing, Memorias de JENUI 2003,pp 249-258, Ed. Thomson.

[4] J. Rocha, F. RosellóAutòmatas, Gramàtiques iProgrames: Verificació i Concurrència, Mate-rials Didàctics, UIB, 2a ed. en imprenta, 2005.

Page 19: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

SOTA, una herramienta educativa para la enseñanza de la tabla de símbolos

Gallego Carrillo, Micael, Gortázar Bellas, Francisco, Urquiza Fuentes, Jaime y Velázquez Iturbide, J. Ángel

Universidad Rey Juan Carlos, Grupo de investigación VIDO, C/ Tulipán s/n, 28933 MADRID [email protected], {francisco.gortazar,jaime.urquiza,angel.velazquez}@urjc.es

Resumen Se presenta una herramienta educativa para el aprendizaje de la tabla de símbolos, dentro del marco de una asignatura de enseñanza de compiladores. La herramienta muestra de forma visual el funcionamiento de una tabla de símbolos durante el proceso de análisis de un código fuente. Planteamos también una propuesta de evaluación de la herramienta que se llevará a cabo en el segundo semestre del presente curso académico dentro de la asignatura de Procesadores de Lenguajes de la titulación de Ingeniería Informática en la Universidad Rey Juan Carlos.

1. Introducción

La visualización es un recurso ampliamente utilizado en la enseñanza de la informática. Una de las primeras experiencias en este ámbito fue Sorting Out Sorting [2], una animación de 30 minutos que visualiza el funcionamiento de diferentes algoritmos de ordenación. Existen múltiples estudios sobre la utilización de la visualización en la enseñanza de la informática [1, 5, 6, 11, 12, 16]. Actualmente existen herramientas que visualizan tanto la propia ejecución de programas [8, 9, 10, 14, 15] como estructuras de datos y algoritmos [4].

En el ámbito de la construcción de compiladores, y en general en cualquiera de los tipos de procesadores de lenguajes, también se han desarrollado numerosas herramientas que visualizan la ejecución de procesos relevantes en este área. Entre ellas se pueden mencionar algunas aproximaciones iniciales, como [7] y [13], y herramientas que aún cuentan con un activo desarrollo, como JFlap[3] y Jaccie[17].

La enseñanza de procesadores de lenguajes se suele dividir en dos partes claramente diferenciadas. Una parte de análisis léxico y

sintáctico del código fuente que se realiza mediante técnicas basadas en la teoría de lenguajes formales. Y otra parte que engloba procesos adicionales como por ejemplo el uso de tablas de símbolos, técnicas de unificación de expresiones de tipos, técnicas de generación de código o técnicas de optimización de código. Las herramientas mencionadas anteriormente se centran en la parte de análisis léxico y sintáctico que está basada en la teoría de lenguajes formales, como por ejemplo algoritmos de transformación de autómatas no deterministas en deterministas, reconocimiento de cadenas de entrada con diferentes mecanismos dependiendo del tipo del lenguaje o simuladores de máquinas de Turing.

En la literatura relacionada no se mencionan herramientas con fines educativos que visualicen otras fases del proceso de compilación, como la utilización de la tabla de símbolos.

En este contexto se presenta SOTA, una herramienta educativa sencilla que se centra en la visualización de alto nivel del funcionamiento de una tabla de símbolos en el proceso de análisis del código fuente.

En la siguiente sección se describen los conceptos teóricos de la tabla de símbolos. La sección 3 muestra cómo SOTA visualiza, tanto estática como dinámicamente, dichos conceptos. La sección 4 expone una discusión sobre la utilidad pedagógica y se describe cómo se evaluará la herramienta. Finalmente, en la sección 5, se describen nuestras conclusiones y líneas de trabajo futuro.

2. Enseñanza de la Tabla de Símbolos

En esta sección se muestra un resumen de los conceptos teóricos de la tabla de símbolos. SOTA se ha diseñado para apoyar la explicación y el aprendizaje de estos.

Page 20: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

340 Fundamentos teóricos de la informática

En el proceso de compilación de un programa, la comprobación de tipos se encarga de asegurar que los elementos del lenguaje se utilizan correctamente (su correcta escritura y ubicación ya ha sido comprobada por los analizadores léxico y sintáctico respectivamente). Los elementos de los lenguajes de programación para los que se necesita hacer este tipo de comprobación son los identificadores.

Las principales características de los identificadores son: su nombre, y su ámbito de visibilidad y tipo, que indican respectivamente en qué lugar del programa se pueden usar y qué operadores se puede aplicar sobre ellos. El nombre de un identificador se encuentra en el propio token. Sin embargo, el ámbito de visibilidad y el tipo son datos que se encuentran asociados al identificador en una sentencia de declaración, que está compuesta por varios tokens agrupados en una o varias producciones de la gramática. Por lo que se necesita una estructura de datos donde almacenar toda esa información relativa a los identificadores.

La tabla de símbolos es esa estructura de datos, y la información a guardar es muy variada. Un identificador puede desempeñar diferentes papeles en un código fuente, éstos dependen del lenguaje de programación utilizado. Por ejemplo: variables, funciones, procedimientos, clases o tipos definidos por el usuario.

La información de tipo a guardar dependerá del papel desempeñado por el identificador. Si se trata de una variable, bastará con guardar su tipo asociado. Si es un matriz, ya habrá que guardar el tipo de los elementos y las dimensiones de la matriz. Para los procedimientos será necesario guardar los parámetros formales, y para las funciones, además habrá que guardar el tipo del valor devuelto. De los tipos definidos por el usuario se necesita guardar su definición. Por último, de las clases habría que guardar la información de herencia, así como sus atributos y métodos con su correspondiente información de acceso.

El ámbito de visibilidad de un identificador depende del lenguaje de programación utilizado, pero en general se define mediante entornos. Un

entorno es una parte del programa delimitada de alguna forma. Todo el programa está estructurado de forma jerárquica en entornos: el programa en sí es un entorno, formado por tantos entornos como funciones y procedimientos tenga declarados dentro del programa principal. A su vez cada uno de estos entornos estará compuesto por otros resultantes de funciones o procedimientos definidos dentro de ellos (si es que el lenguaje lo permite) o cuerpos de bucles o sentencias de bifurcación, también llamados entornos anónimos, por no tener identificador asociado, como es el caso de funciones y procedimientos. La información del ámbito de visibilidad de un identificador se puede guardar de muchas formas. Una forma podría ser asignando códigos a entornos, guardando el entorno de declaración de un identificador, las relaciones entre los distintos entornos y el entorno activo actual. Otra forma es creando la estructura de la tabla de símbolos a modo de árbol, de forma que la raíz es el entorno principal y sus nodos hijos son los entornos creados a partir de él. Esta última forma es la utilizada en nuestro caso.

Otro punto importante es la manipulación de la tabla de símbolos. Ésta consiste en la creación de entornos, inserción de entradas en dichos entornos, modificación y eliminación de dichas entradas, y localización de entradas en la estructura de la tabla de símbolos. La primera acción en la tabla de símbolos es la creación del entorno raíz. A partir de aquí se insertarán entradas, comprobando los posibles errores de duplicidad; se crearán los entornos hijos correspondientes si procede; se buscarán identificadores en la estructura de la tabla de símbolos, donde entra en juego la información del ámbito de visibilidad; y se accederá a la información de las distintas entradas encontradas.

3. Visualización de la tabla de símbolos con SOTA

Esta sección describe la visualización que hace nuestra herramienta de los conceptos de la tabla de símbolos presentados en la sección anterior.

Page 21: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 341

Figura 1. Interfaz de SOTA

En la Figura 1 se puede ver la interfaz de SOTA. Ésta se divide en tres zonas: zona de programa, zona de estado actual de la tabla de símbolos y zona de mensajes. La zona de programa, parte izquierda de la pantalla, es donde se muestra el programa que se está analizando. Dicho programa se deberá escribir utilizando una variante de Pascal que hemos denominado SimplePascal, cuya descripción se encuentra en la ayuda de la herramienta. La zona de estado actual de la tabla de símbolos, parte derecha superior, es donde se muestra la representación gráfica del estado actual de la tabla de símbolos, así como las últimas operaciones realizadas (inserción, creación de entorno y búsqueda con o sin éxito). La zona de mensajes, parte derecha inferior, muestra una breve descripción textual de las operaciones realizadas hasta el momento sobre la tabla de símbolos.

El/la alumno/a podrá tanto editar directamente en la herramienta sus propios programas, como utilizar un conjunto de programas de demostración que son descargados por la herramienta desde un sitio web, permitiendo al

docente incorporar nuevos programas de demostración cuando lo considere necesario. En la Figura 2 se muestra el cuadro de diálogo que permite seleccionar una de estas demostraciones. Cada una de ellas viene acompañada de un nombre y una descripción del contenido.

Figura 2. Demos utilizables.

Page 22: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

342 Fundamentos teóricos de la informática

A continuación se describirá la visualización de los conceptos de la tabla de símbolos, tanto de forma estática como dinámica.

3.1. Visualización estática

Para representar la estructura de la tabla de símbolos cuando está compuesta por distintos entornos, se utiliza un árbol cuyos nodos son los entornos y cuyos arcos son las relaciones entre entornos padres e hijos. El árbol crece hacia la derecha para los nuevos procedimientos y funciones y hacia abajo para los entornos anónimos. Dentro de cada entorno, las entradas se añaden en la parte inferior. Se puede ver un ejemplo de esta estructura arbórea en la Figura 3. El estado en el que se encuentra la tabla de símbolos en esta figura se corresponde con el punto en el que se procesa el token resaltado en el código fuente de la Figura 4.

Figura 3. Estructura de árbol

Para facilitar la legibilidad de la visualización, es posible aumentar o disminuir el tamaño de la fuente. Esto permite adecuar el tamaño a una presentación para una clase teórica, o disminuirlo cuando al árbol crece más allá de los límites de la zona donde se visualiza.

Los entornos se representan gráficamente como rectángulos. La entradas se representan textualmente dentro del rectángulo asociado al entorno al que pertenecen. En la Figura 3 se pueden observar cinco entornos. El entorno raíz con cinco entradas, tres de ellas son subprogramas. Cada uno de ellos tiene su entorno asociado. En concreto, dentro del procedimiento “proc2” se distinguen, en primer lugar el parámetro “y”, y a continuación las tres variables “b2”, “b1” y “b3”. Finalmente existe un entorno

anónimo definido dentro del procedimiento “proc2” con dos variables (“b21” y “b22”).

Figura 4. Código analizado

Para facilitar el seguimiento de las acciones

realizadas sobre la tabla de símbolos, SOTA destaca tanto el último entorno creado como la última entrada insertada con color azul, de forma que se diferencien del resto. En la Figura 5 se destacan el último entorno creado, que es el correspondiente al cuerpo del procedimiento “proc2” y la última entrada insertada que es la variable “b3”.

Figura 5. SOTA destaca el último entorno creado y la última entrada insertada.

También se destacan las acciones de búsqueda, así como su resultado. El entorno sobre el que se está buscando en cada momento se resalta en color rojo. Si la búsqueda no tiene éxito en ese entorno se marca con una línea diagonal también en rojo, pasándose a buscar en el entorno padre. Si la búsqueda encontró la entrada, ésta se resalta en color verde. En las siguientes tres figuras se presentan las tres posibilidades. En la Figura 6 se ve cómo se está buscando en el

Page 23: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 343 entorno anónimo. En la Figura 7 se muestra que la búsqueda falló en el entorno anónimo, pero se continúa en el entorno padre. Finalmente, en la Figura 8 se visualiza que en el entorno del cuerpo de procedimiento también falló la búsqueda, pero en el entorno raíz se encontró la entrada que se buscaba.

Figura 6. Búsqueda de una entrada en el entorno anónimo.

Figura 7. Búsqueda sin éxito en el entorno

anónimo y traslado de la búsqueda al entorno padre.

Figura 8. Búsqueda sin éxito en el entorno del cuerpo del procedimiento y éxito en la

búsqueda en el entorno raíz.

3.2. Animación

La animación de la estructura de la tabla de símbolos consiste en la representación de las operaciones realizadas sobre ella a medida que se avanza en el proceso de análisis léxico y sintáctico.

Cada paso de la animación corresponde a una acción sobre la tabla de símbolos durante la compilación del programa. En el caso de las acciones de búsqueda, cada paso corresponde a la búsqueda en cada uno de los entornos. El proceso se inicia en el entorno actual y finaliza cuando la entrada es encontrada o en su defecto cuando se llega al entorno raíz.

Para controlar la animación se ofrecen los controles típicos: inicio, finalización, pausa, reproducción y selección de velocidad. Además se permite trasladarse al estado inmediatamente anterior o posterior con los controles “paso anterior” y “paso siguiente”, o a un estado en concreto utilizando una barra de tiempo.

Para permitir que el usuario tenga siempre accesible información sobre las acciones que han ocurrido hasta el momento, se dispone de la zona de mensajes. En esta zona se muestra un mensaje por cada acción realizada sobre la tabla de símbolos, así como su resultado: creación de nuevos entornos, inserción de entradas y búsquedas. Además el usuario podrá elegir qué tipo de mensajes se mostrarán mediante filtros. Hay un filtro definido para cada tipo de acción que se puede realizar sobre la tabla de símbolos, si está seleccionado, se muestran los mensajes correspondientes a ese tipo de acciones, en caso contrario se omiten.

Cuando se selecciona un mensaje se resalta el punto del programa en el que se realizó la acción representada en el mensaje. Por ejemplo en la Figura 9 se puede ver que al seleccionar un mensaje de búsqueda con éxito, se resalta en el programa el token que provocó dicha búsqueda.

Figura 9. Resaltado de partes del programa relacionadas con los mensajes.

Page 24: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

344 Fundamentos teóricos de la informática 4. Discusión

Como ya se ha mencionado, existen múltiples estudios sobre la influencia de la visualización en la enseñanza de la informática. El contenido de la tabla de símbolos cambia durante el proceso de compilación, no es algo estático. Por tanto, disponer de una herramienta que permite mostrar el estado de la misma en cualquier punto del proceso puede ayudar en la comprensión de su funcionamiento.

Durante las explicaciones teóricas, el profesor puede hacer uso de la herramienta. Frente a técnicas tradicionales, como transparencias o pizarra, el uso de una herramienta de estas características permite ampliar las explicaciones en clase con múltiples ejemplos, avanzar o retroceder en el proceso de análisis con un mínimo esfuerzo y centrarse en las explicaciones teóricas de lo que está sucediendo contrastadas con un caso práctico real.

También creemos que es positivo que los alumnos dispongan de una herramienta que les permita afianzar y ampliar sus conocimientos mediante sucesivas pruebas con diferentes programas. Creemos importante que el/la alumno/a disponga, tanto de programas preparados por el profesor y que son accesibles fácilmente a través de la herramienta, como otros que el alumnado desee probar por su cuenta.

Para medir el grado de utilidad y de aceptación entre el alumnado se va a efectuar una evaluación en el actual curso académico enmarcada en la asignatura de Procesadores de Lenguajes de la titulación de Ingeniería Informática de la Universidad Rey Juan Carlos. La evaluación se va a realizar en dos fases: la primera estará compuesta de una sesión experimental de explicación de la tabla de símbolos; la segunda corresponde al análisis de la herramienta durante el resto del curso por parte de los/as alumnos/as.

Para la sesión experimental se dividirá a los/as alumnos/as en dos grupos: el de control y el experimental. La sesión constará de explicaciones teóricas y ejercicios prácticos. En el grupo experimental se utilizará la herramienta tanto para las explicaciones como para los ejercicios, y en el grupo de control se utilizarán los medios típicos como transparencias y pizarra. A ambos grupos se les realizará un test previo, para comprobar sus conocimientos iniciales. Tras la sesión se les

realizará otro test con el objetivo de evaluar la utilidad de la herramienta.

Después de la sesión experimental, también se explicará la herramienta al grupo de control. Finalmente ambos grupos responderán a un cuestionario sobre la usabilidad de la herramienta e impresiones obtenidas del uso de la misma.

5. Conclusiones y trabajo futuro

Hemos desarrollado una herramienta de visualización del funcionamiento de la tabla de símbolos que sirve tanto al profesor para las explicaciones teóricas como al alumno/a para afianzar sus conocimientos y experimentar con ella. El docente puede proporcionar al alumnado múltiples y variados ejemplos que son accesibles a través de la herramienta. Creemos que el hecho de que se presente visualmente el funcionamiento de la tabla de símbolos relacionado con el proceso de análisis del código fuente facilitará la comprensión de los conceptos teóricos.

La herramienta está disponible vía web en la dirección http://vido.escet.urjc.es/sota. Además, se distribuye bajo licencia de software libre GPL.

No obstante, puesto que es necesario comprobar empíricamente la efectividad de la herramienta en los aspectos mencionados, se están ultimando los detalles para la realización de la sesión experimental durante el actual curso académico. Además, en la página web de la herramienta se encuentran disponibles dos cuestionarios que nos permitirán conocer la opinión de los profesores y alumnos que la usen. De esta forma, podemos mantener una evaluación continua de la misma.

En la herramienta se podrían introducir mejoras útiles en el proceso de aprendizaje como ofrecer al alumnado la posibilidad de experimentar con diferentes lenguajes de programación para que observen las diferencias entre ellos. También sería interesante que se pudieran establecer diferentes políticas de la tabla de símbolos, por ejemplo no permitiendo declarar variables con el mismo nombre en entornos anidados. Cabe también generalizar SOTA para que el propio usuario (profesor o alumno) pueda incorporar diferentes lenguajes suministrando los analizadores léxicos y sintácticos y usando una librería que la herramienta proporcionase para usar la tabla de símbolos. Finalmente, se está

Page 25: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica

XI Jornadas de Enseñanza Universitaria de la Informática 345 estudiando la posibilidad de incorporar una nueva visualización de la tabla de símbolos desde el punto de vista de su implementación (por ejemplo mostrando las tablas hash).

Para permitir una mejor evaluación del uso que los/as alumnos/as hacen de la herramienta, sería deseable que pudiera generar un registro del uso de la misma. Esto es especialmente útil durante la sesión experimental de la herramienta con el alumnado, pues permitiría determinar pautas de aprendizaje.

Desde el punto de vista de la implementación, existen muchos aspectos del control de la animación que pueden reutilizarse en otras herramientas similares. Podrían generalizarse estos aspectos creando un conjunto de librerías que permitiera la construcción de las mismas con menos esfuerzo.

Referencias

[1] Anderson, J.M., Naps, T.L. A context for the assessment of algorithm animation systems as pedagogical tools. In Proceedins of the First Program Visualization Workshop (Porvoo, Finland, 2001), pp. 121-130

[2] Baecker, R. Sorting Out Sorting: A Case Study of Software Visualization for Teaching Computer Sceince. In Stasko, J.T. et al. (eds.), Software Visualization, MIT Press, Cambridge, Ma , USA, 1998, pp. 369-381

[3] Cavalcante, R., Finley, T., Rodger, S.H. A Visual and Interactive Automata Theory Course with JFLAP 4.0. Thirty-fifth SIGCSE Technical Symposium on Computer Science Education, 2004, pp.140-144

[4] Chen, T., Sobh, T.M. A tool for data structure visualization and user-defined algorithm animation. In Proceedings of the 31st ASEE/IEEE Frontiers in Education Conference, Reno, US, 2001

[5] Grissom, S. et al. Algorithm Visualization in CS Education: Comparing Levels of Student Engagement. ACM SOFTVIS 2003, pp. 87-94

[6] Hundhausen, C.D., Douglas, S.A., Stasko, J.T. A Meta-Study of Algorithm Visualization Effectivenes. Journal of Visual Languages and Computing, 2002, 13, pp. 259-290

[7] Lovato, M.E., Kleyn, M.F. Parser visualizations for developing grammars with yacc. In Proceedings of the twenty-sixth SIGCSE technical symposium on Computer

science education, Nashville, USA, 1995, pp. 345-349.

[8] Moreno, A. et al. Visualizing programs with Jeliot 3. In Proceedings of the working conference on Advanced Visual Interfaces, Gallipoli, Italy, 2004, ACM Press, pp. 373-376

[9] Naharro-Berrocal, F. et al., Redesigning the animation capabilities of a functional programming environment under an educational framework. In M. Ben-Ari (ed), Proceedings of the second Program Visualization Workshop, Aarhus, 2002, pp. 60-69

[10] Naps, T. et al. JHAVÉ -- An Environment to Actively Engage Students in Web-based Algorithm Visualizations. In Proceedings of the SIGCSE Session, ACM Meetings, Austin, Texas, March 2000, pp. 109-113

[11] Naps, T. et al. Exploring the role of visualization and engagement in computer science education. ACM SIGCSE Bulletin, Junio 2003, 35(2), pp. 131-152

[12] Naps, T. et al. Evaluating the Educational Impact of Visualization. ACM SIGCSE Bulletin, Diciembre 2003, 35(4), pp. 124-136

[13] Resler, R.D. Deaver, D.M. VCOCO: a visualisation tool for teaching compilers. In Proceedings of the 6th annual conference on the teaching of computing and the 3rd annual conference on Integrating technology into computer science education, Dublin City Univ., Ireland, 1998, pp. 199-202.

[14] Rodger, S. H., Using Hands-on Visualizations to Teach Computer Science from Beginning Courses to Advanced Courses. Second Program Visualization Workshop, Hornstrup Centert, Denmark, June 2002, pp. 103-112

[15] Rößling, G. et al. Enhanced Expressiveness in Scripting Using AnimalScript V2. In Proceedings of the Third International Program Visualization Workshop, Warwick, England, 2004, Ari Korhonen (Ed.), pp. 10-17

[16] Stasko, J.T., Lawrence, A. Empirically assessing algorithm animations as learning aids. In Stasko, J.T. et al. (eds.), Software Visualization, MIT Press, Cambridge, Massachusetts, USA, 1998, pp. 419-438.

[17] http://www2-data.informatik.unibw-muenchen.de/Research/Tools/JACCIE/

Page 26: Fundamentos teóricos de la informática - UIBbioinfo.uib.es/~joemiro/aenui/procJenui/Jen2005/Ponencias8.pdf · de compiladores. Se tratan las etapas de comprobación de semántica