Memoria TFC Julio 2002

Anuncio
UNIVERSIDAD POLITÉCNICA DE MADRID
ESCUELA UNIVERSITARIA DE INFORMÁTICA
TRABAJO FIN DE CARRERA
SOBRE LA INFORMACIÓN,
SU ESTRUCTURA Y SU-GESTIÓN
MICAEL GALLEGO CARRILLO
JULIO 2002
LUIS FERNÁNDEZ MUÑOZ
Indice
I. INTRODUCCIÓN ................................................................................ 1
II. MECANISMOS RECURRENTES DE LA ESTRUCTURA DE
LA INFORMACIÓN .................................................................................... 9
1. Comparación de lenguajes y paradigmas ............................................................9
2. Mecanismos de construcción de estructuras de datos en lenguajes de
programación ....................................................................................................................16
3. Representaciones Gráficas ......................................................................................21
4. Mecanismos de construcción de estructuras de datos de otros sistemas de
representación de la información ...................................................................................36
5. Mecanismos de construcción de la estructura de las sentencias en los
lenguajes de programación .............................................................................................41
6. Mecanismos de construcción de los lenguajes de programación .....................43
7. Axiomática de la información ................................................................................45
III. MODELADO DE PASCAL .............................................................. 48
1. Introducción..............................................................................................................48
2. Tiempo de Compilación..........................................................................................49
3. Tiempo de Ejecución ...............................................................................................87
IV. LAS INNUMERABLES VENTAJAS Y EL SUPUESTO
INCONVENIENTE CON SUS INNUMERABLES DESVENTAJAS..... 107
1. Entornos de Desarrollo ...........................................................................................108
2. Procesadores de lenguajes ......................................................................................115
3. Prototipado de lenguajes de programación .........................................................118
4. Ingeniería del Software ...........................................................................................123
5. Asistencia a la educación ........................................................................................127
6. Interfaces gráficos de usuario ................................................................................130
7. La eficiencia, el inconveniente y sus innumerables desventajas.......................131
V. CONCLUSIONES................................................................................ 135
VI. BIBLIOGRAFÍA................................................................................. 137
ANEXO A. FUENTES .............................................................................. 140
ANEXO B. ESPECIFICACIÓN EN JAVACC....................................... 223
ANEXO C. DIAGRAMAS UML............................................................. 231
I. Introducción
En los planes de estudio de las de las ingenierías de informática he
observado muchos aspectos referentes a la programación que resultan curiosos
e interesantes. Desde un punto de vista personal intentaré explicar cuáles son
estos aspectos. El objetivo de la realización de este TFC es poner en orden todos
ellos y aclararlos. Sin embargo, esta tarea es muy ambiciosa así que
intentaremos sentar una base para seguir trabajando en el futuro.
Realizando los primeros cursos de la ingeniería técnica de sistemas siempre
sentí gran atracción por la idea de la visualización del software. Esta rama de la
informática
estudia
cómo
visualizar
los
conceptos
presentes
en
la
programación, tanto en la fase de desarrollo como en la fase de ejecución. A mi
entender muchos conceptos introducidos en las primeras asignaturas de
programación serían bastante más comprensibles y permitirían al alumno una
mayor rapidez de aprendizaje. Ya en ese momento empecé a pensar cómo se
podría construir un sistema que permitiese eso, visualización de código fuente
y un programa en ejecución.
Lamentablemente, en aquellos cursos, mi base teórica y conocimientos
prácticos distaban mucho de los necesarios para plantearme la construcción de
1
Sobre la información, su estructura y su-gestión
un sistema con esas funciones. Tener un sistema de estas características me
permitiría incluir el código fuente en las memorias de las prácticas con un poco
mas de formato que el simple código en texto plano.
La creación de memorias para los programas suponía mucho esfuerzo. La
documentación de un código era una tarea muy tediosa, por la dificultad de ir
generando la documentación de una manera cómoda a la vez que se
programaba. Me gustaría que el sistema asociara la documentación al código
fuente de forma asistida. La inclusión de comentarios en el código fuente me
parecía un método bastante rudimentario de generar documentación. Con este
método es necesario tener todo el código para entender el comentario.
Otras inquietudes me llegaban a medida que iba conociendo lenguajes de
programación del paradigma procedural, declarativo y orientado a objetos.
Notaba una asombrosa similitud en muchos de los conceptos e ideas que
observaba en cada uno de ellos. Sin embargo no comprendía que no hubiese
una terminología común para referirse a dichos conceptos y prácticamente
ningún documento que sirviese de puente entre lenguaje del mismo paradigma
e incluso entre lenguajes de distintos paradigmas.
Por otro lado, a medida que construía programas cada vez mas grandes, con
miles de líneas de código observaba la dificultad de gestión de dicho código.
No me refiero a nivel lógico, de gestión de los tipos del programa, las variables
globales, etc... sino a nivel práctico. Cada vez era mas difícil navegar entre las
ingentes cantidades de código con el añorado TurboPascal 5.0. Algo tan sencillo
como poder saltar de la llamada a un procedimiento a su implantación era una
cosa que sólo ocurría en mi cabeza, pero no en los entornos de programación.
Con la forma textual de programar también comencé a tener mis dudas.
Comencé a darle vueltas a la programación visual, en la cual todos los
elementos que se pueden ir incorporando al programa, como definición de
2
Sobre la información, su estructura y su-gestión
subprogramas, referencias a variables, etc... se gestionase de forma visual. En
ese momento consideraba que reduciría considerablemente la curva de
aprendizaje de un nuevo lenguaje y a la vez que mejoraría la gestión del código.
Cuando empecé a descubrir las ventajas de la programación orientada a
objetos con la asignatura Programación II. Concebí una nueva forma de
programar. Sin ser rigurosos, se puede decir que la programación orientada a
objetos se basa en representar los conceptos que se manejan en el dominio de la
aplicación como clases y en materializaciones de esos conceptos como objetos.
Este acercamiento permite una mayor abstracción y una forma de programar
más cercana a los conceptos humanos.
Descubrí el lenguaje Java y con él como el problema de la documentación a
medida que se realizaba el código se había tenido en cuenta y se creó JavaDoc.
JavaDoc es un convenio que permite generar documentación a partir de los
comentarios escritos en código fuente con una determinada sintaxis. La
herramienta que lo permite genera documentación en formato HTML. Aunque
esos comentarios siguen escribiéndose de forma textual. Mas adelante
comentaremos el estado actual de los entornos de desarrollo integrados (IDE) y
que cosas han ido incorporando para manejo de todas estas posibilidades.
Pero la creación de documentación se limita a lo que Sun Microsystem
consideró necesario para documentar un código fuente orientado a objetos. No
hay posibilidad de personalizar la estructura de la documentación, para
documentar aquello que crees conveniente.
La representación gráfica de la estructura de un código fuente,
aunque
matizándolo mucho, es posible con UML. Este lenguaje unificado de modelado
con representación gráfica tiene su principal aplicación en los programas
orientados a objetos. Pero existen muchas dificultades para encontrar software
3
Sobre la información, su estructura y su-gestión
actualmente (15 Marzo 2002) que permitan gestionar el código de manera
gráfica y cómoda usando este convenio.
En la carrera nos enseñan a modelar los elementos de un dominio concreto.
Creamos aplicaciones con gráficos, aplicaciones de bases de datos, aplicaciones
web, etc... Estas aplicaciones se usan en diseño gráfico, en la gestión de las
pymes, y en muchos más lugares. Generalmente estos programas manejan
información de estructura muy compleja. Si la información que manejan es
bastante sencilla, estos programas manejan cantidades ingentes de datos.
Tenemos numerosas técnicas a nuestro alcance para gestionar este tipo de
información, a la que podríamos llamar de “usuario”. Sabemos hacer
estupendos interfaces gráficos y somos capaces de hacer muy sencilla la
navegación por cantidades ingentes de información. Hemos estudiado formas
de modelar información, de obtener las características esenciales y estructurales
de ésta para construir programas que la gestionen. Además se requiere que
estos programas, en la medida de lo posible, sean fácilmente adaptables.
Todos los estudios se centran en manejar los datos del usuario, y sin
embargo,
¿que
ocurre
con
los
datos
que
manejamos
nosotros
los
programadores? En la carrera sólo nos dan las ideas básicas de como se
representa la estructura de una información textual, en la asignatura de Teoría
de Autómatas y Lenguajes Formales. Pero nada de gestión del código, nada de
cómo se manejan programas de miles y miles de líneas. Seguimos programando
en modo texto, como en los años 50. ¿Dónde están los gráficos?
Afortunadamente en algunos entornos comenzamos a tener resaltado de
sintaxis, pero... ¿es esto suficiente para gestionar un código fuente? Sabemos
manejar cualquier tipo de información excepto la que nosotros creamos y
manejamos, los códigos fuente.
Por todos estos motivos la creación de un intérprete y un compilador me
llamaba poderosamente la atención. Pero como hemos comentado, la asignatura
de Teoría de Autómatas y Lenguajes formales no estudia cómo modelar un
4
Sobre la información, su estructura y su-gestión
código fuente, si no se basa en definir en modo textual una secuencia correcta
de caracteres.
Por otro lado, veo que en muchos lenguajes aparecen estilos de
programación. Pero no hay una manera formal de gestionarlos, lo que hace que
sean difíciles de manejar. Me refiero a convenciones en cuanto a cual es la
estructura del nombre de una variable o de una clase. O de que forma se
deberían de poner las llaves. Estas cosas pueden parecer superfluas, pero
tienen bastante importancia.
También existen convenciones de una naturaleza superior. Por ejemplo, el
modelo de eventos en Java, se basa en una serie de convenciones con los
nombres de los métodos y de las clases que representan a los eventos. Además,
mucho del código para implementar los eventos tiene la misma estructura. De
una forma similar, la especificación de JavaBeans usa convenciones en nombres
y crea clases “hermanas” para representar información. Pero todos estos
convenios, aparecen en tutoriales y sólo algunos entornos permiten
gestionarlos.
De un nivel superior tenemos los patrones de diseño orientado a objetos.
Conjuntos de objetos y clases, que dispuestos de determinada forma consiguen
un comportamiento con un nivel mayor de abstracción. Aunque existen
lenguajes de patrones, para la gestión de código con patrones no existe en
ningún entorno.
Según hemos observado, se demanda “básicamente” una forma de gestionar
el código de la misma forma que sabemos gestionar cualquier otro tipo de
información. Necesitamos estudiar en los mismos términos tanto datos de
“usuario” como sus metadatos (nombre de las variables donde se encuentran
esos datos, su tipo, etc...) y los datos que manejan a los datos de usuario
(instrucciones). Existen multitud de similitudes entre ellos y a continuación
5
Sobre la información, su estructura y su-gestión
indicaremos una serie de ejemplos en los que se ven mucho mas claro estas
similitudes.
Por lo que hemos estado comentando hasta ahora, parece que únicamente
queremos gestionar los metadatos (nombre de variables, tipo, etc...) y la
distribución del código para labores de desarrollo de programas. Parece que
este estudio sólo tiene interés para la labor de creación de software pero que
poco o nada tiene que ver con el software en fase de explotación. Pero existen
muchos mas casos de los que parecen en los cuales este estudio sería muy útil
en la propia ejecución del código.
En ingeniería del software, la forma de comprobar si la compañía
desarrolladora de software a comprendido bien los requisitos del cliente es
presentándole pantallas prototipo de la aplicación, esto es porque las pantallas
muestran una parte muy importante de una aplicación y son casi un fiel reflejo
de las estructuras (clases) y objetos que aparecen en el programa. El desarrollo
de software en el proceso propuesto por los creadores de UML se basa mucho
en los casos de uso, en aquellas acciones que se hacen desde “fuera” del sistema
y que le alteran o permiten su consulta. De alguna forma, todos aquellos
elementos del sistema que se crean siguiendo los casos de uso van a tener una
representación gráfica asociada.
En muchos casos, la representación de un objeto de una clase depende tanto
de su estructura interna, es decir de los atributos y método de su clase, que
podría generarse de forma asistida código para la visualización partiendo de la
clase del objeto. Por ejemplo si nuestra aplicación trabaja con alumnos,
obviamente tendremos la clase alumno, con atributos como nombre, foto,
apellido1, domicilio, población... o algunos atributos mas raros como, media
primera fase, códigoA, tutor. Si nosotros quisiéramos representar los datos que
tenemos de un alumno, lo mas lógico es que pusiéramos a un lado el nombre de
cada atributo y al otro lado la representación de cada uno de los atributos.
6
Sobre la información, su estructura y su-gestión
Observamos como un metadato, el nombre de un atributo, se convierte en un
dato de la aplicación ya que necesita mostrarse por pantalla. Pero si deseáramos
asociar una descripción detallada de cada uno de los atributos... ¿ que
estructura usaríamos ? Esta descripción puede ser la misma descripción que
aparezca en la propia documentación del código fuente. De alguna forma, lo
ideal sería guardar esa información en el lugar (no sabemos de momento cual)
donde se guarda el nombre del atributo. En ese lugar también necesitaríamos
una descripción.
Otro ejemplo sería la creación de un recubrimiento gráfico para un algoritmo
de clasificación, por poner un ejemplo. La forma de comunicarnos con el
algoritmo es mediante parámetros, y mediante el lanzamiento del mensaje en
donde vayan los parámetros. Imaginemos que simplemente estamos
depurando el código de dicho algoritmo y queremos una representación gráfica
medianamente aceptable. El tiempo necesario para crear dicha representación
sería muy grande. Básicamente en la interfaz gráfica aparecerían los nombres
los parámetros, un botón que permitiera lanzar el mensaje y una forma de crear
gráficamente cada uno de los parámetros necesarios y luego poder mostrar
gráficamente el valor devuelto.
Es decir, necesitaremos una representación del objeto devuelto (volvemos al
ejemplo del alumno) y una forma gráfica de crear un objeto. La forma gráfica de
crear un objeto puede ser la escritura de cada uno de los parámetros del
constructor. Esta pequeña aplicación de prueba del algoritmo se podría
convertir en un vistoso applet sin apenas esfuerzo y gran parte de ese applet se
podría construir automáticamente.
Vemos que por uno u otro lado, el estudio pormenorizado de los conceptos
de cualquier lenguaje de programación es muy necesario. Por tanto el primer
paso en la construcción de un sistema que permita la gestión de los metadatos y
7
Sobre la información, su estructura y su-gestión
los datos que manejan datos comienza con el modelado de los conceptos que
aparecen en un lenguaje de programación.
Basándome en todas estas ideas e inquietudes he decidido crear este TFC. El
trabajo va a consistir en analizar las similitudes entre los datos y el código
fuente, presentar un prototipo que modela un lenguaje como datos de
“usuario” y ver las ventajas de este enfoque en la construcción de herramientas
de desarrollo.
En el capítulo 2 se estudiará el gran parecido que tienen todos los lenguajes
de programación entre sí. También se hará patente la gran similitud en la forma
que tienen de modelar la información. Como último punto veremos como el
código y los datos son estructuralmente equivalentes.
En el capítulo 3 modelaremos el lenguaje Pascal e implantaremos un
prototipo en el que se ponen en práctica la idea de la equivalencia entre datos y
código.
En el capítulo 4 veremos las ventajas que supondría construir herramientas
basadas en el modelado propuesto. Estas mejoras abarcan los entornos de
desarrollo, compiladores, soporte a la ingeniería del software, aplicación en
enseñanza de la programación, etc…
8
II. Mecanismos recurrentes de la
estructura de la información
1. Comparación de lenguajes y paradigmas
A lo largo de la carrera he aprendido distintos lenguajes de programación.
Algunos de estos lenguajes de programación son Pascal, C, Pascal-FC, PIIPOO,
Java, Delphi, LISP, CAML, Prolog, etc... A medida que aprendía cada uno de los
lenguajes de programación notaba como se parecían en sus aspectos más
importantes. Vamos a clasificarlos dependiendo del paradigma al que
pertenecen.
9
Sobre la información, su estructura y su-gestión
Paradigma Lógico (Declarativo)
Prolog
Paradigma Orientado a Objetos
Paradigma Concurrente
Parlog
Prolog++
Vulcan
Paradigma Funcional
(Declarativo)
LISP
CAML
Haskel
Clean
Paradigma Imperativo
Java
CLOS
Smalltalk
C++
ObjetivePascal
Eiffel
Pascal
PascalFC
Ahora vamos a repasar las similitudes que tienen los lenguajes del mismo
grupo, y posteriormente veremos las similitudes que presentan entre los
distintos grupos.
Lenguajes Imperativos Procedurales
Las diferentes sentencias son equivalentes entre C y PASCAL, salvando las
diferencias sintácticas y potencia del lenguaje:
Descripción
Asignación
Una
C
Pascal
variable a = 3;
A:= 3;
llamada a de tipo
entero a la que se
le asigna un valor.
Expresión
Una expresión de 5 % 2
tipo
entero
5 MOD 2
de
valor 1
10
Sobre la información, su estructura y su-gestión
Llamada
función
a Llamada
la close();
a
CLOSE;
función close que
no
tiene
parámetros.
Sentencia If
Dependiendo del if( a == 2 ){
valor
de
a
incrementa
IF A = 2 THEN
BEGIN
A := A + 1;
END
ELSE
BEGIN
A := A + 2;
END;
a++;
}
else
{
se
a += 2;
su }
valor en 1 o en 2.
Bucle For
Bucle
for
imprime
secuencia
que for(i=1,i<=5;i++){
pritnf(“ %d”,i);
la }
FOR I:=1 TO 5 DO
BEGIN
WRITE(‘ ‘,I);
END
de
números 1 2 3 4
5
Bucle While Bucle while que i = 0;
while( num[i]!=0 ){
printf( num[i] );
lee de la entrada
i++;
estándar y saca }
I := 0;
WHILE NUM[I]<>0 DO
BEGIN
WRITE( NUM[I] );
I:=I+1;
END;
una a hasta hasta
que se lee un 0.
Los tipos primitivos en ambos lenguajes son similares. Los tipos que se
pueden crear son también similares. Vamos a comparar sus constructores.
Descripción
C
Pascal
Registro
Definimos un registro typedef struct fecha{ TYPE
int dia;
FECHA = RECORD
con tres campos para
int
mes;
DIA: INTEGER;
representar una fecha.
int anno;
MES: INTEGER;
};
ANNO: INTEGER;
END;
Registro
Definimos un registro typedef
fechaNumero {
con 2 partes para
int dia;
representar una fecha,
int mes;
en formato numérico
int anno;
o como un array de
Variante
11
struct TYPE
FECHANUMERO = RECORD
DIA: INTEGER;
MES: INTEGER;
ANNO: INTEGER;
Sobre la información, su estructura y su-gestión
caracteres.
};
END;
typedef
fechaTexto[40];
char FECHATEXTO = ARRAY
[1..40] OF CHAR;
typedef union fecha{
struct fechaNumero
fechaNumero;
struct fechaTexto
fechaTexto;
}
Array
Referencia
Definimos un array de
enteros de longitud 20
para almacenar
medidas.
Definimos una
referencia a un entero.
typedef int
medidas[20];
typedef int
*referencia;
FECHA = RECORD
CASE TIPO:INT OF
0:(FECHANUMERO:
FECHANUMERO);
1:(FECHATEXTO:
FECHATEXTO);
END;
TYPE
MEDIDAS = ARRAY
[1..20] OF INTEGER;
TYPE
REFERENCIA = ^INT;
Existen algunas diferencias entre los tipos de C y de Pascal. Estas diferencias
se basan en que C es más cercano a la máquina que Pascal y debido a ello C no
controla cosas que Pascal si controla. Pascal es un lenguaje de un nivel más alto
que C, pero mantiene su esencia.
Lenguajes Imperativos Orientados a Objetos
Vamos a hacer una breve comparación entre Java y PIIPOO. Las sentencias
de control de flujo de ejecución, la asignación, el lanzamiento de mensajes y las
expresiones son bastante similares.
Descripción
Una variable llamada
a de tipo entero a la
que se le asigna un
valor.
Una expresión de tipo
Expresión
entero de valor 1
Llamada a Llamada a la función
close que no tiene
función
parámetros.
Asignación
Sentencia If
Java
PIIPOO
a = 3;
A:= 3;
5 % 2
5 MOD 2
fichero.close();
FICHERO.CLOSE;
if( a == 2 ){
Dependiendo del
a++;
valor de a se
}
else
{
incrementa su valor en
a+=2;
1 o en 2.
}
12
IF A = 2 THEN
BEGIN
A := A + 1;
END
ELSE
Sobre la información, su estructura y su-gestión
BEGIN
A := A + 2;
END;
Bucle For
Bucle for que imprime for(i=1,i<=5;i++){
System.out.println(“
la secuencia de
números 1 2 3 4 5 ”+i);
}
Bucle While Bucle while que lee de i = 0;
la entrada estándar y
saca una a hasta hasta
que se lee un 0.
while( num[i]!=0 ){
printf( num[i] );
i++;
}
FOR I:=1 TO 5 DO
BEGIN
(‘ ‘+I).WRITE;
END
I := 0;
WHILE NUM[I]<>0 DO
BEGIN
WRITE( NUM[I] );
I:=I+1;
END;
En Java existen tipos primitivos con diferente uso que las clases. En PIIPOO
no existen tipos primitivos, el sistema ofrece unas clases primitivas. En cuanto a
los nuevos tipos que se manejan existen más diferencias que en el caso de C y
Pascal, pero también son similares. Vamos a comparar sus constructores.
Descripción
Clase
Array
Java
PIIPOO
class fecha {
Definimos una clase
int dia;
con tres atributos para
int mes;
representar una fecha.
int anno;
}
CLASS IMPLEMENTATION
FECHA;
ATRIBUTES
DIA:INTEGER;
MES:INTEGER;
ANNO:INTEGER;
END CLASS
IMPLEMENTATION
TYPE
Definimos un array de int medidas[];
=
ARRAY
enteros de longitud 20 Java no permite definir tipos MEDIDAS
[1..20] OF INTEGER;
que sean arrays. En la
para almacenar
declaración de tipo no se
medidas.
indica el tamaño.
Referencia
Definimos una
Java no permite
referencia a un entero. explícitamente el uso de
TYPE
REFERENCIA = ^INT;
referencias.
Se puede ver que existen múltiples parecidos entre lenguajes del mismo
paradigma. De hecho se han creado traductores para convertir programas
escritos en un lenguaje en programas en el otro lenguajes. Se pueden encontrar
traductores entre C y Pascal.
13
Sobre la información, su estructura y su-gestión
Lenguajes Declarativos Funcionales
Con el paradigma funcional ocurre lo mismo, vamos a comparar las distintas
instrucciones de LISP y CAML. Como se trata de un paradigma funcional las
únicas instrucciones que existen son las llamadas a funciones y las expresiones
como un caso particular de estas.
Descripción
Expresión
LISP
CAML
Una expresión de ( + 5 2 )
tipo
entero
5 + 2
de
valor 7
Llamada
función
a Llamada
a
la ( eleva 5 2 )
eleva(5,2)
función eleva con
dos parámetros.
Ambos lenguajes constan de tipos primitivos. CAML, entre otros tiene el tipo
entero, real, booleano, etc.... En LISP los tipos más representativos son el
símbolo, la cadena de caracteres y el valor numérico. A parte de los tipos
primitivos de datos de los dos lenguajes, ambos tienen tipos compuestos. En
LISP el tipo compuesto por excelencia es la lista de elementos, para la cual se
han creado primitivas de acceso. En el caso de CAML también existe el tipo
compuesto lista de elementos. Además en CAML se pueden tener tuplas.
Lenguajes Declarativos Lógicos
El lenguaje lógico impartido en la escuela es PROLOG, la construcción de
programas en este lenguaje parte de la lógica matemática y el uso de
predicados. Pese a la aparente diferencia con los lenguajes funcionales como
CAML y LISP, de todos es sabido la existencia de conversores entre lenguajes
de ambos paradigmas.
14
Sobre la información, su estructura y su-gestión
Similitudes entre paradigmas
Sin un estudio riguroso y profundo hemos encontrado muchos parecidos
entre los distintos lenguajes de un mismo paradigma y los lenguajes de los
distintos paradigmas.
Las instrucciones de control de flujo de ejecución en los lenguajes
imperativos son prácticamente iguales. En todos los lenguajes imperativos que
hemos “repasado” aparecen la sentencia alternativa, if, y varias formas de la
secuencia iterativa, for, while, repeat, etc... También se permite en todos los
lenguajes imperativos la construcción de rutinas (funciones y procedimientos).
La forma de ejecutar dichas rutinas, es decir, la llamada, es también similar en
todos los lenguajes. La asignación también está presente en todos ellos.
En los lenguajes del paradigma declarativo, aunque los hemos visto muy
superficialmente, se aprecian muchas diferencias. En funcional existen las
funciones como piezas para la construcción de programas; de igual forma, en
PROLOG, el lenguaje lógico, las piezas de construcción son los predicados,
similares a las funciones.
La forma de construir estructuras de datos complejas es muy similar entre los
lenguajes del mismo paradigma. En el paradigma imperativo aparecen el array
y registro o clase como elementos principales. La referencia también juega un
papel importante en todos ellos, incluido Java aunque su tratamiento en Java no
sea explícito. En todos los lenguajes declarativos ocurre una cosa similar, el tipo
lista aparece como elemento clave en la construcción de estructuras de datos.
A parte de las similitudes entre lenguajes del mismo paradigma,
encontramos grandes parecidos entre los lenguajes del paradigma imperativo y
el declarativo. La sentencia de control de flujo de ejecución alternativa, el if, se
representa en ambos paradigmas. En el paradigma imperativo se representa
mediante una instrucción concreta. En el paradigma declarativo, existen
funciones con el mismo funcionamiento.
15
Sobre la información, su estructura y su-gestión
Las sentencias iterativas de la programación imperativa, como son los bucles
for, while, etc... se pueden “simular” en la programación declarativa usando la
recursividad y el correspondiente caso base.
Las primitivas de construcción de estructuras de datos también son similares.
Las listas y los arrays son muy parecidos, con la salvedad de la longitud fija
ligada a los arrays y la longitud variable de las listas de elementos. Los registros
también aparecen en los lenguajes imperativos en los RECORD de Pascal, struct
de C, etc y en los lenguajes funcionales en las tuplas de CAML.
Hemos visto como entre todos los lenguajes estudiados, siendo de distintos
paradigmas existen similitudes. Las similitudes se manifiestan en las dos partes
en las que se divide un programa. Los lenguajes son similares en los
mecanismos para formar estructuras de datos y en los mecanismos para
controlar el flujo de ejecución de instrucciones.
2. Mecanismos de construcción de
estructuras de datos en lenguajes de
programación
Hemos visto los mecanismos o primitivas que tienen los distintos lenguajes
de los distintos paradigmas para construir estructuras complejas de datos a
partir de sus tipos primitivos. En cada uno de los lenguajes dichas primitivas
tienen nombres distintos. Además la semántica de cada una de ellas puede
diferir de un lenguaje a otro. Por estos motivos vamos a hacer una recopilación
de dichas primitivas y vamos a darles un nombre independiente del usado en
cada uno de los lenguajes.
16
Sobre la información, su estructura y su-gestión
Tipos atómicos
En todos los lenguajes de programación existen los tipos simples. Los tipos
simples, entre otras cosas, definen un conjunto de valores. Los tipos simples
mas comunes son el entero, el decimal y el carácter. Muchos lenguajes tienen
como tipos simples la cadena de caracteres, el tipo lógico, etc...
En todos los lenguajes se pueden manejar datos cuyo tipo sea uno de los
atómicos. En los lenguajes imperativos los valores se albergan en variables. En
los lenguajes declarativos, los valores se transmiten de la salida de una función
a la entrada de otra función.
Registro
En Pascal se permite crear agrupaciones de datos de tipos distintos. Esta
agrupación se puede tratar como un todo o bien acceder a cada una de sus
partes. Sus partes son accesibles mediante un identificador.
En Pascal están los record. En C tenemos los struct con la misma
funcionalidad que la vista en Pascal. En los lenguajes orientados a objetos se
obtiene la misma funcionalidad con las clases y sus atributos.
En CAML no se accede a los atributos mediante un identificador, si no que se
accede a ellos mediante un patrón. El acceso mediante patrón básicamente
asocia un nombre al elemento en el momento del acceso y no en el momento de
la creación del tipo. Se puede decir que se tiene un acceso híbrido entre posición
e identificador.
En Lisp y Prolog no se tiene un tipo directamente equivalente. Para obtener
una funcionalidad similar se usa el tipo lista, ya que en estos lenguajes no
tienen comprobación de tipos en tiempo de compilación y los elementos de la
lista pueden tener cualquier tipo distinto unos de otros. Cuando en Lisp y
Prolog se usan las listas como registros, debido a que cada elemento de la lista
representa un elemento similar al registro, el número de elementos suele ser
fijo.
Cuando definimos un tipo registro estamos indicando que cada una de las
variables de ese tipo estará compuesta por varias variables. Cada una de ellas
17
Sobre la información, su estructura y su-gestión
tendrá un tipo determinado. El número de variables por los que se forme será
fijo.
Secuencia
En todos los lenguajes de programación se permite crear agrupaciones de
datos del mismo tipo. En C y en Pascal se tienen los arrays, en Java y PIIPOO
también tenemos los arrays. En los lenguajes funcionales LISP y CAML usamos
las listas para este fin. Hay que destacar, que en LISP los elementos de la lista
no están obligados a tener el mismo tipo, pero un uso bastante común de ellas
es que tengan todos el mismo tipo. El tamaño de la secuencia en algunos
lenguajes se fija en el tipo, como es el caso de Pascal y C. Y en otros casos puede
ser determinado cuando se crean las variables del tipo, como en el caso de Java.
En LISP y CAML el tamaño se varía en tiempo de ejecución. Por tanto podemos
decir que el tamaño es potencialmente variable. Los elementos de la secuencia
están ordenados. El acceso a ellos se hace siempre por su posición, bien sea
indicando el índice, en el caso de los lenguajes imperativos, o bien tratando la
cabeza de la lista en los lenguajes declarativos.
Cuando definimos un tipo secuencia estamos indicando que cada una de las
variables de ese tipo estará compuesta por varias variables, todas ellas del
mismo tipo.
Podemos usar la siguiente representación gráfica para representar el tipo
secuencia:
Referencia
Con este nombre queremos identificar al concepto de puntero que aparece en
algunos lenguajes de programación. Aunque no es obligatorio, generalmente en
los lenguajes de programación se almacenan los elementos de un array de
forma contigua en memoria. Los elementos de un registro suelen también
almacenarse de forma contigua en memoria. Debido a esta implementación no
18
Sobre la información, su estructura y su-gestión
se puede compartir el mismo dato en dos lugares distintos de la estructura de
datos.
Vamos a ver un ejemplo de la idea de referencia. Supongamos que deseamos
representar una agenda de teléfonos. Podemos definir un tipo registro llamado
regPersona con los campos nombre, apellidos, dirección, teléfono, etc.
Posteriormente podemos definir una secuencia de datos del tipo regPersona,
para poder almacenar todas las entradas de la agenda.
Si ahora queremos representar en cada uno de los registros que mantienen
los datos de una persona quién es su padre dentro de los que están
almacenados en la agenda, ¿cómo lo hacemos? Podemos tener un campo del
registro regPersona con el tipo regPersona de nuevo y en él almacenar los datos
del padre. Pero… ¿que ocurre si varias personas tienen el mismo padre?
Estarían los datos del padre duplicados en nuestra estructura de datos. Esto
traería problemas a la hora de modificar cualquier dato, ya que habría que
modificarle en todos los lugares donde se almacene el padre. También sería un
desperdicio de espacio.
Por estos motivos se crean las referencias. Podríamos decir que una
referencia es un valor, comprensible por el sistema o por el programador, que
identifica inequívocamente a un dato concreto. De esa manera, cuando tenemos
ese valor, podemos identificar un único dato, pero sin tener el dato
propiamente dicho; en vez de el dato tenemos un acceso al dato.
Una representación bastante habitual de las referencias es mediante una
flecha desde el lugar donde está la referencia al lugar donde está el dato.
En los lenguajes de programación imperativos como C o Pascal las
referencias se implementan con punteros. En el caso de PIIPOO ocurre lo
mismo. Java en cambio es distinto en este sentido. En Java los objetos son
accedidos siempre a través de referencias, nunca se tiene el dato propiamente
dicho. En los lenguajes declarativos, la referencia la ha de implementar el
programador usando identificadores explícitos.
19
Sobre la información, su estructura y su-gestión
Cuando definimos un tipo referencia estamos indicando que cada una de las
variables de ese tipo contendrá un valor que nos permitirá acceder al dato
concreto que identifican.
Alternativa
Los tipos alternativa se definen indicando dos o más tipos existentes.
Cuando definimos un tipo alternativa estamos indicando que cada una de las
variables de ese tipo contendrá un valor perteneciente a uno de los tipos
indicados en la definición.
La alternativa se permite en Pascal a través de los registros variantes. En C
una funcionalidad parecida se obtiene con las uniones. En la programación
orientada a objetos la alternativa se obtiene mediante la herencia en la creación
de tipos y el polimorfismo, de forma que una variable declarada de un tipo
admite debido al polimorfismo cualquier valor que sea de un tipo derivado de
ese tipo.
En Lisp no hay comprobación de tipos en tiempo de compilación porque se
posibilita la alternativa de cualquier símbolo asociado a un átomo o lista. El tipo
de un valor se comprueba justo en el momento en el que se va a usar dicho
valor. Por tanto se puede hacer uso de esta alternativa pero será controlada en
todo momento por el programado
Pongamos un ejemplo de la construcción de un programa usando la
alternativa. Por ejemplo, para saber donde vive una persona podemos
almacenar un texto con el nombre de su pueblo o bien el código postal. Por
tanto, localidad es una alternativa entre entero o cadena de caracteres.
Podemos hacer un breve resumen de las características más importantes de
cada uno de los mecanismos:
20
Sobre la información, su estructura y su-gestión
Mecanismos de construcción de estructuras de datos
Átomo
Representa a un dato atómico. No se puede dividir en otros
datos.
Registro
Representa una agrupación no ordenada de datos. El número de
elementos es fijo. Cada uno de los datos de la agrupación puede
tener un tipo distinto. A los datos se accede generalmente por un
identificador.
Secuencia
Representa una agrupación ordenada de datos. La longitud de la
secuencia puede ser fija o variable. Todos los elementos de la
secuencia tienen el mismo tipo. A los datos se accede mediante su
posición.
Referencia
Representa un enlace a un dato, no el dato propiamente dicho. Se
usa para compartir el mismo dato entre varios puntos de la
estructura de datos.
Alternativa
Representa a un dato. El dato puede ser de uno de los tipos
indicados en la alternativa.
Hemos observado que básicamente todos los lenguajes “estudiados”
comparten las mismas primitivas. Existe un gran parecido entre ellos, pero
permanece oculto por la diferente sintaxis y los distintos puntos de vista con los
que se estudia cada lenguaje y cada paradigma.
3. Representaciones Gráficas
Para un mejor entendimiento de las primitivas anteriormente expuestas,
vamos a proponer una representación gráfica para cada una de ellas.
Átomo / Tipo Simple
Podemos representar un tipo simple poniendo su nombre dentro de un
rectángulo. Aquí mostramos los tipos entero y decimal:
21
Sobre la información, su estructura y su-gestión
entero
decimal
Un dato cuyos valores sean de tipo simple se puede representar dentro de
una forma rectangular con las esquinas redondeadas. Aquí mostramos algunos
datos con valores 4, 7, 3.2, 89.3:
4
7
3.2
89.3
Para representar en un mismo gráfico un tipo y aquellos datos que tienen
ese tipo, los unimos con una línea terminada en círculo. En este gráfico
podemos ver las dos representaciones anteriores juntas:
entero
4
decimal
7
3.2
89.3
En algunas estructuras de datos que tengan muchos datos simples, puede
ser bastante engorroso unir todos los datos con su respectivo tipo mediante
una línea. Para poder indicar el tipo de un dato sin necesidad de unirse a él
mediante una línea se puede usar la siguiente representación:
entero
decimal
4
3.2
Registro
Para representar un tipo registro dibujamos un rectángulo y dentro el
nombre del tipo. Cada uno de los campos de un registro viene determinado
22
Sobre la información, su estructura y su-gestión
por un nombre y un tipo. Para representar gráficamente un campo, unimos
el rectángulo del tipo registro con el rectángulo del tipo del campo con una
línea terminada en rombo. El nombre del campo aparecerá sobre la línea de
unión. Mostramos aquí la representación gráfica de un registro llamado
tPersona con tres campos:
nombre
tPersona
apellido
telefono
entero
Como podemos observar, el tipo cadena es usado en dos campos. Puesto
que hay un único tipo llamado cadena, solo aparece un rectángulo que lo
representa. Sin embargo, cuando el dibujo sea muy lioso, se permite duplicar
la representación de un tipo. En la siguiente figura mostramos el mismo
esquema duplicando el tipo cadena.
nombre
tPersona
apellido
telefono
cadena
cadena
entero
Hay que destacar que no existen dos tipos cadena. Sólo existe un tipo cadena.
La duplicación del elemento gráfico es por mejorar la legibilidad de la
representación. Si el tipo cadena fuese a su vez un registro, sólo sería necesario
indicar los campos en uno de los cuadrados.
Hay veces en que esta representación gráfica es demasiado detallada. Se
necesita una forma compacta de poder representar gráficamente un tipo
basado en registro. La representación elegida será la siguiente:
23
Sobre la información, su estructura y su-gestión
tPersona
nombre : cadena
apellido : cadena
teléfono : entero
Para representar datos que tengan como tipo un registro también existe una
representación. El dato se representa por un cuadrado vacío y unido a él se
representan los valores de cada uno de sus campos. Un dato que represente la
información de una persona sería:
nombre
tPersona
apellido
telefono
nombre
apellido
entero
Pedro
"Sánchez"
telefono
670456534
En este gráfico existe información redundante. Los datos se asocian a su tipo
directamente, por ejemplo “Pedro” con el tipo cadena. Por otro lado los datos se
asocian a su tipo a través del nombre con el que se asocian al dato registro. En
algunas ocasiones, para mejorar la legibilidad, se permite omitir la relación
explícita entre dato y su tipo siempre que esta relación se pueda inferir a través
de un tipo compuesto. Con esta simplificación el dato anterior junto con su tipo
quedaría:
24
Sobre la información, su estructura y su-gestión
nombre
tPersona
apellido
telefono
nombre
apellido
entero
Pedro
"Sánchez"
telefono
670456534
Al igual que existe una representación resumida para el tipo registro,
también existe una representación resumida para los datos de ese tipo. La
representación resumida se crea metiendo los datos que componen el registro
dentro del cuadrado que representa el dato. Una representación con el tipo y el
dato juntos sería:
tPersona
nombre : cadena
apellido : cadena
teléfono : entero
nombre = "Pedro"
apellido = "Sánchez"
teléfono = 670456534
Es importante destacar que las representaciones resumidas de datos se
pueden combinar con representaciones completas de tipos y viceversa.
Al igual que con los datos de tipos simples, los datos de tipos registro
también se pueden representar colocando el nombre del tipo en la parte
25
Sobre la información, su estructura y su-gestión
superior del cuadrado redondeado. Una representación equivalente a la
anterior sería:
tPersona
nombre : cadena
apellido : cadena
teléfono : entero
tPersona
nombre = "Pedro"
apellido = "Sánchez"
teléfono = 670456534
Secuencia
Para representar un tipo secuencia creamos un rectángulo con el nombre del
tipo dentro. Unimos mediante una línea terminada en rombo el cuadrado al
tipo de los elementos que contendrá la secuencia. Además, acompañamos el
rombo con un número que indique el número de elementos que contendrá la
secuencia. Si queremos indicar que el número de elementos no está
determinado en el tipo usamos un asterisco.
La representación de un tipo array de cinco de enteros sería:
5
tSecEnteros
entero
La representación de un tipo array con un número indeterminado de enteros
sería:
*
tSecEnteros
26
entero
Sobre la información, su estructura y su-gestión
Para representar un dato de este tipo junto con el tipo la representación
usada es la siguiente:
tSecEnteros
*
entero
[1]
4
[2]
5
[3]
7
Los números que acompañan a los datos individuales se han puesto para
mejorar la legibilidad del gráfico, pero pueden omitirse. Al igual que en los
registros, no es necesario tampoco especificar explícitamente la relación entre
los datos del array y su tipo, ya que esta relación está implícita en la pertenencia
al dato array. Un gráfico con las dos simplificaciones sería:
*
tSecEnteros
entero
4
5
7
También existen representaciones resumidas para el tipo y para los valores.
En el siguiente gráfico mostramos la definición del tipo junto con dos datos
pertenecientes al tipo.
27
Sobre la información, su estructura y su-gestión
tSecEnteros
* : entero
4,5,7
4
5
7
Nótese que existen dos formas de resumir los datos de tipo array. Se pueden
colocar los valores horizontalmente separados por comas o verticalmente
separados por un espacio.
Referencia
El tipo referencia se representa mediante un rectángulo (como el resto de los
tipos) unido al rectángulo del tipo al que referencia. La unión se crea mediante
una línea con punta de flecha lineal apuntando en el sentido del tipo
referenciado. Una tipo referencia a entero sería:
entero
tRefEnt
La representación de un dato apuntando a otro dato de tipo entero sería:
pEntero
entero
4
28
Sobre la información, su estructura y su-gestión
Podemos ver como el dato que tiene albergado un 4 y es de tipo entero se
asocia a su tipo por dos caminos. Por un lado directamente y por otro a
traves del dato puntero. En este caso también se puede hacer la
simplificación hecha anteriormente para definir los tipos explícitamente. El
gráfico con esa simplificación quedaría:
pEntero
entero
4
Para crear una representación simplificada del tipo se puede usar el
siguiente la siguiente representación:
^entero
Con los demás mecanismos usábamos simplificaciones también en los
datos del tipo determinado, en este caso no podemos hacerlo. Una referencia
se crea para crear un enlace entre dos datos de la misma estructura. Este
enlace se crea para no tener duplicada la información en dos lugares de la
estructura de datos. Por este motivo siempre que se represente una referencia
hay que dibujar una flecha desde el punto donde esté la referencia hasta el
punto donde esté el dato referenciado.
Alternativa
Para representar una alternativa se crea una caja con el nombre del tipo de la
alternativa. Esta caja se une a los tipos que forman la alternativa mediante una
29
Sobre la información, su estructura y su-gestión
línea que “comienza” en punta rellena. Para representar el tipo alternativa
AltNumero que alberga a enteros y reales se representaría:
real
AltNumero
entero
La representación del tipo alternativa se puede hacer de forma resumida. La
representación del ejemplo anterior sería:
tAltNumero
entero | real
Para representar la relación con los datos que pertenecen a ese tipo se
representaría:
real
AltNumero
entero
4
Otro valor se podría representar:
30
Sobre la información, su estructura y su-gestión
real
AltNumero
entero
4.5
En este caso no se pueden hacer simplificaciones a la hora de mostrar la
relación del tipo con el dato. Antes simplificábamos porque la información era
redundante, ahora el tipo interno de un dato de tipo alternativa puede ser
elegido entre mas tipos, por tanto es necesario indicarlo explícitamente.
Para representar la pertenencia de un dato a un tipo concreto se puede usar
la representación que coloca el nombre del tipo en la parte superior del dato.
Una representación equivalente a la anterior sería:
AltNumero
decimal
4.5
entero
4
Unión de mecanismos
En la mayoría de las ocasiones necesitaremos usar todos los mecanismos
vistos de manera conjunta. Si queremos representar una secuencia de tamaño
31
Sobre la información, su estructura y su-gestión
ilimitado de registros para representar personas lo representaremos de la
siguiente forma:
nombre
*
tSecPersonas
apellido
tPersona
telefono
entero
Resumiendo el tipo registro sería:
tPersona
*
tSecPersonas
nombre : cadena
apellido : cadena
teléfono : entero
Resumiendo los dos tipos tendríamos la siguiente representación:
tSecPersonas
* : tPersona
nombre : cadena
apellido : cadena
teléfono : entero
Algunos ejemplos de representación de datos de este tipo compuesto de
forma detallada serían:
32
Sobre la información, su estructura y su-gestión
nombre
*
tSecPersonas
apellido
tPersona
telefono
entero
nombre
[1]
"Pedro"
apellido
"Sánchez"
telefono
670456534
nombre
[2]
"Alberto"
apellido
"Arevalillo"
telefono
670236534
nombre
[3]
"Juan"
apellido
"Pérez"
telefono
670411534
La misma representación se puede hacer de forma resumida de la siguiente
forma:
33
Sobre la información, su estructura y su-gestión
tSecPersonas
* : tPersona
nombre : cadena
apellido : cadena
teléfono : entero
nombre = "Pedro"
apellido = "Sánchez"
teléfono = 670456534
nombre = "Alberto"
apellido = "Arevalillo"
teléfono = 670236534
nombre = "Juan"
apellido = "Pérez"
teléfono = 670411534
Simplificaciones en la unión de mecanismos
La forma de representar una secuencia de referencias a un registro de tipo
persona sería:
nombre
tSecRefPersona
*
tRefPersona
tPersona
apellido
telefono
entero
En algunas ocasiones definir explícitamente el tipo tRefPersona es engorroso.
Por este motivo se permite poner el asterisco en la flecha de relación. El
siguiente gráfico sería equivalente al anterior:
nombre
tSecRefPersona
*
tPersona
apellido
telefono
34
entero
Sobre la información, su estructura y su-gestión
Si queremos representar un registro que tiene una secuencia entre sus
atributos lo representaremos de la siguiente manera:
nombre
apellido
tPersona
numerosTlf
tSecEnteros
*
entero
Para representar que un campo es una secuencia de datos se puede hacer
la siguiente representación:
nombre
tPersona
apellido
numerosTlf
*
entero
Ambas simplificaciones se pueden usar juntas si lo que queremos
representar es que un registro tengan como campo una secuencia de
referencias. Por ejemplo, el siguiente esquema nuestra los datos de una
persona y las direcciones son referencias ya que varias personas pueden
compartir la misma vivienda.
35
Sobre la información, su estructura y su-gestión
tSecPersonas
nombre
*
apellido
tPersona
numerosTlf
direcciones
*
calle
entero
*
ciudad
tDireccion
número
entero
Un dato de ejemplo para esta estructura de datos sería:
tSecPersonas
tPersona
[1]
nombre = "Pedro"
apellido = "Sánchez"
teléfonos = 670456534, 925761515
direcciones = ,
tDirección
calle = "Tejar"
ciudad = "Torrijos"
número = 7
tDirección
calle = "Puente"
ciudad = "La Pueba"
número = 13
tPersona
[2]
nombre = "Alberto"
apellido = "Arevalillo"
teléfonos = 670236534
direcciones =
tPersona
[3]
nombre = "Juan"
apellido = "Pérez"
teléfonos =
direcciones =
4. Mecanismos de construcción de
estructuras de datos de otros sistemas de
representación de la información
Para
hacer
nuestro
“estudio”
hemos
elegido
varios
lenguajes
de
programación de distintos paradigmas. En el mundo de la informática hay
36
Sobre la información, su estructura y su-gestión
otros sistemas en los cuales hay que crear estructuras de datos. Nos referimos,
entre otros, a las bases de datos relacionales y al XML. Las bases de datos
relacionales persiguen la persistencia de la información y su rápida consulta y
modificación. XML persigue la transmisión de información jerarquizada entre
sistemas heterogéneos. Vamos a hacer un breve repaso de los mecanismos que
tienen para construir sus estructuras de datos. Veremos si tienen alguna
relación con los cinco mecanismos básicos que hemos encontrados en los
lenguajes de programación.
Bases de datos relacionales
Tipo Atómico
Para representar la información en una base de datos se dispone de los tipos
atómicos mas comunes. Los tipos atómicos permiten representar enteros,
cadenas de caracteres, reales, lógicos, etc...
Registro
En una base de datos relacional se pueden crear registros. De hecho en una
base de datos existe el concepto de registro de la misma forma que el que
hemos visto aquí. Los registros son agrupaciones de datos, con el número de
datos fijo. Cada uno de los datos que forman un registro se llama campo. Los
campos se referencian por su nombre.
Secuencia
Las secuencias de elementos del mismo tipo también se permiten. Estas
secuencias se conocen como tablas. El número de elementos de las tablas es
variable. Curiosamente, en las bases de datos relacionales teóricas, los datos
dentro de la tabla no están ordenados y por tanto no son accesibles mediante un
índice. Para acceder a los datos de las tablas se tienen en cuenta los valores de
los campos.
37
Sobre la información, su estructura y su-gestión
Referencia
En las bases de datos existe una forma primitiva de referencia. Es el usuario
el que tiene que manejar en gran parte las referencias. La forma en que se crean
enlaces es similar que la que hemos visto en Lisp. El usuario tiene que crear un
campo en el registro cuyo valor identifique al registro entre todos los demás, la
llamada clave primaria. Cuando desea crear una referencia a datos de ese
registro, tiene que crear un campo denominado clave foránea.
Alternativa
En una base de datos relacional no hay nada parecido a la alternativa. Hasta
hace muy poco tiempo la alternativa no se consideraba esencial. Por ese motivo
y para mejorar la eficiencia no se ha incluido en las bases de datos relacionales.
Actualmente, las bases de datos derivadas de las relacionales, denominadas
objeto-relacionales, incorporan mecanismos propios de orientación a objetos
que permiten hacer uso de la alternativa.
Combinación de Mecanismos
Una base de datos relacional tiene una estructura muy rígida. El anidamiento
de mecanismos está muy restringido. El esquema de cualquier base de datos
relacional es el siguiente:
38
Sobre la información, su estructura y su-gestión
nombreBaseDeDatos
nombreCampo1Tabla1
nombreTabla1
nombreCampo2Tabla1
nombreCampoNTabla1
nombreCampo1Tabla2
nombreTabla2
nombreCampo2Tabla2
nombreCampoNTabla2
nombreCampo1TablaN
nombreTablaN
nombreCampo2TablaN
nombreCampoNTablaN
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
tipoAtómico
Siendo tipoAtómico cualquiera de los tipos atómicos que se permiten en una
base de datos relacional.
Pese a las limitaciones observadas, vemos como una base de datos relacional
tiene la gran mayoría de los mecanismos vistos en los lenguajes de
programación.
Es sorprendente que sean tan parecidos unos sistemas y otros y que no se
expliquen en los mismos términos.
XML basado en XML Schema
La definición a la que se tiene que acoger un documento XML se puede
expresar mediante una DTD, Document Type Definition o bien mediante un
XML Schema. El XML Schema es mucho mas potente en cuanto a especificación
que la DTD. Vamos a “estudiar” los mecanismos existentes en XML Schema.
39
Sobre la información, su estructura y su-gestión
Tipos Atómicos
En un documento XML se pueden tener datos de los tipos primitivos mas
importantes. XML dispone de muchos tipos primitivos, pero los básicos son los
enteros, reales, lógicos y cadena de caracteres.
Registro
Es el llamado tipo compuesto. Los campos que forman un registro en XML se
dividen en dos grupos, los atributos y los elementos. Los tipos y mecanismos
usados en los atributos son un subconjunto de los usados en los elementos. La
funcionalidad básica es similar a la vista hasta ahora en los demás registros.
Secuencia
Mediante unos valores de número mínimo de ocurrencias y número máximo
de ocurrencias se pueden tener secuencias de longitud fija o secuencias de
longitud variable. La funcionalidad es similar a la vista en los lenguajes de
programación. El acceso a los elementos es bastante potente, pudiéndose
acceder a ellos por posición, como en los lenguajes de programación y mediante
los valores de los datos, como en las bases de datos.
Referencia
La referencia esta también soportada en XML. Mediante los mecanismos key
y keyRef se pueden crear referencias. Además una referencia puede
establecerse dinámicamente dependiendo del valor de los registros.
Alternativa
XML soporta la creación de tipos de forma similar a la programación
orientada a objetos. Por tanto, con herencia y polimorfismo se consigue la
funcionalidad de la alternativa, pero de forma mucho mas avanzada.
Combinación de Elementos
XML es muy potente en este aspecto. De hecho el registro y la secuencia se
pueden combinar de una manera muy eficaz y rápida para crear estructuras
40
Sobre la información, su estructura y su-gestión
muy complejas. Además, el soporte avanzado de referencias que posee es muy
potente a la hora de gestionar datos muy interrelacionados.
Los niveles de anidamiento en los elementos de un tipo compuesto, lo que
nosotros conocemos como campos, es ilimitado.
En XML aparecen todos los mecanismos vistos hasta ahora. Parece que
estos mecanismos se encuentran en todos aquellos lugares donde hay que
definir la estructura de los datos.
5. Mecanismos de construcción de la
estructura de las sentencias en los lenguajes
de programación
Según Cox "el software son los datos que se suministra el programador en
tiempo de compilación para manipular los datos del usuario en tiempo de
ejecución". Si los códigos fuente son datos, ¿cómo se define la estructura de esos
datos? Los mecanismos que definen la estructura de un código fuente ¿son los
mismos que los que definen las estructuras de datos que manejan esos
programas? Cabe preguntarse si los programas, como datos, respetan las reglas
de construcción de datos expuestos hasta el momento: la sentencia de
asignación puede entenderse como una orden de ejecución atómica. La
sentencia compuesta puede entenderse como un registro de ordenes de
ejecución de diversa naturaleza formando un todo. La sentencias iterativas
pueden entenderse como una secuencia de ordenes de ejecución. Las sentencias
alternativas pueden entenderse como una orden de ejecución u otra
dependiendo de cierta condición. La llamada a un subprograma puede
entenderse como la referencia a una orden de ejecución.
Procedemos a su estudio:
41
Sobre la información, su estructura y su-gestión
Se pueden agrupar elementos atómicos en los lenguajes de programación, es
decir, se crean grupos de instrucciones que se tratan formando un todo, por
ejemplo en Pascal, todas aquellas instrucciones que vayan entre BEGIN y END
se tratan como una instrucción compuesta. De igual forma, en cuanto a los
datos podemos crear registros, unidades que tratan como un todo a un conjunto
de variables.
En esta forma de agrupar elementos hay que especificar cada uno de los
elementos que forman el grupo, de tal forma, que esos elementos son
potencialmente de naturaleza distinta.
En cambio, existe otra forma de agrupar elementos, en la cual, los elementos
han de ser de la misma naturaleza. En cuanto a datos nos estamos refiriendo a
un array, es decir, un conjunto de variables del mismo tipo. La forma de definir
estas agrupaciones de datos es indicando el tipo de un elemento, y el número
de estos elementos que deseamos tener. En código se observa una cosa
parecida, nos referimos a los bucles, el caso mas sencillo, en un bucle for con
una instrucción, lo que tenemos conceptualmente es una instrucción repetida
tantas veces como lo que se indique en el bucle for.
En muchos casos, la instrucción que contiene un bucle es una instrucción
compuesta, de igual forma, se podría construir un array de registros.
El otro elemento clave en la estructuración tanto de datos como de la
ejecución de instrucciones es la alternativa. En el caso de la ejecución de las
instrucciones, la alternativa viene representada por la sentencia condicional if.
Esta sentencia alberga a otras dos instrucciones, que de forma general pueden
ser compuestas, y cuya semántica es que no se pueden ejecutar ambas
instrucciones, sino solamente una de ellas. En cuanto a la estructura de los
datos, tenemos los llamados registros variantes, registros en los cuales se
definen dos posibles “estructuras”, las variables que tengan este tipo variante,
solo pueden tener unos datos, o bien con una estructura o bien con la otra, pero
no con ambas.
La manera de determinar que alternativa es la que se hace efectiva (se ejecuta
en código o indica la estructura de los datos) depende de la naturaleza del
42
Sobre la información, su estructura y su-gestión
elemento del que hablemos. En código ejecutable, es decir, en la instrucción if se
determina que instrucción ejecutar dependiendo del resultado de evaluar una
expresión, es decir, del resultado de ejecutar un código. En cambio en cuanto a
datos, la manera de elegir la estructura que va a tener posteriormente una
variable con ese tipo es el valor de un campo común que han de tener las dos
alternativas.
Esta proyección entre la estructura de datos y estructura del código fue
preconizada por Warnier y Jackson que establecían que la estructura de datos
del programa determinaba la estructura del sentencias del programa. En textos
docentes actuales sobre la programación permanece explícitamente la relación
entre los arrays y la sentencia for, el registro variante y la sentencia case, etc.
6. Mecanismos de construcción de los
lenguajes de programación
No hay ninguna duda de que un código fuente de un programa es un dato
compuesto. Cada uno de los códigos fuente de un lenguaje de programación se
tienen que acoger a unas reglas, a una definición para considerarse
correctos.Todos los lenguajes de programación obligan a que sus códigos fuente
estén definidos de forma textual. El mecanismo usado para especificar una
estructura en una secuencia de caracteres se llama gramática.
Pese a que hay dos gramáticas usadas para definir un lenguaje, las de tipo 3
y las de tipo 2. Sabemos que se pueden especificar todas las reglas textuales de
un lenguaje usando una gramática de tipo 2. La forma más común de escribir en
gramática tipo 2 es mediante la forma EBNF. Por tanto, vamos a estudiar los
mecanismos que permite EBNF para definir estructuras. ¿ Aparecerán los cinco
mecanismos vistos hasta ahora?
Comprobemos su proyección:
•
los elementos atómicos son los terminales de la gramática;
43
Sobre la información, su estructura y su-gestión
•
la producción "registra" conjuntamente elementos de diversa
naturaleza de la gramática;
•
los operadores de repetición '+' y '*' determinan las secuencias de
elementos de la misma naturaleza;
•
el operador de disyunción '|' determina la alternatividad entre dos
elementos de la gramática;
•
el no terminal da la "referencia" a otras producciones;
Parece que con elementos atómicos, y con la posibilidad de agruparlos de
forma homogénea o heterogénea, la capacidad alternativa y las referencias
hemos modelizado las instrucciones ejecutables y los datos en un lenguaje de
programación estructurado como es Pascal.
Volvemos a encontrarnos estas cinco formas de relacionar elementos
atómicos en las gramáticas, mecanismo formal para definir los lenguajes. Los
elementos atómicos son los tokens cuando hablamos de gramáticas que definen
las características sintácticas de un lenguaje o bien caracteres cuando la
gramática define las características léxicas. Tenemos también agrupación de
elementos heterogéneos, es decir, que pueden no ser iguales, y que se tratan
como una unidad. Es el caso de los no terminales. Básicamente los no
terminales son símbolos que representan la agrupación de otros símbolos que a
su vez pueden ser o no terminales. De igual forma se puede representar la
alternativa, mediante eso, la alternativa de las gramáticas y representa que ante
un mismo símbolo no terminal, se está representando un elemento u otro, pero
no ambos a la vez. Y por último nos queda la representación de elementos
homogéneos, es decir, la repetición de elementos. En las gramáticas, se
representa la repetición mediante la recursividad en la definición de elementos
y la alternativa para ofrecer el caso base. De ese modo se pueden definir
conjuntos o secuencias de elementos homogéneos.
44
Sobre la información, su estructura y su-gestión
7. Axiomática de la información
Llegamos a la conclusión de que de igual forma que se modelan los datos,
podemos modelar el código e incluso las reglas con las que se define ese código.
Este aspecto nos conduce a la posible unificación de notaciones sintácticas para
aspectos tan dispares como la definición de datos, de sentencias y de gramáticas
con un lenguaje único.
Por ejemplo, la siguiente definción de Pascal
TYPE
Intervalo = RECORD
minimo: Integer;
maximo: Integer;
END;
podría reescribirse con un lenguaje universal
Intervalo REFERENCE CLUSTER
minimo: Integer;
maximo: Integer;
END CLUSTER
También, la siguiente definción de Pascal
PROCEDURE Inicializar;
BEGIN
x:=0;
y:=0;
END;
podría reescribirse con un lenguaje universal
Inicializar REFERENCE CLUSTER
x:=0;
y:=0;
END CLUSTER
Y, finalmente, parte de la gramática de Pascal
Suprograma ::= Cabecera Cuerpo
podría reescribirse en un lenguaje universal
45
Sobre la información, su estructura y su-gestión
Suprograma REFERENCE CLUSTER
Cabecera
Cuerpo
END CLUSTER
En este punto cabe reflexionar sobre la naturaleza de los cinco mecanismos
¿son primitivos? ¿son ampliables? Parece extraño la presencia de secuencia y
registro frente a la ausencia de estructuras jerárquicas como árboles o grafos,
aplicaciones y conjuntos entre otros. Rápidamente se puede observar que estas
propuestas con fácilmente soportadas por los conceptos de átomos y referencias
dispuestas según la estructura particular. Luego, si cabe, los cinco mecanismos
son reducibles más que ampliables.
De hecho una secuencia puede concebirse como un átomo con un conjunto
de referencias a otros átomos, sus elementos, que a su vez se referencian entre
sí para determinar un orden total. Por otro lado, un registro puede concebirse
como un átomo con un conjunto de referencias a sus campos sin establecer un
orden entre ellos. En ambos casos, para la secuencia y la alternativa en datos,
sentencias o gramáticas, las referencias a sus elementos en su definición y su
concreción (p.e. un tipo registro y una variable de ese tipo registro) mantiene la
misma cardinalidad de relaciones con sus elementos (p.e. el tipo registro tiene
un número de campos y una variable de ese tipo registro tiene el mismo
número de campos). Por contra, la definición de la alternativa (p.e. un tipo
registro variante) mantiene un número de relaciones con sus elementos
alternativos y su concreción (p.e. una variable de ese tipo registro) alberga una
única relación con el elemento escogido. Esta situación se repite en el código
estático de la sentencia compuesta (mecanismo de registro) con sus sentencias y
la ejecución dinámica de la sentencia compuesta y la ejecución de todas sus
sentencias; frente al código estático de la sentencia alternativa (mecanismo
alternativo) con sus ramas y la ejecución dinámica de la sentencia y la ejecución
de una de sus ramas.
46
Sobre la información, su estructura y su-gestión
Concluyendo, los 5 mecanismos de la estructura de la información pueden
quedar resumidos con 2 primitivas (átomo y referencia) y tres patrones
recurrentes (secuencia, registro y alternativa) construidos a partir de estos dos
mecanismos primitivos.
Quizás esta extrema simplificación asiente una posible formalización de la
estructura de la información a partir de átomos y sus relaciones en un espacio
n-dimensional. El próximo capítulo, persigue un objetivo similar pero menos
ambicioso: modelar léxico, sintáctica y semánticamente el lenguaje pascal, los
códigos de cualquier programa en Pascal y la ejecución de éstos mediante una
misma técnica: la tecnología orientada a objetos restringida a objetos (átomos) y
punteros (referencias).
47
III. Modelado de Pascal
1. Introducción
Queremos construir un sistema que represente los concetptos de la
programación con lenguaje Pascal usando la tecnología orientada a objetos. Hay
que dejar claro en qué grado nuestro programa va a ser orientado a objetos, ya
que todos sabemos que un programa escrito en un lenguaje orientado a objetos,
como Java, no tiene por que estar necesariamente orientado a objetos. Vamos a
usar la tecnología orientada a objetos principalmente para modelar Pascal. En
otras palabras, queremos representar un código fuente escrito en Pascal como
una estructura de objetos en memoria. Una vez que tengamos en memoria esos
objetos, los interpretaremos para realizar las mismas acciones que si se ejecutase
dicho programa.
48
Sobre la información, su estructura y su-gestión
Como se puede ver, existen dos partes muy diferenciadas en el nuestro
modelado:
-
Tiempo de compilación
Tiempo de ejecución
La parte encargada de la compilación generará una serie de objetos partiendo
de un código fuente. Posteriormente veremos con todo detalle tanto el proceso
de modelado de las clases que representan el lenguaje Pascal como la creación
de las instancias de dichas clases para representar un código fuente en
memoria.
La parte encargada de la ejecución interpretará esa estructura de objetos para
representar los conceptos y acciones que ocurren cuando se ejecuta dicho
código fuente. La idea principal en la que nos basamos para construir el
intérprete es la relación que existe en programa y proceso. Si un programa dicta
las reglas para que se ejecute un proceso, un programa actúa de definición y el
proceso actúa como particularización. Llevado esto a la tecnología orientada a
objetos, las definiciones se representan por clases y las materializaciones se
representan por instancias. Por tanto, el objetivo es que a partir de cada
elemento de un código fuente se cree una clase que le represente, y cada vez
que ese elemento se vea reflejado en la ejecución, se creará una instancia de
dicha clase. Más adelante buscaremos la forma de llevar a cabo esta idea.
2. Tiempo de Compilación
Introducción
Para estudiar como podríamos modelar un código fuente en Pascal
comenzaremos con uno muy sencillo. A grandes rasgos veremos como se
podría representar como objetos. Iremos añadiendo cosas a nuestro programa
sencillo y veremos como va aumentando el número de objetos y el modelo se
49
Sobre la información, su estructura y su-gestión
hace mas completo. Cuando hayamos cogido un poco de soltura en la
representación de los elementos de un programa, comenzaremos un estudio
más riguroso de la jerarquía de clases necesaria para representar cualquier
programa en Pascal.
Partiremos del siguiente programa escrito en Pascal:
PROGRAM EJEMPLO;
VAR
I:INTEGER;
BEGIN
I := 4;
END.
A grandes rasgos y sin especificar las clases de cada uno de los objetos, la
representación en memoria del programa anterior sería:
50
Sobre la información, su estructura y su-gestión
programa
Ejemplo
declaracion
Variable
nombre
tipo
“integer”
“EJEMPLO”
instruccion 1
“I”
Asignacion
I:=4
referencia al Tipo
Referenc
Tipo
Entero
nombre
Cuerpo
programa
Ejemplo
Declaración
Variable
I
definicion
nombre
variable
variable
Definicion
Variable
I
cuerpo
parte Derecha
parte Izquierda
Valor4
Referencia
Variable
I
referencia a la Variable
“I”
Parece que tendremos una clase para representar el programa, sus atributos
serán el nombre, la variable y la instrucción. Vemos que los nombres se pueden
representar como cadenas de caracteres. También tendremos que tener una
clase para representar la declaración de una variable, y sus atributos serán el
nombre y el tipo. Otra clase representaría la asignación, con un nombre de
variable y el valor.
Si añadimos una variable más y una instrucción más al programa, teniendo
por ejemplo:
51
Sobre la información, su estructura y su-gestión
PROGRAM EJEMPLO;
VAR
I:INTEGER;
J:CHAR;
BEGIN
I := 4;
J := ‘a’;
END.
Vemos que los atributos que teníamos en la clase para representar un
programa se convierten en arrays o secuencias, ya que en un programa pueden
existir muchas instrucciones y también muchas declaraciones de variables. La
representación en memoria del programa sería:
programa
Ejemplo
declaracion
Variable
1
declaracion
Variable
2
nombre
cuerpo
nombre
variable
Declaración
Declaración
Variable
Variable
“I”
I
J
definicion
definicion
variable
variable
nombre
variable
Definicion
Definicion
Variable
Variable
“J”
I
J
referencia al Tipo
Referenc
Tipo
Entero
referencia al Tipo
Referenc
Tipo
Caracter
Cuerpo
programa
Ejemplo
“EJEMPLO”
instruccion 1
instruccion 2
Asignacion
I:=4
Asignacion
J:=’a’
parte Derecha
parte Izquierda
parte Derecha
parte Izquierda
Valor4
nombre
tipo
“integer”
Valor ‘a’
nombre
tipo
“char”
Referencia
Variable
I
referencia a la Variable
“I”
Referencia
Variable
J
referencia a la Variable
“J”
Vamos a añadir una sentencia condicional a nuestro programa. Quedaría:
52
Sobre la información, su estructura y su-gestión
PROGRAM EJEMPLO;
VAR
I:INTEGER;
J:CHAR;
BEGIN
IF TRUE THEN
I := 4
ELSE
J := ‘a’;
END.
Nuestra estructura de objetos quedaría por tanto:
programa
Ejemplo
declaracion
Variable
1
declaracion
Variable
2
nombre
cuerpo
nombre
variable
Declaración
Declaración
Variable
Variable
“I”
I
J
definicion
definicion
variable
variable
nombre
variable
Definicion
Definicion
Variable
Variable
“J”
I
J
referencia al Tipo
Referenc
Tipo
Caracter
nombre
tipo
nombre
tipo
“EJEMPLO”
instruccion 1
Sentencia
Condicional
referencia al Tipo
Referenc
Tipo
Entero
“integer”
Cuerpo
programa
Ejemplo
parte Evaluable
parte THEN
Asignacion
I:=4
Asignacion
J:=’a’
parte Derecha
“char”
parte Izquierda
parte Derecha
parte Izquierda
Valor4
Referencia
Variable
I
referencia a la Variable
“I”
53
Valor
TRUE
parte ELSE
Valor ‘a’
Referencia
Variable
J
referencia a la Variable
“J”
Sobre la información, su estructura y su-gestión
Una clase para representar la sentencia condicional de nuestro ejemplo
tendría tres atributos, uno de ellos para representar el valor, otro para
representar la parte THEN y el último para representar la parte ELSE. Vemos
que los atributos para la parte THEN y para la parte ELSE serían una secuencia
compuesta, ya que pueden existir varias instrucciones en cada parte.
Podemos observar que la sentencia condicional y la sentencia de asignación
tienen que colocarse en la misma secuencia, al fin y al cabo, las dos son
instrucciones. Por tanto, parece razonable crear una clase padre para ambas o
un interfaz que las dos implementen y que sean tratadas de forma polimórfica.
Vemos que la cosa se complica por momentos. Indicar aquí toda la evolución
desde estos primeros bocetos hasta dar con una jerarquía de clases adecuada
podría aburrir al lector. Pasaremos por tanto a estudiar la jerarquía de clases
propuesta para modelar el lenguaje Pascal, es decir, que las instancias de esas
clases representarán un código fuente concreto.
Modelado del Lenguaje Pascal
Introducción
El problema fundamental que tenemos a la hora de modelar un lenguaje de
programación es que tenemos que tener muy claro que representa cada
concepto. Por tanto, antes de poder indicar que como es la clase que representa
un concepto, tenemos que dar una breve descripción de lo que para nosotros
representa ese concepto. Esto es importantísimo, ya que un mismo concepto,
dependiendo desde el punto de vista desde donde se mire, podría estar
representado por una clase u otra totalmente distinta.
Otra cosa que hay que comentar antes de comenzar a modelar es la falta de
ortogonalidad. Por ejemplo, se pueden hacer una llamada a un subprograma
54
Sobre la información, su estructura y su-gestión
dentro del propio subprograma para hacer llamadas recursivas. En cambio esto
no es posible cuando se trata del principal. Este tipo de excepciones en la
estructura general no serán contempladas en el modelado. Ya veremos como se
controlarán en sucesivos capítulos.
Vamos a hacer un breve repaso de los conceptos más generales que aparecen
en Pascal. Éstos son el concepto de evaluable, de sentencia, de declaración, de
definición, de referencia, de elementos predefinidos, de ámbito, y los conceptos
de tipos y valores. Posteriormente haremos un repaso un poco más detallado de
otros elementos que aparecen en un código fuente.
Evaluable
Consideramos elementos evaluables todos aquellos de los que se podrá
obtener un valor en tiempo de ejecución. En Pascal son muy dispares los
elementos de los que se puede obtener un valor en tiempo de ejecución. Un
valor, por ejemplo el 5, es evaluable, ya que en tiempo de ejecución él será su
propia evaluación. La llamada a una función también es evaluable, ya que
cuando se ejecute devolverá un valor. Las expresiones son evaluables, por
ejemplo, la expresión constante 4 + 92 es evaluable, en ejecución se evaluará con
el valor 96. Las variables también son evaluables, ya que en tiempo de
ejecución, tendrán un valor asociado y ese valor será el que se obtendrá de ellas
al evaluarlas. Por último nos quedan las constantes, la constante MAXINT de
Pascal, en tiempo de ejecución se evaluará como 32.676.
Para que no existan dudas, veremos algunas cosas que no son evaluables.
Por ejemplo en Pascal una asignación no es evaluable, porque en tiempo de
ejecución no se obtiene un valor de ella. Hay que tener claro esto, porque en C
una asignación sí que es evaluable. Una declaración de una variable no es
evaluable. Una declaración de variable es la parte del código en la que se indica
55
Sobre la información, su estructura y su-gestión
el tipo y el nombre de una variable, pero de ese elemento del código no se
obtiene ningún valor en tiempo de ejecución.
Sentencia
Consideramos sentencias aquellos que pueden aparecer solos en una línea
del cuerpo de un programa o de un subprograma. Las sentencias de Pascal son
la asignación, la llamada a un procedimiento y las sentencias de control de flujo
de ejecución, es decir, if, for, while, repeat y case. La sentencia with,
obviamente, también es una sentencia.
De igual forma vamos a ver los elementos que no consideramos sentencias.
Por ejemplo, la declaración de una variable no se considera una sentencia. En
otros lenguajes como Java, una asignación se consideraría una sentencia, ya que
se pueden declarar variables en medio de otras sentencias. La definición de un
subprograma no es una sentencia, ya que no se puede declarar un subprograma
en el cuerpo de otro subprograma.
Declaración
Se considera una declaración a cualquier elemento del código fuente que
asigne un nombre a otro elemento del código, al que llamaremos el elemento
definido, y que permita usar el elemento definido en el ámbito donde se
encuentre esa declaración. Por ejemplo, una declaración de una variable es una
declaración. Podemos ver que una declaración de una variable asocia el nombre
de la variable a un tipo. Una declaración de constante es también una
declaración, porque asocia el nombre a un valor. Una declaración de tipo
también es una declaración, ya que asocia un nombre a un tipo, ya sea primitivo
o no. Lo mismo ocurre con la declaración de un subprograma, son
declaraciones porque asocian un nombre a la definición de un subprograma
que consta de parámetros, cuerpo, etc... Por último una declaración de un
56
Sobre la información, su estructura y su-gestión
parámetro también es una declaración, ya que asocia un nombre a un tipo y a
una posición determinada de la lista de parámetros. Un programa principal
también es una declaración, porque se asigna un nombre a la definición del
programa formada por subprogramas, variables, etc. Hay que tener en cuenta,
que no se permite referenciar al propio programa en su propio ámbito, lo que
sin duda es una falta de ortogonalidad del lenguaje Pascal, pero como hemos
comentado al principio, ese tipo de situaciones no se consideran en el
modelado.
Definición
Todo aquel elemento del código fuente que define un nuevo elemento.
Elementos que se pueden definir son nuevos tipos, nuevos procedimientos,
nuevas variables, nuevas constantes, etc... Existen definiciones que pueden
existir en un código fuente sin aparecer formando parte de una declaración. Por
ejemplo, existen los tipos anónimos, son nuevos tipos pero que no tienen un
nombre asociado. Aunque la mayoría de los elementos que se definen son
nombrados y por tanto aparecen formando parte de una declaración. Por
ejemplo las variables, constantes, procedimientos, funciones, etc...
Elementos predefinidos
En todos los lenguajes de programación existen elementos predefinidos. Son
elementos declarados, con un nombre asociado, que pueden ser usados en
cualquier parte del programa. Las declaraciones no aparecen en el código
fuente, pero conceptualmente son declaraciones, ya que asocian un nombre a
elementos que pueden ser usados en cualquier parte del programa.
Referencia
Consideramos referencia al uso de un elemento declarado. Por ejemplo, el
nombre de una variable en una sentencia de asignación es una referencia a la
variable previamente declarada. La llamada a un procedimiento, es decir, el
57
Sobre la información, su estructura y su-gestión
nombre del procedimiento junto con sus parámetros actuales, si tiene, sería una
referencia al procedimiento previamente declarado. El uso de una constante en
la parte derecha de una asignación también es una referencia a la constante
previamente declarada. Cuando ponemos el nombre de un tipo en la parte
derecha de una declaración de variable estamos haciendo referencia al tipo,
hacemos referencia a él por su nombre. Es decir, en cualquier parte del código,
en la que pongamos el nombre para hacer referencia a un elemento
previamente declarado, se considera una referencia. Hay veces que las
referencias llevan asociadas además del nombre más información, por ejemplo,
en la llamada a un subprograma se usan los argumentos.
Ámbito
Sabemos que una declaración asocia un nombre a un elemento del código.
Una referencia básicamente es un nombre que indica que queremos usar el
elemento al que se le ha asociado dicho nombre. Dos elementos no se pueden
estar declarados con el mismo nombre si son del mismo tipo, ya que ante una
referencia no se podría determinar a que elemento nos estamos refiriendo.
Existen ocasiones en la que las referencias a elementos de distinto tipo se hacen
exactamente igual, por lo tanto, dos de estos elementos no podrían estar
declarados con el mismo nombre ya que no se podría determinar a qué
elemento corresponden. Por otro lado, un programa puede ser muy grande y
puede
tener
muchos
elementos
declarados.
Además,
podemos
usar
procedimientos programados por otras personas que declaren variables con
unos nombres que nosotros ni siquiera sepamos que existen. Debido a que los
nombres no pueden coincidir, necesitamos dividir los nombres en grupos para
que se puedan repetir los nombres sin problemas y crear reglas para ver en que
circunstancias se pueden dos elementos con el mismo nombre y en cuales no.
58
Sobre la información, su estructura y su-gestión
Si en cualquier parte del código se pudiesen crear referencias a los elementos
declarados en cualquier parte del código, al final la gestión de un código con
esas características sería muy difícil.
Por un lado para gestionar la colisión de nombres y por otro para restringir
las referencias a los elementos declarados en cualquier parte del código se ha
creado el concepto de ámbito. Un ámbito es un conjunto ordenado de
declaraciones y referencias. Cada ámbito está asociado unidireccionalmente a
otro ámbito llamado ámbito superior. Varios ámbitos pueden estar asociados al
mismo ámbito formando un árbol. El ámbito raíz del árbol no tiene ningún
ámbito superior. En un código fuente, cada una de las definiciones y referencias
pertenecen a un ámbito determinado.
Nos quedan por saber principalmente dos cosas. La primera es agrupar las
declaraciones y referencias de un código fuente en distintos ámbitos. La
segunda cosa es saber qué reglas existen en esta estructura jerárquica para
resolver la colisión de nombres y qué elementos se pueden referenciar en cada
uno de los ámbitos.
Cuando se declara un subprograma existe un nuevo ámbito. Todas las
declaraciones y referencias que existen en la definición de un programa
pertenecen a ese nuevo ámbito. El ámbito superior de ese nuevo ámbito es al
ámbito en el que se declara el subprograma. El nombre del subprograma
pertenece al ámbito en el cual se declara el subprograma. El orden en el que
aparecen las declaraciones y referencias dentro de un ámbito es el orden en el
que aparecen en el código fuente.
Las reglas que se aplican en los ámbitos son las siguientes:
59
Sobre la información, su estructura y su-gestión
No pueden existir dos declaraciones en el mismo ámbito que asocien el
mismo nombre a dos elementos del mismo tipo. Por ejemplo, en un mismo
ámbito no puede haber dos variables que se llamen igual.
Tampoco se pueden existir en un mismo ámbito dos declaraciones de
elementos de distinto tipo si es posible es exista ambigüedad a la hora de
determinar cual es el elemento referenciado. Por ejemplo, si existiese una
función sin parámetros de nombre a y una variable de nombre a, cuando en un
código fuente apareciese el nombre a en la parte derecha de una asignación no
se podría determinar si es una referencia a la variable o si es una referencia a la
función, por lo tanto existiría ambigüedad y esto no estaría permitido.
Si pueden existir dos declaraciones en ámbitos distintos que asocien el
mismo nombre a dos elementos del mismo tipo o en los que exista ambigüedad.
Solo pueden existir referencias a declaraciones hechas en el mismo ámbito o
en ámbitos superiores. Si la referencia aparece en el mismo ámbito que la
declaración, la declaración debe aparecer en el código fuente antes de que
aparezca la referencia.
Es cierto que es posible hacer un tipo de declaraciones especiales en las que
se asocia un nombre a una definición incompleta y posteriormente se asocia el
mismo nombre a una definición que completa la anterior, pero esta
característica no la vamos a considerar para no complicar mas el modelado.
Tipos y Valores
Estos dos elementos están íntimamente relacionados entre sí, así que los
estudiaremos conjuntamente. Para nosotros un tipo representa un conjunto de
valores. Para determinar si dos valores son del mismo tipo hay que comprobar
si pertenecen al mismo conjunto. Los tipos predefinidos del lenguaje Pascal son
el tipo de los enteros, el tipo de los caracteres, el tipo de los reales, el tipo de los
lógicos, etc... Estos tipos predefinidos se encuentran declarados, es decir, se ha
asociado a ellos un nombre para que puedan existir referencias a ellos, el
60
Sobre la información, su estructura y su-gestión
nombre asociado al tipo de los enteros es “integer”, el de los caracteres es
“char”, el de los reales es “real”, el de los lógicos es “boolean”, etc...
Coerción de Tipos
Los operadores y la sentencia de asignación tienen una característica que
permite relajar la compatibilidad de tipos. En ciertas ocasiones, partiendo de
valores de algunos tipos predefinidos se genera automáticamente un valor de
otro tipo para satisfacer la compatibilidad de tipos. Por ejemplo, en la siguiente
expresión:
5 / 3
Aparece el operador división real / que requiere operadores reales. Nosotros
ponemos operadores de tipo entero y él, automáticamente genera unos valores
de tipo real que representan la misma información que los de tipo entero.
Nosotros no vamos a modelar esta capacidad de los valores o de los tipos. De
tal forma que si un parámetro se define que es de tipo entero, no podrá ser de
otro tipo para ser correcto.
Diagramas de clases
Vamos a hacer un breve repaso a cada una de las clases e interfaces usados
para representar los elementos del lenguaje Pascal.
Interfaz Evaluable
Este interfaz es el que deben de implementar todas aquellas clases que vayan
a representar elementos evaluables del código fuente. El único método que
tiene este interfaz es el método que permite inspeccionar al objeto por el tipo
del valor que devolverá en ejecución. Con el valor del tipo se harán las
comprobaciones semánticas pertinentes. Los elementos evaluables son los
valores, las referencias a variables, las referencias a constantes y las funciones.
61
Sobre la información, su estructura y su-gestión
Quizás puede sorprender al lector que no hayamos mencionado las expresiones
en ningún lugar de este documento, y parece que son de obligado
nombramiento en esta última lista. La respuesta está en como vamos a modelar
las expresiones, como simples funciones con unas características peculiares. Ya
lo veremos cuando hablemos de las funciones predefinidas.
Interfaz Sentencia
Este interfaz es el que deben implementar todos aquellos elementos del
lenguaje que representen a una sentencia. Le implementarán la sentencia de
asignación, la sentencia condicional, la sentencia while, for, repeat y with, y la
llamada a un procedimiento. De momento no tiene ningún método.
Interfaz Definición
Este interfaz es el que implementan todos aquellos elementos que
representan una definición. Le implementarán la definición de tipo, de variable,
de constante, de procedimiento, de función y de programa. De momento no
tiene ningún método.
Clase abstracta Declaración
Una declaración es la asociación de un nombre a una definición, por tanto los
atributos de esta clase son el nombre y la definición. Dependiendo del tipo de la
declaración la definición será de una clase o de otra.
Clase abstracta Referencia
Esta clase representa todas las referencias que podamos encontrar en un
código fuente. Tiene dos atributos, el nombre y la declaración en la que se da
nombre al elemento al que referencia. Los métodos que tiene la clase son los de
acceso al nombre y a la declaración.
62
Sobre la información, su estructura y su-gestión
Clase Asignación
Esta clase representa a la asignación. Tendrá un atributo para representar la
referencia a la variable de la parte izquierda y un atributo evaluable de la parte
derecha de la asignación.
Clase Condicional
Esta clase representa una sentencia condicional, el if. Tendrá tres atributos,
uno para mantener la expresión, es decir, será del tipo Evaluable. El otro será la
parte Then, que será una sentencia. Por último tendrá un atributo, para
representar la parte Else si es que la tiene, si no, el valor de este atributo podrá
ser null. Los métodos de la clase serán los necesarios para la consulta de sus
atributos.
Veamos la jerarquía de herencia de las sentencias:
63
Sobre la información, su estructura y su-gestión
Sentencia
Sentencia
Compuesta
Control
Flujo
Asignacion
With
Iterativa
Condicional
For
Indeterminada
While
Repeat
Clase abstracta DefTipo
Hay varias formas de definir un tipo. Esta clase representa el concepto de
definición de tipo. Sus subclases instancibles representan cada una de las
formas concretas de definir un tipo.
Clase DefTipoSinonimo
Una forma de definir un nuevo tipo es mediante lo que se conoce como
sinónimo. El nuevo tipo es exactamente igual que su sinónimo, pero al
declararle hay que darle un nuevo nombre. La clase que represente esta
definición de tipo tiene solamente un atributo de la clase RefTipo del que crea el
sinónimo.
64
Sobre la información, su estructura y su-gestión
Clase abstracta DefTipoConstruccion
Esta clase, hermana de DefTipoSinonimo se usa para representar aquellas
formas de crear nuevos tipos que no son iguales que otros, si no que su
estructura es distinta a cualquiera que exista.
Clase abstracta DefTipoConstSimple
Esta clase representa a aquellos tipos que para su construcción no se basan en
otros existentes. Sigue siendo abstracta porque hay mas subclasificaciones
dentro de esta idea.
Clase abstracta Ordinal
Esta clase representa aquellos tipos considerados ordinales. No es
instanciable porque no se puede definir un tipo ordinal directamente.
Clase Enumerado
Esta clase representa a los tipos enumerados. Es instanciable porque el
usuario puede crear sus tipos enumerados. Cada nuevo tipo enumerado que
aparezca en el código fuente corresponde con una nueva instancia de esta clase.
Por tanto su atributo será una secuencia ordenada de identificadores.
Clase Lógico
Esta clase representa el tipo predefinido llamado boolean. Los identificadores
de los que se compone este tipo son “true” y “false”, en este orden.
Clase Entero
Esta clase representa el tipo entero predefinido.
Clase Caracter
Esta clase representa el tipo carácter predefinido.
65
Sobre la información, su estructura y su-gestión
Clase abstracta DefTipoConstCompuesto
Esta clase representa aquellos tipos que se basan en otros tipos para su
construcción. Es abstracta porque hay formas concretas de definir un tipo
compuesto.
Interfaz AccesoDefTipo
Esta clase es un poco peculiar. Sabemos que hay dos formas de representar
en el código un tipo. Podemos usar una referencia a un tipo existente (instancia
de RefTipo) o bien podemos crear uno con su constructor, por ejemplo al crear
un array. Hay partes del código en las que se puede indicar el tipo de
cualquiera de las dos maneras. Por ejemplo al indicar el índice de un nuevo tipo
array, podemos usar un tipo subrango existente, por ejemplo diasSemana, o
bien podemos crear uno en ese mismo momento, por ejemplo 3..6. Resulta
necesaria la construcción de un interfaz que implanten tanto RefTipo como
DefTipo. Este será el interfaz AccesoTipo. El único método de este interfaz será
el método para obtener el DefTipo asociado. Si es un RefTipo, este método
devolverá el objeto de su atributo, si es DefTipo, este método devolverá a él
mismo.
Clase Array
Esta clase representa a los tipos array. Un array de enteros será una instancia
de esta clase. Tiene un atributo de la clase AccesoDefTipo.
Clase Registro
Representa un nuevo tipo registro. En el subconjunto de Pascal que estamos
modelando no existen los registros variantes. Esta clase estará formada por una
secuencia de DefVariable.
Clase Puntero
Representa al tipo puntero. Simplemente tiene un atributo de la clase
RefTipo.
66
Sobre la información, su estructura y su-gestión
Interfaz TipoDeValorAtomico
Este interfaz lo van a implantar aquellos tipos cuyos valores son atómicos,
indivisibles.
Interfaz TipoDeValorCompuesto
Este interfaz lo van a implantar aquellos tipos cuyos valores son compuestos.
Veamos la jerarquía de clases de la definición de tipos:
67
Sobre la información, su estructura y su-gestión
Acceso
DefTipo
RefTipo
DefTipo
DefTipoSinonimo
DefTipoConstrucc
Valor
atomico
Simple
Ordinal
Valor
Estructurado
Compuesto
Real
Subrango
Registro
Puntero
Entero
Char
Array
Enumerado
Logico
Clase DefConstante
Esta clase representará la definición de una constante. No consideramos el
nombre como parte de la definición de la constante, si no que es en la
declaración de la constante donde se asocia al nombre a la definición. Por tanto,
una definición de constante solo tiene un atributo de tipo valor.
68
Sobre la información, su estructura y su-gestión
Clase DefVariable
Esta clase representará la definición de una variable. Seguimos con la idea de
que el nombre se asocia a la variable en la declaración, y no en la definición. Por
tanto la información que tiene que albergar la clase DefVariable es el tipo de la
variable. Por tanto tiene un atributo de la clase AccesoTipo.
Clase DefSubprograma
Esta clase representará la definición de un subprograma. Estará formada por
un cuerpo, y las secuencias de declaraciones, en las que estarán las
declaraciones de parámetros, de tipos, de subprogramas, etc...
Clase DefFunción
Esta clase va representar a las funciones. El único atributo que tiene, además
de los heredados es la referencia al tipo, es decir, tiene un atributo de la clase
RefTipo. Como hemos comentado mas arriba, en nuestro modelado de Pascal
no existen las expresiones. A nuestro parecer todo los aspectos relacionados con
expresiones, que hacen que sean diferentes a las funciones predefinidas son
aspectos sintácticos. Por este motivo vamos a modelar las expresiones de
operadores predefinidos como llamadas a funciones predefinidas. Será en el
momento de la conversión de texto a objetos cuando se tengan que tomar las
decisiones referentes a asociatividad y precedencia, pero estas cuestiones no se
va a reflejar en el modelo.
Clase DefParametro
Esta clase que representa a los parámetros, amplía la clase DefVariable para
poder especificar si los parámetros son por referencia o por valor.
Veamos la jerarquía de definiciones:
69
Sobre la información, su estructura y su-gestión
Definicion
DefVariable
DefParametro
DefConst
DefSubprograma
DefFuncion
DefTipo
DefProcedure
DefPrograma
Para las declaraciones, tenemos la siguiente jerarquía:
70
Sobre la información, su estructura y su-gestión
Declaracion
DecVariable
DecConst
DecParametro
DecSubprograma
DecFuncion
DecTipo
DecProcedure
DecPrograma
Clase abstracta RefSubprograma
Esta clase va a representar la llamada a un subprograma, ya sea una función
o un procedimiento. Es abstracta porque será necesario subclasificarla en
RefFuncion y RefProcedimiento. Obviamente hereda de la clase Referencia. Esta
clase tendrá el atributo nombre y declaración, que le vienen por herencia de la
clase Referencia y la lista de parámetros actuales. Los métodos de la clase, como
es habitual, serán para consultar sus atributos.
Clase RefProcedimiento
Esta clase representa una llamada a un procedimiento. Hereda de la clase
RefSubprograma. No incorpora ningún atributo ni método más. El cambio con
respecto a la clase padre es que es instanciable y que implementa el interfaz
sentencia.
71
Sobre la información, su estructura y su-gestión
Clase RefFuncion
Esta clase representa una llamada a una función. Hereda de la clase
RefSubprograma. El cambio respecto a la clase padre es que es instanciable y
que implementa el interfaz evaluable. El implementar el interfaz Evaluable
implica que tiene que implementar el método para determinar el tipo de la
función, cosa que hará preguntándoselo a la definición asociada. Hay que
recordar que las expresiones se van a modelar como llamadas a funciones, por
tanto, instancias de la clase RefFunción también representarán expresiones con
operadores.
La jerarquía de clases será:
Referencia
RefVariable
RefConst
RefSubprograma
RefTipo
Evaluable
RefParametro
RefFuncion
72
RefProcedure
Sentencia
Sobre la información, su estructura y su-gestión
No tenemos que olvidar que un valor es evaluable, por tanto:
Evaluable
Valor
Veamos ahora de una forma muy esquemática, las relaciones de asociación y
composición hacia cada uno de los cuatro conceptos generales:
Asignacion
ControlFlujo
ParActuales
*
Evaluable
Declaracion
DefSubprograma
*
*
Definicion
Declaracion
73
Sobre la información, su estructura y su-gestión
ControlFlujo
ControlFlujo
DefSubprograma
SentenciaCompuesta
*
Sentencia
Clase Ámbito
Como hemos visto, los ámbitos están ligados a la definición de los
subprogramas, por tanto, la gestión del ámbito podría hacerse en la propia
definición del subprograma, no obstante, debido a que las tareas del ámbito son
un poco mas complejas que el resto de funciones de los elementos del modelo,
hemos decidido crear un delegado para esta tarea. La clase ámbito es la elegida
para atender todas las peticiones que deberían llegar a la definición del
subprograma.
Instanciación de objetos. Análisis Léxico-Sintáctico.
Hemos visto de qué forma vamos a modelar un subconjunto del lenguaje
Pascal. El objetivo es que cada elemento de un código fuente del Pascal se
represente con una instancia. Ahora nos falta obtener esa representación en
memoria a partir de un código escrito de forma textual. En este tema veremos
cómo hemos construido el analizador léxico sintáctico y cuales son las rutinas
semánticas asociadas a cada producción para instanciar los objetos.
Para hacer el análisis léxico-sintáctico hemos usado una herramienta de
generación automática. La herramienta se llama JavaCC. Es una herramienta al
74
Sobre la información, su estructura y su-gestión
estilo de los tradicionales lex y yacc pero que genera código en lenguaje Java.
No vamos a detallar el funcionamiento de esta herramienta ya que queda fuera
del ámbito de este TFC. De todos modos, con el objetivo de que el lector no se
pierda, daremos un breve repaso a sus características más importantes.
La especificación léxica y sintáctica es conjunta, se realiza en el mismo
fichero. A partir esta definición y las rutinas semánticas asociadas a las
producciones la herramienta genera una clase en Java. Esta clase se incorpora
en nuestro programa como cualquier otra clase. Al instanciarse se la pasa un
flujo de entrada de texto y cuando queramos analizar un código fuente
proveniente de ese flujo y ejecutar las rutinas semánticas asociadas
simplemente ejecutamos un método. El método de análisis es LL(N), esto quiere
decir que ajusta automáticamente el número de caracteres por adelantado
necesarios para tomar decisiones sobre que producción expandir. Se
implementa usando el método predictivo-recursivo.
Respecto a las rutinas semánticas, la herramienta permite asociar líneas de
código a las producciones para que estas se ejecuten justo después de haber
aplicado una producción. También es posible el paso de parámetros a las
producciones y que las producciones devuelvan valores.
La gramática usada para definir Pascal, escrita en formato EBNF es la
siguiente:
programa
::=
<PROGRAM> id ";" bloque "."
bloque
::=
declaracion grupoSent
declaracion
::=
(
decCte
)?
(
decTip
)?
(
decVar
decSubprograma )?
decSubprograma
::=
( ( decProcedimiento | decFuncion ) )+
decCte
::=
<CONST> ( unaConst ";" )+
75
)?
(
Sobre la información, su estructura y su-gestión
unaConst
::=
<ID> "=" valorSimple
decTip
::=
<TYPE> ( unTipo ";" )+
unTipo
::=
<ID> "=" tipoEstr
tipoEstr
::=
( estrTab | estrReg )
estrTab
::=
<ARRAY> "[" valorSimple ".." valorSimple "]"
<OF> <ID>
estrReg
::=
<RECORD> listaVar ( ";" listaVar )* <END>
decVar
::=
<VAR> ( listaVar ";" )+
decProcedimiento
::=
<PROCEDURE> id ( "(" parFormales ")" )? ";"
bloque ";"
decFuncion
::=
<FUNCTION> id ( "(" parFormales ")" )? ":" id ";"
bloque ";"
parFormales
::=
listaParam ( ";" listaParam )*
listaParam
::=
( <VAR> )? id ( "," id )* ":" id
listaVar
::=
id ( "," id )* ":" id
grupoSent
::=
<BEGIN> sentencia ( ";" sentencia )* <END>
sentencia
::=
( ( asignacion | condicional | ciclica | llamada |
grupoSent ) )?
asignacion
::=
id ":=" expresion
condicional
::=
<IF> expresion <THEN> sentencia ( <ELSE>
sentencia )?
ciclica
::=
<WHILE> expresion <DO> sentencia
llamada
::=
id ( "(" parLlamada ")" )?
parLlamada
::=
( expresion ) ( "," expresion )*
expresion
::=
exprSimple ( opRel exprSimple )?
exprSimple
::=
( signo )? termino ( opAdt termino )*
termino
::=
factor ( opMul factor )*
76
Sobre la información, su estructura y su-gestión
factor
::=
( valorSimple | nombreEval | <NOT> factor | "("
expresion ")" )
nombreEval
::=
id
selector
::=
"[" expresion "]"
|
"." <ID>
valorSimple
::=
( signo )? <NUMERO>
opRel
::=
( "<" | ">" | "<=" | ">=" | "=" | "<>" )
opAdt
::=
( signo | "OR" )
opMul
::=
( "*" | "DIV" | "MOD" | "AND" )
signo
::=
( "+" | "-" )
id
::=
<ID>
Partiendo de esta gramática y de las clases del modelo, tenemos que definir
unas pautas para ir creando instancias a medida que vamos haciendo el análisis
léxico-sintáctico. El momento del análisis en el que se crean las instancias va a
estar muy determinado por los parámetros que tengan los constructores de las
clases. Veamos esto con un ejemplo. La parte de la gramática que especifica la
estructura textual de una asignación escrita con la sintaxis de JavaCC y sin
rutinas semánticas es la siguiente:
void asignacion() :
{
id() ":=" expresion()
}
Podemos poner una rutina semántica en cualquier espacio entre terminales y
no terminales, y será ejecutada cuando el analizador léxico-sintáctico pase por
ese lugar. La pregunta es ¿cuando instanciar el objeto de la clase asignación?. Si
el constructor de la clase asignación, requiere la parteIzquierda y la parte
77
Sobre la información, su estructura y su-gestión
derecha de la asignación, no podemos instanciar justo después de que se
reconozca el terminal “:=”, porque en ese momento no sabemos cuál es la parte
derecha de la programa. En nuestras manos también está determinar qué
parámetros se requieren para instanciar un objeto de la clase asignación,
entonces... ¿qué hacemos?.
La idea general que vamos a seguir, es que cualquier instancia de alguna
clase que modela el lenguaje Pascal, tiene que ser estructuralmente correcta.
Vamos a explicar que queremos decir con esto. Por ejemplo, si una asignación
siempre tiene una referencia a una variable en su parte izquierda y un elemento
evaluable en su parte derecha, no vamos a permitir que se instancie ningún
objeto de la clase Asignacion que no tenga un objeto RefVariable en su atributo
parteIzquierda y un objeto que implente el interfaz Evaluable en su atributo
parteDerecha. Dicho de otro modo, en el constructor de la clase Asignacion hay
que pasarle como parámetros la parteIzquierda y la parteDerecha.
Siguiendo esta idea, la especificación léxico-sintáctica junto con las rutinas
semánticas de una asignación será:
Asignacion asignacion() :
{
Evaluable evaluable;
Nombre n;
}
{
n = id() ":=" evaluable = expresion()
{ return new Asignacion(new RefVariable(n),evaluable); }
}
La rutina semántica se ejecuta cuando se ha completado el reconocimiento de
la asignación. De este modo sabemos cual es su parte izquierda y cuál es su
parte derecha. Vamos a repasar lo que hemos hecho. Cada parte de una
asignación genera un objeto que la representa. Estos objetos son recogidos en
variables temporales. Partiendo de los objetos obtenidos en la aplicación de los
78
Sobre la información, su estructura y su-gestión
no terminales construimos el objeto que representa la producción actual y le
devolvemos a una producción superior.
En la llamada al constructor de la clase Asignacion vemos como creamos un
objeto de la clase RefVariable. En algunos casos, la información que obtenemos
de los no terminales al ser aplicados no se le pasa directamente al constructor, si
no que sirve para construir un objeto que será el que se le pase al constructor.
Por último podemos observar la clase Nombre. Esta clase, con un String
como único atributo representa un nombre o identificador en el código fuente.
Veamos otro ejemplo en el que aplicamos esta idea para instanciar los
objetos. El caso de la sentencia condicional. Hemos dicho que el requisito para
instanciar un objeto es que éste sea estructuralmente correcto. Una sentencia
condicional se puede considerar estructuralmente correcta si tiene la expresión
y la parte then. La parte else es opcional. Por tanto, para ser coherentes, los
parámetros del constructor son la expresión y la parte then, y dejamos la
posibilidad de incorporar posteriormente la parte else. La especificación léxicosintáctica de una sentencia condicional sin rutinas semánticas sería:
void condicional():
{
<IF> expresion() <THEN> sentencia()
[<ELSE> sentencia()]
}
Los corchetes indican que esa parte de la producción puede que aparezca o
no. Al incorporarle las rutinas semánticas, la especificación quedaría:
Condicional condicional() :
{
Condicional condicional;
Evaluable evaluable;
Ejecutable ejecutable;
}
{
<IF> evaluable = expresion() <THEN> ejecutable = sentencia()
79
Sobre la información, su estructura y su-gestión
{ condicional = new Condicional(evaluable,ejecutable); }
[
<ELSE> ejecutable = sentencia()
{ condicional.setParteElse(ejecutable); }
]
{ return condicional; }
}
Podemos observar que mantenemos la misma idea de que el elemento sea
estructuralmente correcto y sólo añadimos la parte opcional cuando esta existe.
Otra situación que tenemos que considerar a la hora de ir creando las
instancias a partir del código fuente es cuando tenemos secuencias de
elementos. Como hemos visto al modelar Pascal, en muchas clases aparecen
atributos que son secuencias de objetos. Una secuencia estará representada
mediante una clase. Entonces nos planteamos la siguiente pregunta, ¿es lo
mismo una atributo secuencia con una instancia que no tiene elementos y un
atributo secuencia con el valor null? Para nosotros no. Por ello vamos a
considerar que una instancia de una clase que tiene atributos secuencia es
estructuralmente correcta cuando tiene el atributo secuencia con una instancia,
aunque dicha instancia no tenga ningún elemento. Por esto, cuando
instanciamos un objeto que tenga algún atributo secuencia, o bien le indicamos
todos los elementos de la secuencia en el constructor o bien el constructor de la
clase instancia un objeto instancia vacío. Vamos a usar la segunda opción, en el
constructor se va a instanciar la secuencia sin elementos. Se necesita que cada
clase con atributos secuencia tenga un método de acceso a dichas secuencias
para que podamos insertar los elementos en la secuencia.
Para comprobar como usamos esta idea en la instanciación veamos un
ejemplo. La secuencia de constantes de un programa o subprograma.
void programa() :
{
<PROGRAM> id() ";" bloque() "."
}
void bloque() :
80
Sobre la información, su estructura y su-gestión
{
[
[
[
[
decCte()
decTip()
decVar()
decSubprograma()
grupoSent()
]
]
]
]
}
void decCte() :
{
<CONST> ( unaConst() ";" )+
}
void unaConst() :
{
id() "=" valor()
}
Lo que haremos ahora será añadir las rutinas semánticas referentes a las
constantes para no complicar demasiado la especificación. La especificación
quedaría:
DefPrograma programa() :
{
DecPrograma decPrograma;
DefPrograma defPrograma;
Nombre n;
}
{
<PROGRAM> n = id() ";"
{
defPrograma = new DefPRograma();
decPrograma = new DecPrograma(n,defPrograma);
}
bloque(defPrograma) "." { return decPrograma; }
}
void bloque(Defprograma defPrograma) :
{
[ decCte(defPrograma.getConstantes())
[ decTip( ... )
]
[ decVar( ... )
]
[ decSubprograma( ... ) ]
grupoSent( ... )
}
void decCte(SecuenciaDefConstante constantes) :
{
<CONST> ( unaConst(constantes) ";" )+
}
void unaConst(SecuenciaDefConstante constantes) :
{
81
]
Sobre la información, su estructura y su-gestión
Nombre n;
Valor v;
}
{
n = id() "=" v = valor()
{constantes.addElement(new DecConstante(n,new DefConstante(v)));
}
}
Ambigüedad sintáctica
Hasta ahora, con los ejemplos que hemos visto, parece que simpre podemos
determinar sintácticamente de que clases hay que instanciar los objetos. Pero
supongamos que en el proceso de análisis léxico-sintáctico nos encontramos con
la siguiente línea:
variable := identificador;
Si esta línea es correcta dentro de su programa, la parte izquierda de la
asignación es una referencia a una variable, es decir, habrá que instanciar un
objeto de la clase RefVariable. Pero en la parte derecha no sabemos a que
corresponde ese identificador. Podría ser una referencia a una variable, una
referencia a una constante o una referencia a una función sin parámetros. No
sabríamos si instanciar un objeto de la clase RefVariable, RefConstante o
RefFuncion.
Ante este problema tenemos dos elecciones. Podemos instanciar un objeto en
el que no se tenga que especificar en este momento que tipo de referencia es,
por ejemplo instanciar un objeto de una clase llamada RefIncognita. O bien
podemos hacer que cuando nos encontremos en esta situación, se tenga acceso
al ámbito en el que se encuentra la asignación y así pueda determinar a que
corresponde la referencia. Hemos considerado mejor idea la segunda. Así que
en cada producción en la que exista la posibilidad de que dentro aparezca
alguna ambigüedad de este tipo se ha de pasar como parámetro el gestor del
ámbito en el que se encuentre la ambigüedad.
82
Sobre la información, su estructura y su-gestión
Cuando el análisis encuentre una ambigüedad se le pedirá al gestor del
ámbito que determine a que elemento corresponde el nombre indicado. Cuando
lo haya determinado nos devolverá una instancia de la clase correspondiente
dependiendo de donde encuentre el identificador. Si no encuentra el
identificador declarado, hay un error semántico.
Patrón Capa Observadora
Llegados a este punto hemos diseñado una jerarquía de clases que representa
los conceptos que aparecen en el lenguaje Pascal. También hemos dado unas
pautas para usar un analizador léxico-sintáctico para crear la jerarquía de
objetos partiendo de un código fuente escrito en texto. Nos falta una cosa
fundamental en nuestro diseño, ¿dónde hacemos las comprobaciones
semánticas?.
En el análisis léxico-sintáctico hacemos una tarea semántica para evitar cierta
ambigüedad. Pero nos quedan las tareas semánticas del resto de los elementos
del código. Tenemos que enlazar las referencias a sus definiciones. Si no existe
una definición con ese nombre, o el tipo de definición no corresponde con el
tipo de la referencia existe un error semántico. Si existe la definición tenemos
que asociar la referencia a la definición. En el caso de las referencias a funciones
o programas, además de comprobar que existe un parámetro con el mismo
nombre, hay que determinar si los parámetros son compatibles en cuanto
número y tipo. En las asignaciones también hay que hacer las comprobaciones
de tipos pertinentes.
Otra característica deseable sería poder visualizar el código fuente en
pantalla. Sería buena idea poder tener dos formas de visualizar el código
fuente. Una de ellas podría ser en forma textual. Sería bastante útil que esta
forma textual tuviese la misma sintaxis original de Pascal. Otra de ellas podría
ser gráfica. Debido a la naturaleza eminentemente jerárquica del código,
83
Sobre la información, su estructura y su-gestión
podríamos usar una visualización en forma de árbol del código, con la
posibilidad de expandir o colapsar ciertos nodos del árbol y así poder ver unas
partes u otras del código.
Vamos a analizar desde un punto de visto un poco mas abstracto que es lo
que queremos ahora. Tenemos una estructura de objetos en memoria.
Queremos que la información que representan esos objetos se vea representada
de varias formas. También queremos que dependiendo de la información que
representan algunos de esos objetos se cambien modifiquen para crear los
enlaces entre las referencias y las definiciones. Para hacer las comprobaciones
de compatibilidad de tipos y el resto de comprobaciones semánticas también
tenemos que comprobando ciertas cosas sobre la información que representan.
Vemos que la parte referente a comprobación de tipos y enlace de la referencia
con el elemento referenciado son partes esenciales para que la jerarquía de
objetos represente un programa correcto. En cambio, la visualización del
programa es una característica deseable pero no esencial para que la estructura
de objetos represente un programa correcto.
Otro detalle importante es que el número de visualizaciones diferentes que
se pueden obtener del mismo programa puede crecer bastante. Podría ser
interesante obtener un tercer tipo de visualización, híbrida entre el modelo
textual y el arbóreo.
Las conclusiones que podemos sacar es que necesitamos que en nuestra
estructura de objetos se comprueben ciertas cosas y se cambien otras. También
necesitamos poder obtener la información que representa la estructura de
objetos y visualizarla.
Hemos decidido que aquellas acciones sobre la estructura de objetos que
permiten obtener un programa correcto han de implementarse como métodos
dentro de las clases del lenguaje Pascal. Y las acciones sobre la estructura de
84
Sobre la información, su estructura y su-gestión
objetos que podríamos considerar auxiliares, como la visualización, las
implementaremos en otras clases.
La forma de implantar las rutinas semánticas en las clases y las acciones que
crean los enlaces va a consistir simplemente en añadir un método en las clases
mas altas de la jerarquía de herencia llamado resuelveReferencias.
La forma de implementar las clases para la visualización es algo mas
compleja, vamos a verlo con un poco mas de detalle. Queremos crear otras
clases que accedan a los atributos de las clases que ya tenemos. Veamos
distintas formas de crear estas clases:
Una clase y una instancia. Una instancia de esa clase va recorriendo el árbol.
Se asocia a cada uno de los objetos del árbol y obtiene su información para
visualizarla. Este enfoque presenta bastantes desventajas, por un lado, ¿Cómo
sabe un objeto de esta clase lo que le puede preguntar al objeto asociado? ¿Le
pregunta el tipo y entonces determina que mensajes lanzarle? Obviamente este
no es un enfoque orientado a objetos. Queda desechado.
Una clase y varias instancias. Tenemos el mismo problema que en el caso
anterior, aunque exista una instancia por cada instancia de la estructura de
objetos, no sabremos cuál es su tipo.
Varias clases y una instancia (por clase). Si existe una clase de acceso a la
información por cada clase del modelo de Pascal, tendremos una doble
jerarquía de clases. Esta solución permite que un objeto de acceso a la
información sólo se asocie a objetos de la clase que conoce, a objetos hermanos.
Por tanto, en la implantación de los métodos de cada una de estas clases el
tratamiento de la información es dependiente de la clase hermana. Una clase
por instancia parece un idea razonable siempre que no se necesite un acceso a la
información de forma aleatoria. Si se requiere un acceso secuencial a la
85
Sobre la información, su estructura y su-gestión
información, es viable que los objetos recorran el árbol y cada uno se encargue
de las instancias de su clase hermana. No obstante, existen formas de
implementar el acceso aleatorio con una única instancia por clase, pero son un
poco más complejos de implementar.
Varias clases y varias instancias. Con una jerarquía de clases paralela y una
jerarquía de objetos paralelos se permite un cómodo acceso a la información de
forma aleatoria. Lo que ocurre es que existe un mayor consumo de memoria.
Esta opción es la más cómoda de implementar y la que mas ventajas ofrece. Es
la opción que usaremos.
Hemos hablado un poco en general de cómo se podrían añadir
funcionalidades de acceso a la información de una estructura de objetos sin
modificar las clases de esa estructura de objetos.
Vamos a ver un ejemplo concreto. Queremos representar en forma de árbol
un código fuente. Para ello vamos a usar la clase JTree de Java. Esta clase
requiere, entre otras cosas, que los nodos del árbol tengan unos métodos
especiales de acceso a sus elementos hijos. Nuestra estructura de objeto no
implementa esos métodos de acceso. No estructura de objetos nunca va a
implementar esos métodos de visualización. Entonces necesitamos establecer
un puente entre la funcionalidad que se requiere de los nodos de un árbol para
visualizarse y la información que puede proporcionar nuestro árbol de objetos
del programa. Dicho de otra forma, necesitamos obtener información de cada
uno de los elementos de la estructura de objetos y con esa información haremos
frente a las preguntas realizadas por el JTree. La solución que hemos
comentado, es que exista una clase por cada clase del modelo de Pascal. Por
claridad, en nuestro prototipo hemos llamado a cada una de estas clases
hermanas con el nombre de la clase del modelo seguida de TreeNode, por
ejemplo la clase Asignación tiene como hermana la clase AsignaciónTreeNode.
Las clases TreeNode tienen métodos de consulta que son lanzados por la
86
Sobre la información, su estructura y su-gestión
instancia de la clase JTree en la que se visualice el árbol. Para responder
responder a esos mensajes, la instancia de la clase TreeNode pregunta a su
objeto asociado con los métodos de acceso. Sabe cómo preguntarle porque
conoce sus métodos, al fin y al cabo, es su hermano.
Hemos visto un ejemplo de cómo una capa observadora se usa para
visualizar código. Podríamos construir otra capa observadora para hacer
cualquier otro tratamiento de la información del código. En el último tema
veremos varias ideas que se pueden implantar usando este patrón.
3.Tiempo de Ejecución
Introducción
En el tiempo de compilación hemos obtenido una representación orientada a
objetos de un código fuente. Este modelado nos permite aclarar muchos de los
conceptos que se manejan a la hora de crear un programa, de escribir el código
fuente. En el tiempo de ejecución queremos hacer lo mismo. Queremos modelar
los conceptos que aparecen en la ejecución usando la tecnología orientada a
objetos.
Sabemos que un proceso es un programa en ejecución. El programa actúa
como definición del proceso. Un proceso concreto, es una particularización de
un programa. Un programa se puede ejecutar muchas veces con resultados
diferentes. Un programa define un conjunto de posibles procesos. En la
tecnología orientada a objetos, las definiciones se representan con clases. Y las
particularizaciones, se representan con objetos.
87
Sobre la información, su estructura y su-gestión
Modelado de la ejecución de un programa escrito en Pascal
Introducción
Ahora nuestra definición es un programa y su particularizaciones son
procesos. Por tanto, un programa cualquiera le tendremos que representar por
un conjunto de clases y su ejecución por un conjunto de objetos. Existen muchas
formas de plasmar esta idea, la de que un programa concreto esté representado
por un conjunto de clases relacionadas y cada una de sus ejecuciones estén
representadas por un conjunto de objetos. Modelar el tiempo de compilación o
desarrollo ha sido más sencillo, básicamente teníamos que representar
orientado a objetos la estructura que podía tener cualquier código fuente de
Pascal. La gramática léxico-sintáctica nos ha servido de inicio en el proceso,
aunque pronto nos hemos ido alejando más y más de ella. En ejecución estamos
perdidos, los conceptos no se plasman en ningún sitio de manera algo formal, al
menos un código fuente está en modo texto. En el modelado del lenguaje no
hay profundas diferencias entre la clase que representa un valor y la clase que
representa la referencia a una variable, al fin y al cabo, son clases que
representan información y tienen una pequeña lógica asociada para ver si esa
información es correcta. En el modelado del tiempo de ejecución la diferencia
entre la definición de una variable y la definición de un programa es abismal, la
diferencia entre la llamada a una función y la definición de un tipo son cosas
totalmente distintas. Mostraremos la idea que proponemos de modelado del
tiempo de ejecución.
Sentencias
Cada una de las sentencias de un programa se va a representar por una clase.
Cuando
exista
en
memoria
una
instancia
de
una
de
esas
clases,
conceptualmente consideraremos que la sentencia a la que representa se está
ejecutando. En el momento en que esa instancia deja de existir consideraremos
88
Sobre la información, su estructura y su-gestión
que se ha finalizado la ejecución de la sentencia a la que representa. Todas las
clases que representen una sentencia heredarán de una clase común.
Elementos Evaluables
Cada uno de los elementos evaluables que existan en un programa se va a
representar por una clase. Cuando exista en memoria una instancia de alguna
de estas clases, conceptualmente consideraremos que el elemento evaluable al
que representa está evaluándose. Cuando esa instancia deja de existir
consideraremos que se ha finalizado la evaluación. Todas las clases que
representen un elemento evaluable heredarán de una clase común. Tiene que
existir alguna forma de obtener un valor del elemento evaluable justo antes de
que desaparezca de memoria.
Variables, constantes y parámetros
Cualquier variable, constante o parámetro que aparezca en el código se va a
representar por una clase. Cuando exista en memoria una instancia de alguna
de estas clases, conceptualmente vamos a considerar que existe un dato, un
ítem de información. Cuando esa instancia deja de existir consideramos que ya
no existe ese ítem de información. Todas las clases que representan una
variable, constante o parámetro, heredarán de una clase común.
Valores
Los valores NO se representan por clases distintas. Todos los valores son
instancias de la clase Valor. Lo que ocurre es que un valor puede aparecer en el
código fuente en algún lugar evaluable. Por tanto deberá comportarse como un
elemento evaluable en esos casos.
89
Sobre la información, su estructura y su-gestión
Tipos
En Pascal los tipos no se representan de ninguna forma en tiempo de
ejecución. Cuando se está ejecutando un programa en Pascal en ningún
momento es necesario saber de que tipo es un valor o que tipo devuelve una
función. No es necesario porque en tiempo de compilaciones ya se han dejado
las cosas “preparadas” para que el tipo de todos los valores sea el esperado.
Información y Memoria
Como es lógico, en el tiempo de ejecución no se había hablado de la memoria
del ordenador. Conocemos la memoria como el lugar donde están las variables.
Queremos hacer una representación conceptual del concepto de memoria, así
que la vamos a renombrar. En realidad, el conjunto de variables, constantes y
parámetros, es decir, los ítems de información que se manejan en un programa
representan la información que es capaz de manejar ese programa. Basándonos
en esta idea, en el modelo de tiempo de ejecución existirá una clase llamada
Información que esté formada por todos los ítems de información existentes en
el programa en un instante determinado.
No vamos a hacer distinción entre memoria estática y dinámica, desde un
punto de vista conceptual todo es información. La diferencia entre los items de
información de memoria estática es que el momento de creación y el momento
de destrucción se pueden saben en tiempo de ejecución y en la memoria
dinámica el momento en que se crean y destruyen los ítems de información es
controlable por el propio programa.
Referencias a Variables, Constantes o Parámetros
Hay veces que la referencia a una variable, constante o parámetro es
considerada como un elemento evaluable, por tanto se le aplica lo dicho para
los elementos evaluables. Hay ocasiones en que una referencia a una variable o
90
Sobre la información, su estructura y su-gestión
parámetro se hace en la parte izquierda de una asignación. En tiempo de
ejecución, la función de una referencia es el acceso al ítem de información de la
variable a la que referencia.
Cada referencia a una variable, constante o parámetro se va a representar con
una clase. Si la referencia es tratada como un elemento evaluable, se aplica lo
visto para elementos evaluables. Es decir, cuando exista en memoria una
instancia de alguna de estas clases, conceptualmente consideraremos que el
elemento evaluable al que representa está evaluándose. Si la referencia a una
variable o parámetro, se encuentra en un lugar donde se necesite acceder al
item de información al que está referenciando, la instancia de la referencia se
trata de distinta forma. En este caso, cuando existe en memoria una instancia de
una referencia a una variable significa conceptualmente que se están realizando
las acciones necesarias para acceder al ítem de información de esa variable.
Ámbito
Ya sabemos que significa el concepto de ámbito en tiempo de compilación.
Un ámbito es un conjunto de declaraciones y referencias. Una vez que no existe
ninguna colisión entre las declaraciones, las referencias se enlazan a los
elementos declarados. Sólo los elementos declarados en el ámbito actual o
ámbitos superiores son accesibles desde el ámbito actual, es decir pueden ser
referenciados.
Como sabemos existen elementos de distintos tipos que son declarados y
referenciados. Los subprogramas, las constantes, las variables, los parámetros y
los tipos. En todos ellos existe el concepto de ámbito en tiempo de compilación.
Pero veamos si el concepto de ámbito sigue apareciendo en tiempo de
ejecución. Hemos dicho que los tipos no se representan en tiempo de ejecución,
por tanto el ámbito en los tipos no se va a reflejar tampoco. El ámbito en los
subprogramas sirve para saber qué subprogramas se pueden referenciar en una
determinada zona de código. En tiempo de compilación se determina que las
91
Sobre la información, su estructura y su-gestión
referencias
a
los
subprogramas
son
correctas,
es
decir,
referencian
subprogramas declarados en su ámbito o superiores. Por tanto, todas las
referencias a programas que aparezcan en tiempo de ejecución serán correctas.
Cuando en el proceso de ejecución se tenga que ejecutar la llamada a un
subprograma, se ejecutarán unas acciones previas y al final se ejecutará el
subprograma referenciado. Esto indica que el ámbito referente a subprogramas
en ejecución no se va a considerar.
Según hemos comentado antes, si una instancia de un elemento ejecutable se
encuentra en memoria, se considera
que ese elemento se está ejecutando.
Supongamos que el elemento ejecutable al que representa es la siguiente
asignación:
a := 4;
La instancia se crea en el momento de ejecutar esa asignación. El tiempo que
una instancia de esa asignación esté en memoria, depende de los pasos que
haya que seguir para asociar el valor 4 al ítem de información que representa a
la variable de nombre a. No se preocupe si se pierde en los pasos que hay que
seguir mas adelante lo explicaremos con mucho mas detalle. Lo que queremos
mostrar aquí es que el tiempo que pertenece en memoria la instancia que
representa a la asignación, depende solamente de la asignación o de alguna de
sus partes, pero no depende de nada más.
También habíamos comentado antes que una instancia de un elemento
evaluable representa que en ese momento se está produciendo la evaluación del
elemento. La instancia se crea en el momento en que se requiera evaluar el
elemento. De nuevo vemos que el tiempo que está la instancia en memoria
depende del elemento evaluable o de los elementos de los que se componga,
pero no depende del lugar donde se encuentre la evaluación. Dicho de otra
92
Sobre la información, su estructura y su-gestión
manera, independientemente del lugar donde se encuentra un elemento
evaluable el tiempo que tarda en evaluarse es el mismo.
Pero veamos que ocurre con las variables. Hemos dicho que cada variable de
un programa se representa con una clase. Si existe una instancia de una clase
que representa a una variable concreta significa que existe un dato o ítem de
información. Si esa instancia ya no existe, consideramos que ya no existe esa
información. La pregunta ahora es ¿cuanto tiempo está en memoria un
instancia de una variable? ¿cuánto tiempo permanece un ítem de información
concreto en el programa? ¿cuándo se instancia una variable? ¿cuándo se
destruye? La respuesta a estas preguntas tiene mucho que ver con el ámbito.
Antes de ver la relación repasemos un poco en que momento se instancian otros
objetos del tiempo de ejecución.
La ejecución de un procedimiento se realiza cuando aparece una referencia a
él en el cuerpo de otro subprograma, de su propio cuerpo, o del cuerpo del
programa principal. Es decir, la instancia que represente la ejecución de un
subprograma se creará a raíz de la creación de la instancia que representa la
ejecución de la llamada al subprograma. Como vemos, la instancia que
representa la ejecución de un subprograma se crea cuando se hace referencia a
ese subprograma.
Con las variables no ocurre eso. La referencia a una variable no invoca la
creación de un instancia de esa variable. Dependiendo del lugar del código
donde aparezca esa referencia implicará unas cosas u otras. Por ejemplo si
aparece en una expresión será considerada como evaluable, y si aparece en la
parte izquierda de una asignación será usada para acceder al item de
información al que esté asociado.
Por lo tanto, siguen sin respuesta las preguntas ¿cuándo se instancia una
variable? ¿cuándo se destruye? ¿cuanto tiempo está en memoria un instancia de
93
Sobre la información, su estructura y su-gestión
una variable? ¿cuánto tiempo permanece un ítem de información concreto en el
programa?. Veamos, hemos dicho que la variable se tiene que instanciar antes
de que se produzca ninguna referencia a ella en el programa. Es decir, el item
de información tiene que existir antes de que a través de una referencia sea
usado. También parece lógico que el ítem de información de una variable se
destruya si ya nadie puede acceder a él. Entonces el momento en el que se
instancia una variable y el momento en el que se destruye dependen del lugar
donde se pueda hacer referencia a la variable. Antes de que comience ese lugar
hay que instanciar las variables y justo cuando ese lugar acabe, habrá que
destruir las variables. Como el lector ya sabrá, es el ámbito el que determina
precisamente eso.
Por tanto, la representación del ámbito referente a la declaración y referencia
a las variables se verá reflejado en ejecución en que va a determinar el momento
en el que se creen las instancias de las variables y el momento en el que se
destruyan dichas instancias.
Diagrama de clases
La idea de que una instancia de un objeto representa que en ese momento se
está produciendo la ejecución, o evaluación, o que existe un ítem de
información o que se está accediendo a uno parece bastante razonable. Pero
existen algunas restricciones al llevar esta idea a código Java. Por ejemplo la
destrucción explícita de objetos no es posible. Otro pequeño inconveniente es
que en los constructores no se pueden devolver objetos. Ante esta situación
hemos optado por modificar un poco la idea inicial. La ejecución, evaluación,
etc… se van a representar con una instancia, pero además esta instancia tiene
que estar activa. Vamos a considerar que una instancia comienza a estar activa
cuando se le lance un determinado método. De igual forma, vamos a
representar la destrucción como “desactivar una instancia”, desactivaremos una
instancia cuando le lancemos un determinado método. Aunque hayamos
94
Sobre la información, su estructura y su-gestión
tomado esta decisión por problemas de implementación, vamos a considerar
que un objeto no existe para nosotros si no está activo y deja de existir cuando
se desactiva. Dicho de otro modo, justo después de instanciar un objeto hay que
activarle y justo después de que el objeto haya acabado su tarea hay que
desactivarle.
Hemos dado una idea muy general de cómo vamos a representar los
conceptos que aparecen en Pascal en tiempo de ejecución. Intentaremos
progresivamente entrar cada vez en más detalles. Basándonos en el modelado
del tiempo de compilación iremos dando un poco más detallado el modelado
en tiempo de ejecución.
Repasemos, la existencia de un objeto activo en memoria representa
ejecución, evaluación, ítem de información o acceso a un ítem. Cuando se
desactive una instancia supone que se ha acabado la ejecución, la evaluación,
que el ítem de información ya no existe o que ya se ha accedido al ítem.
Sentencia
Partiendo de cada una de las instancias de aquellas clases del tiempo de
compilación que implementen el interfaz Sentencia se ha de crear una clase que
herede
de
la
clase
EjecucionSentencia.
Las
instancias
de
la
clase
EjecucionSentencia van a representar la ejecución de la sentencia. Se considera
que la clase se activa justo antes de ejecutar el método:
void ejecutate(Informacion i);
El resultado de ejecutar este método es que se cambien los valores
pertinentes de los items de información que se indiquen. Por ejemplo, si a la
instancia que representa la asignación
var := 5;
95
Sobre la información, su estructura y su-gestión
se le lanza el mensaje ejecutate(i) lo que tiene que hacer es modificar el valor
del ítem de información que represente a la variable var que se encuentra en la
información i.
El método que va a representar la desactivación de la instancia de la clase
EjecuciónSentencia es:
void fin();
Por tanto al finalizar la ejecución del método fin() se entenderá que el objeto
está desactivado y ya no representa la ejecución de la sentencia.
Evaluable
El caso de los elementos evaluables es parecido. Partiendo de cada una de las
instancias de aquellas clases del tiempo de compilación que implementen el
interfaz Evaluable se ha de crear una clase que herede de la clase
EjecucionEvaluacion. Las instancias activas de la clase EjecucionEvaluacion van
a representar la evaluación. Se considera que la clase se activa justo antes de
ejecutar el método:
Valor evaluate(Informacion i);
El resultado de ejecutar este método es que se cambien los valores
pertinentes de los items de información que se indiquen y además, que se
devuelva el valor resultado de la evaluación. Por ejemplo, si a la instancia que
representa la llamada a esta función predefinida
pred(5);
se le lanza el mensaje evaluate(i) lo que tiene que hacer es ejecutar el código
asociado a la función y devolver el valor 6.
96
Sobre la información, su estructura y su-gestión
El método que va a representar la desactivación de la instancia de la clase
EjecuciónSentencia es:
void fin();
Por tanto al finalizar la ejecución del método fin() se entenderá que el objeto
ya no representa la evaluación.
Variables, Constantes y Parámetros. Items de Información
Como hemos dicho antes, por cada variable, constante y parámetro que
aparezca en un programa se tienen tiene que generar una clase hija de
ItemDeInformacion. La clase ItemDeInformacion tiene un atributo de la clase
Valor. Además tiene los métodos pertinentes para el acceso en lectura y
escritura del valor. En las subclases de ItemDeInformacion que representen
variables es posible que se lancen los métodos de lectura y escritura sobre el
valor. En los ítems de información que representen a constantes, en tiempo de
ejecución ya nos habremos asegurado de que en ningún momento se emplee el
método de escritura.
Para permitir el acceso al valor del ítem de información, la clase
ItemDeInformación tiene implementados los siguiente métodos:
Valor getValor();
void setValor(Valor v);
El método que activa el ítem de información es:
void creacion(Informacion i);
Tendrá que hacer las acciones necesarias para que el ítem de información
pase a formar parte de la información del programa. Además, si el ítem de
97
Sobre la información, su estructura y su-gestión
información corresponde a una constante o parámetro, en este método se le
asignará el valor pertinente. Conceptualmente, el ítem de información nace con
el valor adecuado.
La desactivación del ítem de información se realiza con el siguiente método.
void destruccion(Informacion i);
Se llevarán a cabo las acciones necesarias para que el ítem de información
deje de formar parte de la información del programa.
La clase ItemDeInformacion tiene tres hijas, cada una representa cada uno de
los distintos tipos de ítems que hay.
Variables
La clase ItemVariable representa a las variables. Implementa el método
creacion(Informacion i) para establecerse un valor aleatorio al construirse.
Constantes
La clase ItemConstante representa a las constantes. Implementa el método
creacion(Informacion i) para establecerse el valor indicado en la constante.
Parámetros
La clase ItemParametro representa a los parámetros. Para crear un parámetro
es necesario saber que valores han resultado de evaluar las expresiones de los
parámetros actuales. En caso de los parámetros por referencia, también es
necesario saber los ítems de información de los parámetros actuales. Por tanto
el método de creación necesita de toda esa información. Se ha decidido añadir
un nuevo parámetro al método para pasar esta información. El método creación
en la clase ItemParametro tiene la forma:
void creacion(Informacion i, ParametrosActuales pA);
98
Sobre la información, su estructura y su-gestión
Existen dos tipos de parámetros, los parámetros por valor y los parámetros
por referencia.
Los parámetros por valor son aquellos que al crearse obtienen el valor del
resultado de evaluar la expresión de su parámetro actual. Una vez que el ítem
de información de un parámetro por valor se ha creado ya no tiene ninguna
relación con el origen del valor.
Cuando se ejecuta un subprograma que tiene un parámetro por referencia y
dentro de ese subprograma se modifica el valor de ese parámetro, al finalizar la
ejecución del subprograma aparece modificado el valor de la variable. Un ítem
de información que representa a una variable que se pasa por referencia es el
mismo ítem de información que se usa dentro del subprograma. Al ser el
mismo ítem de información, el cambio de valor se ve reflejado al finalizar la
ejecución del subprograma.
Para poder representar que sea el “mismo” item el que represente al
parámetro actual y al parámetro formal, según nuestra idea general, una objeto
tendría que ser instancia de dos clases distintas, la clase ItemVariable y la clase
ItemParametro. Y esto no es posible. Por tanto, tenemos que elegir una forma
de representar conceptualmente esta idea en este modelo.
Vamos a usar la idea de C de paso de parámetros por referencia. En C no
existe el paso de parámetros por referencia. En C se obtiene esta funcionalidad
pasando como parámetro por valor la dirección de la variable, es decir, la forma
de acceder al ítem. La forma de implementarlo en nuestro modela va a ser
usando punteros como en C.
Veamos por encima como representamos un puntero en tiempo de ejecución.
Básicamente un puntero es una variable convencional, lo que ocurre que su
valor no es un valor como los demás, no es un 5, ni una ‘a’, su valor es la
99
Sobre la información, su estructura y su-gestión
dirección de un item de información. En nuestro modelo no podemos hablar de
dirección, en nuestro modelo lo que decimos es que el valor de una variable de
tipo puntero permite acceder al item de información. Y para permitir acceder
nos nos asociamos a él. En otras palabras, la referencia a un ítem de información
se puede considerar como un valor convencional. En nuestro modelo
representamos esta referencia con una relación de asociación, al igual que la
asociación que tienen los ítems con los valores.
En el modelo de ejecución un parámetro por referencia se comporta como el
paso del item de información por valor.
Referencias a Variables, Constantes y Parámetros.
Por cada referencia a variable, constante y parámetro que aparezca en un
programa se tienen tiene que generar una clase. Dependiendo del lugar donde
se encuentra la referencia, será tratada como un elemento evaluable o como un
elemento que sirve para acceder al item de información.
Cuando la referencia es tratada como un elemento evaluable se aplica lo que
ya hemos visto para estos elementos. Cuando se trata como un elemento que
sirve para acceder al ítem de información se han de ofrecer los métodos para
obtener esa funcionalidad, es decir, para acceder al ítem de información.
Por tanto, cada referencia a variable, constante y parámetro que aparezca en
un
programa
se
tienen
tiene
que
generar
una
clase
hija
de
RefItemDeInformacion. Esta clase implementa EjecuciónEvaluación. Por tanto,
tiene que implementar los métodos de EjecuciónEvaluación. Cuando se usen
estos métodos de la instancia, conceptualmente significará que el elemento está
siendo evaluado.
Para acceder al ítem de información, el método elegido es:
100
Sobre la información, su estructura y su-gestión
ItemDeInformacion getItemDeInformacion(Informacion i);
Por tanto, cuando se use este método, conceptualmente significará que se
está accediendo al ítem de información de entre toda la información del
programa.
Cuando se trate de un parámetro por referencia, como lo hemos modelado
como un puntero, acceder al item de información asociado requiere de un nivel
mayor de indirección. Igual ocurre cuando queramos obtener o asignar su
valor, necesitamos de un nivel mayor de indirección.
Valores
Cualquier valor se va a representar como una instancia de la misma clase
Valor. Que exista en memoria una instancia que represente el valor 3 significa
que el valor 3 está siendo usando en alguna parte del proceso, bien asociado a
un item de información o bien como resultado intermedio de la evaluación de
una expresión.
En algunas ocasiones, los valores aparecen en la parte evaluable del código.
Por
tanto,
debería
implementar
el
interfaz
EvaluableEjecución
para
comportarse como un elemento evaluable en ejecución. Por este motivo, cuando
se tiene que representar un valor en algún lugar evaluable del código no puede
aparecer como la instancia de la clase valor simplemente, si no que se tiene que
comportar como en elemento evaluable. Por este motivo, cuando aparece un
valor en la parte evaluable se crea una clase que representa la evaluación del
valor, pero no el valor en sí. El valor sigue representándose como una instancia
de la clase valor.
Un ejemplo básico
101
Sobre la información, su estructura y su-gestión
Vamos a poner un ejemplo básico para repasar los conceptos introducidos en
los apartados anteriores. Supongamos que tenemos el siguiente programa:
PROGRAM EJEMPLO;
VAR
I:INTEGER;
BEGIN
I := 4;
END.
Hemos comentado que cada uno de los elementos de este programa se van a
representar con una clase. Vamos a ver las clases que existen:
•
•
•
•
•
•
Clase ProgramaEjemplo
Clase VariableI
Clase CuerpoProgramaEjemplo
Clase SentenciaAsignacion
Clase RefVariableI
Clase AccesoValor4
Cada una de estas clases tiene que heredar, directa o indirectamente, de
ItemDeInformación,
EjecucionSentencia,
EjecuciónEvaluación
o
RefItemDeInformacion. Veamos:
•
•
•
•
•
•
Clase ProgramaEjemplo hereda de EjecucionSentencia
Clase VariableI hereda de ItemDeInformacion
Clase CuerpoProgramaEjemplo hereda de EjecucionSentencia
Clase SentenciaAsignacion hereda de EjecucionSentencia
Clase RefVariableI hereda de RefItemDeInformacion
Clase EvaluacionValor4 hereda de EjecucionEvaluacion
Vamos a ver que objetos se van activando y desactivando como resultado de
la ejecución de este programa. Veamos lo que sucede:
instante 1: Se instancia y activa un objeto de la clase ProgramaEjemplo, el
programa se está ejecutando.
102
Sobre la información, su estructura y su-gestión
instante 2: Se instancia y activa un objeto de la clase VariableI, existe un item
de información en nuestro programa que corresponde a la variable I.
instante
3:
Se
instancia
y
activa
un
objeto
de
la
clase
CuerpoProgramaEjemplo, se está ejecutando el cuerpo del programa.
instante 4: Se instancia y activa un objeto de la clase SentenciaAsignacion, se
está ejecutando la asignación.
instante 5: Se instancia y activa un objeto de la clase EvaluacionValor4, se
está evaluando el valor.
instante 6: Se desactiva el objeto de la clase EvaluacionValor4, ya se ha
evaluado el valor.
instante 7: Se instancia y activa un objeto de la clase RefVariableI, se está
accediendo al item de información de la variable I dentro de toda la
información del programa.
instante 8: Se desactiva el objeto de la clase RefVariableI, ya se tiene el item
de información de la variable I.
instante 9: Una vez que la asignación dispone de el valor resultado de
evaluar su parte derecha y del item de información de su parte izquierda, asocia
el valor al item.
instante 10: Se desactiva el objeto de la clase SentenciaAsignacion, se ha
terminado de ejecutar la sentencia de asignación.
instante 11: Se desactiva el objeto de la clase CuerpoProgramaEjemplo, se ha
terminado de ejecutar el cuerpo del programa.
instante 12: Se desactiva el objeto de la clase VariableI, ya no existe el item de
información de la variable I.
instante 13: Se desactiva el objeto de la clase ProgramaEjemplo, se ha
terminado de ejecutar el programa.
Vamos a repetir el proceso pero entrando un poco mas en detalle. Vamos a
ver que objetos se instancian, que métodos se ejecutan y que parámetros se
pasan a cada método.
103
Sobre la información, su estructura y su-gestión
instante 0: Se crea un objeto de la clase Información vacío, le llamamos i. No
hay ninguna información en nuestro programa todavía.
instante 1: Se crea un objeto de la clase ProgramEjemplo, le llamamos prog.
instante 1.5: Se lanza el mensaje prog.ejecutate(i), se comienza la ejecución
del programa.
instante 2: Dentro del método ejecutate instancia un objeto de la clase
VariableI, le llamamos variable.
instante 2.5: Dentro del método ejecutate de prog se lanza el mensaje
variable.creacion(i), se crea un item de información en nuestro programa que
corresponde a la variable I.
instante 3: Dentro del método ejecutate de prog se instancia un objeto de la
clase CuerpoPrograma, le llamamos cuerpo.
instante 3.5: Dentro del método ejecutate de prog se lanza el mensaje
cuerpo.ejecutate(i), se comienza a ejecutar el cuerpo del programa.
instante 4: Dentro del método ejecutate de cuerpo se instancia un objeto de la
clase SentenciaAsignacion, le llamamos asignacion.
instante 4.5: Dentro del método ejecutate de cuerpo se lanza el mensaje
asignacion.ejecutate(i), se comienza a ejecutar la asignacion.
instante 5: Dentro del método ejecutate de asignación se instancia un objeto
de la clase EvaluaciónValor4, le llamamos v4.
instante 5.3: Dentro del método ejecutate de asignación se lanza el mensaje
v4.evaluate(i), el valor 4 se está evaluando.
instante 5.6: El valor devuelto por el método se guarda temporalmente.
instante 6: Dentro del método ejecutate de asignación se lanza el mensaje
v4.fin(), ha finalizado la evaluación del valor.
Instante 7: Dentro del método ejecutate de asignación se instancia un objeto
de la clase RefVariableI, le llamamos referencia.
instante 7.3: Dentro del método ejecutate de asignación se lanza el mensaje
referencia.creacion(i), se está accediendo al item de información de la variable i.
instante 7.6: El ítem devuelto por el método se guarda temporalmente.
104
Sobre la información, su estructura y su-gestión
instante 8: Dentro del método ejecutate de asignación se lanza el mensaje
referencia.fin(), ha finalizado el acceso a la variable.
instante 9: Una vez que la asignación dispone de el valor resultado de
evaluar su parte derecha y del ítem de información de su parte izquierda, asocia
el valor al ítem.
instante 9.5: Finaliza la ejecución del método ejecutate de asignación.
instante 10: Dentro del método ejecutate de cuerpo se lanza el mensaje
asignación.fin(), se ha terminado de ejecutar la sentencia de asignación.
instante 10.5: Finaliza la ejecución del método ejecutate de cuerpo.
instante 11: Dentro del método ejecutate de programa se lanza el mensaje
cuerpo.fin(), se ha terminado de ejecutar el cuerpo del programa.
instante 12: Dentro del método ejecutate de programa se lanza el mensaje
variable.destruccion(i), ya no existe el ítem de información de la variable I.
instante 12.5: Finaliza la ejecución del método ejecutate de programa.
instante 13: Se lanza el mensaje programa.fin(), se ha terminado de ejecutar
el programa.
Queda por determinar cómo se define lo que ocurre en el instante 9, es decir,
la semántica de las sentencias. La idea es que estas sentencias se modelen con
autómatas, de esta forma, son formales.
No obstante, hemos dejado este punto como otra de las vias posibles de
ampliación de este TFC.
Obtención de clases a partir de un código fuente
Hemos marcado las pautas básicas para representar la ejecución de un
programa usando la orientación a objetos. Cada elemento del código fuente se
va a representar como una clase. Las instancias activas de esas clases
representan ejecución, evaluación, etc…
Vamos a automatizar el proceso de creación de esas clases partiendo de la
representación en objetos del código fuente. Seguiremos la siguiente idea para
105
Sobre la información, su estructura y su-gestión
automatizar el proceso: que cada objeto genere la clase que le representará en el
modelo de ejecución. Hay objetos que no se van a representar en tiempo de
ejecución, como los tipos. Estos objetos no tienen que generar ninguna clase.
Vamos a hacer que cada objeto del tiempo de ejecución escriba la definición
de su clase en modo texto en un flujo de salida. Todos los objetos del programa
escribirán en un mismo flujo de salida. Ese flujo de salida representa un código
fuente de Java. Una vez concluida la generación del código fuente en Java.
Posteriormente, ese código fuente escrito en Java se compila y ya tenemos las
clases que representan los elementos de un código fuente en particular. Si
queremos representar su ejecución simplemente instanciamos un objeto de la
clase que representa el programa principal y la activamos.
106
IV. Las innumerables ventajas y el
supuesto
inconveniente
con
sus
innumerables desventajas
En este capítulo vamos a mostrar las ventajas que aporta utilizar el patrón
propuesto tanto para la construcción de herramientas como para la
experimentación con los propios lenguajes. Hemos dividido el capítulo en
varios apartados, cada uno centrándose en un conjunto de aplicaciones
relacionadas. En los distintos apartados veremos cómo construir herramientas
dedicadas a:
•
entornos de desarrollo de aplicaciones,
•
procesadores de lenguajes,
•
prototipado de lenguajes de programación,
•
ingeniería del software,
•
asistencia a la educación, y
•
interfaces gráficos de usuario.
107
Sobre la información, su estructura y su-gestión
Aunque veamos varios apartados con aplicaciones del patrón totalmente
dispares, la ventaja de que el patrón sea común para todos ellos, permite crear
aplicaciones o herramientas que combinen varias de las posibilidades
mostradas de forma sencilla.
Por último, trataremos el omnipresente aspecto de la eficiencia.
1. Entornos de Desarrollo
En la actualidad, para construir entornos de desarrollo se requiere la
combinación de muchas tecnologías aparentemente muy dispares como
analizadores léxicos, sintácticos, gestión de ficheros objeto de los códigos
fuente, creación del interface de usuario, etc… Sin embargo, el poder usar una
representación orientada a objetos de un código fuente permite la construcción
de entornos de desarrollo mucho más rápida y cómoda al usar un patrón ya
definido como el que proponemos: capas observadoras que recubren el modelo
del lenguaje. Esto permite tratar al código fuente como un conjunto de objetos
relacionados
y
podremos
aplicar
las
técnicas
de
programación
“convencionales” para el tratamiento del código fuente, y no acceder a él
mediante complejos analizadores, conversores, etc…
Veamos en distintas secciones más detalladamente las posibilidades que
ofrece el patrón en la construcción de las partes de un entorno de desarrollo
postergando para otro apartado los compiladores e intérpretes del lenguajes.
Editores dirigidos por la sintaxis
Al disponer de la información del modelo del lenguaje (o del metamodelo de
un código de ese lenguaje, vease como dos caras de la misma moneda),
transitivamente, se dispone de la información sobre las correctas relaciones de
composición entre los elementos programáticos. Toda esta información conduce
a la posibilidad de dirigir la edición del código correcto suministrando las
108
Sobre la información, su estructura y su-gestión
posibilidades correctas en un punto dado y, en caso contrario, generación de
avisos justificados ante ediciones incorrectas.
Visualización del código
Desde que se crearon los primeros lenguajes de programación, un código
fuente se representa por un texto. Pese a que este texto mantenga una estructura
sintáctica el programador “necesita” tener una visión mas geométrica y gráfica
de lo que está programando. Por este motivo, un buen programador tiene la
costumbre de sangrar su código, es decir, hacer que las líneas de código fuente
comiencen un poco más a la derecha cada vez que se inicia un nivel de
anidamiento programático para representar "gráficamente" la jerarquía de
bloques. Esto obliga al programador al constante trasiego de líneas de código
cuando ponemos o quitamos un nivel de anidamiento. Lamentablemente, sólo
unos pocos entornos de desarrollo permiten que este recurso se gestione de
forma semi-automática.
Otra de las formas de representar un código fuente que usan los
programadores es el resaltado de sintaxis mediante colores. Este recurso si que
está ampliamente difundido en los entornos de desarrollo, y es bastante
configurable, aunque realmente lo que realzan son las piezas léxicas. Sería
interesante ampliar esta pobre capacidad de visualización del código que tienen
los entornos de desarrollo.
Si se usa el patrón propuesto para gestionar el código fuente, la visualización
del código resultará una tarea muy sencilla. Ya en el prototipo mostrado vemos
una aplicación de esta idea viendo una representación arbórea de un código
fuente. Se podrían crear componentes específicos que permitiesen tener una
parte del código de forma textual y otra parte del código en forma de árbol para
poder hacer una gestión más efectiva del código fuente. Usando el patrón se
permiten tener varias vistas simultáneas del mismo código fuente cada una con
unas características diferentes. Incluso la personalización de colores, fuentes, ...
por parte del usuario de cada una de estas vistas sería muy sencilla.
109
Sobre la información, su estructura y su-gestión
Se podría incorporar una barra para controlar el “nivel de detalle” del
código. Dependiendo del nivel de detalle requerido en cada momento se
podrían mostrar líneas de código de más alto nivel o bien de más bajo nivel. Por
ejemplo, un bajo nivel de detalle podría mostrar simplemente los nombres de
las clases que se usan en un determinado paquete; con un nivel más alto,
también se mostrarían de cada clase los nombre de los métodos y sus
parámetros; otro nivel de detalle mostraría los atributos; otro nivel de detalle
mostraría las descripciones de cada uno de los elementos de la clase; otro nivel
de detalle mostraría una parte del código fuente, y así sucesivamente. No sería
muy difícil que los niveles de detalle se aplicaran sólo a zonas concretas del
código, por ejemplo sólo a un determinado método dejando los otros con
solamente su descripción. Otro ejemplo de alto nivel de diferente naturaleza
sería conmutar un pseudocódigo o descripcion textual dado por un comentario
asociado a una expresión particular (p.e.: IF el caracter es vocal THEN ...) y su
representación en código ejecutable (p.e.: IF (caracter='a') or (caracter='e') or
(caracter='i') or (caracter='o') or (caracter='u') THEN ... )
Otras visualizaciones de código fuente más “clásicas” son aquellas que
incorporan formas geométricas al código fuente en modo texto para ayudarte a
localizar las partes relevantes. En Visual Basic entre los métodos de una clase
aparece automáticamente una fina línea continua de separación. Esta idea se
podría ampliar creando una línea vertical entre cada BEGIN y END de Pascal
de forma que sea fácilmente reconocible a que BEGIN corresponde cada END y
permitiendo una gestión más sencilla de los sangrados.
La visualización idónea del código fuente sería un tema interesante de
estudio que aquí no vamos a tratar. Lo que sí queremos dejar claro, es que
usando el patrón propuesto, la creación y configuración de cualquiera de estas
visualizaciones es muy sencilla; simplemente creando una clase vista que se
encargue de la visualización de una clase del modelo, y jerarquizando estas
110
Sobre la información, su estructura y su-gestión
clases vistas podemos crear visualizaciones como la del árbol vista en el
prototipo.
Depuradores
La forma de crear un depurador partiendo del intérprete propuesto sería
muy sencilla. Al estar cada objeto que representa la ejecución asociado a su
objeto en el código fuente, hacer un seguimiento de la ejecución sería muy
sencillo. Los puntos de parada se representarían mediante un atributo booleano
en cada objeto que representa un elemento del código fuente y en el proceso de
instanciación de los objetos de ejecución miraríamos ese valor booleano y,
dependiendo de su valor, detendríamos o no la simulación. La monitorización
del valor de las variables sería también muy sencillo e incluso podríamos ver
todas las variables y objetos que actualmente están en memoria y qué valores
tiene una misma variable dependiendo de la llamada recursiva. Podríamos
crear expresiones que se evaluaran cuando quisiéramos, paradas dependiendo
de condiciones, etc…
Todo ello ampliando el modelo de objetos propuesto: más metodos y más
clases sobre la misma jerarquía, previamente analizada y diseñada. Por contra,
los depuradores actuales desarrollan un código intrusivo o software de
andamiaje sobre un árbol de sintaxis abstracta desnaturalizado con una tabla de
símbolo en tiempo de ejecución.
Creación ortogonal de entornos de desarrollo
Una vez dadas unas directivas generales de cómo debería de ser un entorno
de desarrollo prototípico (editor, visualizador, compilador, depurador, ...) la
creación de entornos de desarrollo para cualquier lenguaje de programación
existente sería bastante sencilla, por no decir que automática. Esto es debido a
que, como vimos en el primer capítulo, todos los lenguajes de programación
tienen inmensos parecidos entre sí y los de un mismo paradigma aún más. Por
111
Sobre la información, su estructura y su-gestión
tanto, definiendo un entorno prototípico genérico y especializado para cada
uno de los paradigmas se podrían construir casi automáticamente entornos
para cualquier lenguaje, tan sólo teniendo que especificar el modelo del
lenguaje que queremos. Además, si se "programa/configura" una nueva forma
de visualizar una sentencia alternativa if, de la que constan todos los lenguajes
imperativos, esta nueva visualización se podría incorporar fácilmente en
cualquier entorno de desarrollo de otro lenguaje.
Esta forma de crear entornos de desarrollo permite mantener entornos de
desarrollo homogéneos de diferentes lenguajes, que en definitiva, tienen
prácticamente la misma funcionalidad. Por tanto, no ocurriría como ocurre
actualmente que cuando queremos programar con dos lenguajes de
programación, tenemos que aprender, además de dos lenguajes, dos entornos
de desarrollo.
Como se puede observar, la creación de un entorno de desarrollo
multilenguaje no sería ningún problema, la única diferencia con un entorno de
un solo lenguaje sería que en vez de trabajar con una sólo jerarquía de clases y
objetos que modelan un solo lenguaje, se trabajaría con varias jerarquías que
modelan diversos lenguajes.
Programación visual
Como hemos comentado antes, la forma de programar desde los primeros
días de la programación es mediante texto. Si creamos un entorno de desarrollo
que permita una visualización del código fuente de forma arbórea, eliminando
aquellos elementos textuales necesarios para el analizador sintáctico y dejamos
sólo los elementos clave del código, sería interesante, que el código se pudiese
editar en esa misma representación gráfica. Es decir, sería interesante poder
disponer de programación visual (aspecto diferente de la programación textual
del interfaz gráfico de usuario).
112
Sobre la información, su estructura y su-gestión
Con una representación orientada a objetos de un código fuente, la
programación visual sería muy sencilla. Los objetos no los crearía un analizador
sintáctico dependiendo de lo que se va encontrando en el texto de entrada como
se ha propuesto en el prototipo. Simplemente, los objetos del código se crearían
como acción asociada a botones que representen dichos elementos del código
fuente; se podrían “arrastrar y soltar” bloques de código de un lugar a otro,
básicamente sería quitar los bloques de donde estaban e insertarlos en el nuevo
lugar; los mensajes a objetos se podrían hacer de forma visual eligiendo el
objeto de entre los "elementos/iconos" accesibles en nuestro ámbito y, cuando le
elijamos, poder seleccionar en otra ventana el método adecuado; es más, si
queremos hacer lanzar un mensaje que corresponde con un método que todavía
no existe, en la ventana donde aparezcan los métodos se podría crear su interfaz
y luego más tarde se definiría.
Mantener un modelo híbrido, en el que convivan la programación visual y
textual tampoco es problema, ya que cada objeto que representa un elemento
del programa, siempre podrá mostrarse en modo gráfico y posteriormente ser
editado de forma convencional. Es un patrón recurrente de la programación
orientada a objetos: modelo (elemento programático), controlador y vistas (vista
textual, vista gráfica, ...)
Como vemos, al tratar el problema del código fuente, en vez de cómo una
secuencia de caracteres, como una estructura de objetos, muchos de los
elementos que estamos comentando son muy sencillos, mientras que tratando el
código fuente como una secuencia de caracteres serían impensables.
Documentación del código
Aparentemente un código fuente es símplemente un fichero en formato
textual que representa, con mayor o menor nivel de abstracción, los datos que
113
Sobre la información, su estructura y su-gestión
se manejan y cómo han de manejarse. Pero un código fuente es mucho más que
eso.
Los comentarios, pese a no ser una parte ejecutable o de definición de datos
es un elemento importante del código fuente. Lo que ocurre es que
generalmente, esta parte del código no presenta ninguna estructura y no se hace
ninguna gestión con ellos. Sería interesante, poder dotar a los entornos de
desarrollo de la capacidad de menjar los comentarios. JavaDoc es una
herramienta propuesta por Sun para este propósito.
Usando el modelo propuesto, la gestión de los comentarios no supone un
cambio radical en el desarrollo del sistema, si no que símplemente se necesitaría
añadir el atributo de la clase “comentario” a la clase más alta de la jerarquía
para que todos los elementos del sistema fuesen susceptibles de ser
comentados. De esta forma los comentarios se asociarían a los elementos del
lenguaje, sirviendo para su documentación y no aparecerían “flotanto”
desestructuradamente en el código como habitualmente se disponen.
Otra parte importante es la gestión de versiones. Actualmente la gestión de
versiones se basa en modificaciones en un fichero de texto y no se toman en
cuenta los elementos del lenguaje para ver realmente que partes del código
cambian de un código en una versión al código en la siguiente. Usando un
sistema en el que se representan los elementos orientados a objetos, la gestión
de versiones, apoyándose en un gestor de bases de datos orientadas a objetos,
sería más sencilla de implementar y quizás más útil que basada en texto.
Hay ocasiones en las que se escriben "a la vez" dos versiones del mismo
programa. Por ejemplo hay veces que estamos construyendo programas que
tienen trazas de depuración hasta que funcionan correctamente y cuando llega
este momento ya no se necesitan, pero no se quitan por si fueran necesarias en
un nuevo cambio en el código fuente. El soporte de este tipo de trazas es muy
114
Sobre la información, su estructura y su-gestión
variado de unos lenguajes a otros y de unos entornos a otros. Por ejemplo en C
estas trazas se crear mediante directivas de compilación para compilar o no
ciertas líneas en el código. En LISP existe la función con efectos laterales
TRACE. Como contrapartida, se podrían crear “capas” superpuestas de código.
De manera que a la hora de generar un ejecutable se decida que capas se
quieren compilar y que capas no. Crear esto con nuestro sistema sería bastante
sencillo ya que se trataría de la simple gestión de datos orientados a objetos y
no una compleja gestión de un compilador.
2. Procesadores de lenguajes
En el prototipo mostrado, básicamente se construye una jerarquía de objetos
a partir de un código fuente. La jerarquía de objetos representa el mismo
programa que el código fuente; la diferencia es que la información está
representada de forma distinta: estática/textual frente a dinámica/orientada a
objetos.
Para
la
construcción
de
procesadores
(traductores,
conversores
o
compiladores, e intérpretes) esta nueva representación es muy beneficiosa
porque todas las tareas de análisis léxico y sintáctico propias de un procesador
ya están realizadas usando este modelo. El cambio radical es que con este
modelo se parte de un arbol de sintaxis abstracta que soporta sobre sí mismo
toda la información de la tabla de simbolos, ya que todos los elementos que
hacen referencia a otros dentro del código están debidamente “enlazados”.
Creación de intérpretes
Se acoge, tal y como se ha expuesto en el capítulo anterior, al modelado del
lenguaje en una jerarquía de clases y su implantación interna basándose en la
creación de objetos, enlaces y autómatas para dotar de la semántica del tiempo
al modelo.
115
Sobre la información, su estructura y su-gestión
Creación de compiladores
La única labor del compilador sería la traducción a código máquina que
podría realizarse con una capa observadora adicional (acogiendose al patrón
expuesto en el capítulo anterior) que obtuviese los valores necesarios de los
elementos del código fuente y volcase su traducción.
Este volcado puede ser directamente en texto o bytes, si es sencilla una
traducción directa, o bien puede ser realizado mediante la instanciación de una
"secuencia" de objetos que represente el código ensamblador, y que sea
posteriormente dicha "secuencia" de objetos la que se represente en bytes. Este
paso intermedio sería necesario debido al gran hueco semántico entre los
lenguajes de alto nivel y los lenguajes destino de los compiladores, de muy bajo
nivel.
Creación de conversores
Partiendo de la base de que todos los lenguajes de programación se parecen
muchísimo compartiendo elementos comunes, vamos a ver como sería una
traducción de un programa escrito en un lenguaje a un programa escrito en otro
lenguaje.
Supongamos dos lenguajes, C y Pascal, del paradigma imperativo,
comparten la inmensa mayoría de elementos, y sólo algunos que existen en C
no existen en Pascal, por tanto, la estructura de objetos que represente el mismo
código fuente en los dos lenguajes es prácticamente igual. Debido a esto, no
sería necesario crear una jerarquía intermedia como en el caso de los
compiladores, si no que la jerarquía sería prácticamente común en ambos
lenguajes. Por tanto, se podrían crear conversores de lenguajes indicando
simplemente como representar las abstracciones presentes en un lenguaje con
las abstracciones presentes en otro lenguaje. El sistema podría automáticamente
cambiar sólo aquellos elementos del lenguaje que cambien de un lenguaje a otro
y dejar el resto igual, porque en definitiva, son iguales. La capa observadora
116
Sobre la información, su estructura y su-gestión
encargada de la representación textual, se cambiaría al lenguaje destino y por
tanto se representaría el código fuente como el lenguaje destino.
Esta funcionalidad no tiene porque ser un proceso que se realice una vez
finalizado un programa completo, si no que se podría incluir en el desarrollo
del software, como una visualización más del mismo código.
Optimizadores y des-optimizadores de código
En muchas ocasiones, la claridad en el código fuente está reñida con la
supuesta eficiencia de ese código fuente en ejecución. Los compiladores hacen
muchas optimizaciones en el código para evitar esas situaciones. Se podrían
crear optimizadores de alto nivel, que transformen un código fuente en otro,
con la misma semántica, pero que evite ineficiencias. Todo ello obviando la
manipulación de tercetos o cuartetos de bajo nivel, mediante el acceso a objetos
de "alto nivel" con comportamiento.
El ejemplo típico sería el de transformar el código:
If (a > b) then
aMayor = true;
else
aMayor = false;
por el código:
aMayor := (a > b);
Pero es que en otras ocasiones, lo que se busca es lo contrario. Partir de un
código eficiente pero altamente críptico para obtener un código menos eficiente
pero más legible para un programador; el código C ofuscado, que es altamente
usado por programadores convencionales, es representativo de esta situación.
La creación de este tipo de optimizadores directos e inversos sería crear un
nuevo código fuente partiendo de otro código fuente. Por tanto, sería cuestión
117
Sobre la información, su estructura y su-gestión
de crear una capa observadora conversora, que obtenga los datos de la
estructura de objetos y se encargue de mantener una estructura de objetos
paralela.
3. Prototipado de lenguajes de programación
La posibilidad de poder representar los elementos de un lenguaje de
programación sin la engorrosa e inexpresiva formalidad sintáctica y mediante
UML o una jerarquía de clases relacionadas mediante relaciones de asociación,
composición y herencia sin duda aporta numerosas ventajas a la hora de
estudiar el mundo relacionado con los lenguajes de programación. A lo largo de
los siguientes apartados iremos viendo qué ventajas tenemos usando este
modelado de los lenguajes de programación.
Estudio comparativo entre lenguajes
Al tener que representar los elementos de un lenguaje de programación
mediante clases es mucho más fácil compararlos. Muchos lenguajes de
programación usarán muchas clases comunes entre ellos, por ejemplo la
sentencia condicional, la capacidad de llamada a una subrutina, la asignación,
etc...
En los lenguajes del mismo paradigma de programación estos parecidos son
fáciles de reconocer, cosa que no ocurre en los lenguajes de distintos
paradigmas. Por ejemplo, la modelización de Pascal y CAML usando las clases
propuestas hace ver el tremendo parecido que tienen y estrictamente sus
diferencias (p.e.: sentencia if de Pascal frente al operador if de CAML).
La definición de los lenguajes mediante este modelado permitiría disipar
muchas dudas que se plantean cuando se aprende un nuevo lenguaje de
programación.
118
Sobre la información, su estructura y su-gestión
Creación de lenguajes multiparadigmáticos
Cuando se hace un estudio de lenguajes de distintos paradigmas se observa
como tienen muchos conceptos en común. Por tanto si definimos un lenguaje
que en algunas partes del código permite usar los elementos de un paradigma y
en otras partes del código los elementos de otro paradigma habríamos creado
un lenguaje multiparadigmático como Leda de Budd o C++ que es un lenguaje
multiparadigmático porque permite programación orientada a objetos y
programación procedural en un mismo código.
Modelando nuestro propio lenguaje de programación podríamos juntar
paradigmas tan dispares como el lógico y el orientado a objetos con la facilidad
de elegir clases del repositorio de modelados de lenguajes pertenecientes a uno
u otro paradigma.
Creación de aplicaciones multilenguaje
La creación de un lenguaje propio es una tarea ardua mirado desde un punto
de vista tradicional, aunque usando los patrones propuestos se limita a pensar
en el lenguaje y no en la forma de implantación. Otra utilidad sería disponer de
un "multilenguaje" que en algunos momentos te permita escribir código LISP,
por reutilización de código existente en bibliotecas, adecuación a la naturaleza
de cierta parte de la aplicación (p.e. cálculo simbólico) y en otras partes del
código te permite escribir código Java (p.e. bibliotecas de redes, multimedia,
etc).
Partiendo de un modelado de ambos lenguajes basado en UML, e indicando
las conversiones de datos de un lenguaje a otro sería posible hacer aplicaciones
multilenguaje de forma natural y sencilla evitando "puentes" (p.e.: código
nativo de Java a C), "configuraciones" (p.e.: SQL embebido en COBOL), ...
complejas e innecesarias.
119
Sobre la información, su estructura y su-gestión
Solo habría que definir cómo deben coexistir ambas gramáticas, la forma en
que se convertirían los datos intercambiables de un lenguaje a otro y la cesión
del control de flujo de ejecución de un lenguaje a otro. Pero gracias a tener una
base común para representar ambos lenguajes en el modelado, afrontar este
proyecto sería abordable.
Creación de meta-procesadores
Como hemos observado a la hora de construir el modelo de Pascal en UML,
simplemente hemos ido creando clases. Luego hemos asociado una gramática
para la instanciación de objetos de esas clases en los momentos oportunos.
Hemos creado una representación textual de cada uno de los elementos del
lenguaje para que sea equivalente a la forma en que se reconoce dicho
elemento. Lo que queda claro es que el modelado de un nuevo lenguaje de
programación siguiendo el patrón propuesto se basa en seguir unas directivas
en cuanto a representación de los conceptos. Es más, el modelado de C
partiendo del modelado de pascal sería bastante sencillo.
Por tanto, no sería muy complejo construir un sistema que ayude en la
creación de lenguajes. Puede ofrecer todos aquellos mecanismos básicos, como
la gestión de ámbitos, la asignación destructiva, la correspondencia de patrones,
el algoritmo de unificación, la subprogramación, el manejo de excepciones y
otros más avanzados como la orientación a objetos o la programción
parametrizada. De esta forma, el creador de un lenguaje solo tiene que elegir
aquellos mecanismos que le pueden ser útiles en la construcción de su lenguaje,
desechar el resto y determinar la conjunción de los elegidos.
La definición de la gramática puede también estar ayudada por un
programa. Sólo tendremos que indicar que palabra reservada queremos usar
para cada cosa, que token sería el adecuado para separar cada uno de los
elementos de una lista y poco más. De forma automática se puede determinar
120
Sobre la información, su estructura y su-gestión
las reglas gramáticales y con técnicas tradicionales, determinar la condición de
LL(k) o LR(k), entre otros, para la generación automáticas de procesadores de
éste lenguaje.
Experimentación con lenguajes
Con la construcción de un programa que permita crear nuevos lenguajes de
programación
aparece
una
posibilidad,
antes
impensable,
de
la
experimentación con los lenguajes.
Generalmente para crear un lenguaje de programación se estudia y se
especifica claramente qué elementos se requieren en el lenguaje y en qué forma
se van a expresar en texto. Una vez que se ha decidido y está todo
perfectamente especificado se crea la gramática, el analizador léxico-sintáctico y
luego se crea el compilador. Introducir un cambio a última hora en el lenguaje
puede ser bastante engorroso por la cantidad de elementos que hay que
cambiar.
Si se crea un programa con el que se permita crear lenguajes de
programación, el ciclo se acortaría, la gramática y la semántica no serían dos
cosas totalmente disjuntas y se podría cambiar fácilmente elementos del
lenguaje, quitar unos, poner otros, etc… en definitiva, se podría experimentar
con los lenguajes. Podría estudiarse en un mismo lenguaje los 5 tipos de paso de
parámetros (valor o entrada, entrada/salida, salida, variable, por nombre)
alterando mínimamente el autómata de la implantación de las clases del
modelo del lenguaje responsables del concepto del paso de parámetros.
Otros aspectos posibilitados, al disponer del metamodelo en el sistema, sería
la implantación de objetos re-clasificables en tiempo de ejecución (p.e. el propio
objeto de la clase Empleado se comporta dinámicamente como objeto de la clase
Jefe, sin necesidad de destrucción y nueva creación), jerarquías de clasificación
121
Sobre la información, su estructura y su-gestión
multi-criteriales (p.e. un objeto se comporta como de la clase Vaca o Producto
dependiendo del contexto); etc
Meta-programación
La metaprogramación es una potentísima herramienta que rara vez se usa
para
la
construcción
de
aplicaciones,
que
permite
altas
cotas
de
desacoplamiento y, por tanto, reusabilidad.
El soporte de metaprogramación es muy variado dependiendo del lenguaje
de programación. En Basic se puede obtener el nombre de una variable como
cadena de texto simplemente antecediendo a su nombre el carácter $. Es decir,
se permite una metaprogramación de sólo lectura. En C la metaprogramación
es inexistente. En LISP la metaprogramación es de lectura y escritura, es decir,
en tiempo de ejecución se puede leer un código como una lista de elementos y
se puede ejecutar una lista de elementos que representa un código. En Java la
metaprogramación es de sólo lectura, aunque mucho más potente que la de
Basic, pues permite la obtención de la clase de un objeto, y una vez obtenido la
clase, preguntar su nombre, sus métodos etc… También se pueden lanzar
mensajes mediante su nombre, definido en tiempo de ejecución, pero no se
puede crear un código nuevo en tiempo de ejecución.
El problema de la metaprogramación es la distinta forma en que los
metadatos y las instrucciones se representan en ejecución. En nuestro modelo,
eso es precisamente lo que hemos homogenizado, representamos los datos de
un lenguaje orientado a objetos como el código fuente: objetos ambos del
mismo modelo.
122
Sobre la información, su estructura y su-gestión
4. Ingeniería del Software
La ingeniería del software se caracteríza, entre otras muchas cosas, por
aportar mucha más información en el desarrollo del software que el propio
código fuente. Otra parte importante en la ingeniería del software es que los
programas que se desarrollen tengan determinada calidad en determinados
aspectos. Por último, se podría decir que en ingeniería del software se estudian
las pautas o patrones que han de seguir los programas para que sean reusables,
desacoplados, etc... La gestión de información asociada al código fuente se
simplifica mucho como hemos visto en el apartado anterior. Para determinar la
calidad de un código fuente se dibujan gráficos de síntesis y se calculan
medidas cauntitativas sobre el código. Veamos las facilidades que aporta el
sistema propuesto en la creación de herramientas de ingeniería del software.
Métricas del Software
Debido a que cada elemento del programa se prepresenta como un objeto en
una estructura de objetos, hacer medidas sobre el número de objetos que
pertenecen a una determinada clase o que tienen unas características
particulares es bastante sencillo. Sería básicamente aplicar los conocimientos de
recorridos de árboles para inspeccionar todo el código fuente o recurrir a un
sistemas gestor de bases de datos orientado a objetos para realizar las consultas
de alto nivel deseadas. Es más, fácilmente se podría crear un patrones que
permitiesen ocultar estos algoritmos de recorrido del árbol o consultas al gestor,
dejando al usuario/programador la tarea de determinar qué cosas quiere
medir.
Patrones de diseño
El uso de patrones, como hemos comentado más arriba, suponen una
importante herramienta en la consrtucción de software de calidad. La detección
123
Sobre la información, su estructura y su-gestión
automática de patrones facilita la documentación del código (para la
legibilidad, el mantenimiento, la extensibilidad, ...) y caracteriza las cotas de
calidad de la aplicación. Se pueden ver varios tipos de patrones en el desarrollo
del software.
Los patrones de bajo nivel son aquellas partes del código que se repiten
habitualmente con la misma funcionalidad. Se podría considerar patrón de bajo
nivel los usos que se hacen de una variable como contador, como switch, como
acumulador, etc... La detección de estos patrones en un código fuente y la
gestión de los mismos podría crearse en nuestro sistema. La cosa no sería tan
sencilla como un simple contador de variables. Habría que aplicar ciertos
conocimientos de inteligencia artificial en la búsqueda de patrones. La gestión
de los mismos también sería un poco compleja debido a que no quedan
claramente plasmados en el código. Pero sin duda, el problema sería abordable
partiendo de una representación orientada a objetos del código fuente mediante
la inspección de objetos declaración, expresión y sentencia.
Los patrones de diseño actualmente se encuentran gestionados en
aplicaciones de alto nivel. Lo que crea una separación a veces “insalvable” entre
el diseño y la codificación. Al incorporar este tipo de funcionalidades a una
herramienta de desarrollo se facilitaría el uso de estos patrones. Una forma de
soportar esta característica en nuestro sistema sería que en una parte del
sistema se pudiesen elegir o crear los patrones de diseño y que quedaran
“asociados” al código que los implementa. De forma que a la hora de explorar
el código sepamos a qué patrón o patrones se acoge cierto método, clase o
grupo de instrucciones o bloques de código. La creación del “esqueleto” del
código a partir de patrón del diseño se podría realizar de manera asistida. Para
implementar esta capacidad en nuestro sistema habría que crear otras clases
que representaran patrones de diseño y que se asociaran a las clases del código;
de forma que, en un código fuente concreto, instancias de las clases de los
patrones se asocien a instancias de las clases de los elementos del código fuente;
124
Sobre la información, su estructura y su-gestión
donde algunas instancias del código fuente se podrían haber creado de forma
asistida partiendo de las instancias de los patrones de diseño.
Por último, están los patrones de alto nivel, aquellos patrones que se usan en
el proceso de análisis. Sería un paso más en la jerarquía de patrones, de forma
que estos patrones condicionarían el uso de los patrones de más bajo nivel. El
esquema sería similar al comentado anteriormente pero en un nivel superior. El
estudio más detallado de este aspecto supera los límites de este TFC.
Visualización de diseño
Hemos comentado que la ingeniería del software basa muchos de sus
esfuerzos en encontrar representaciones gráficas apropiadas y expresivas de los
patrones de alto y medio nivel que aparecen en el desarrollo del software. Para
soportar esta característica en nuestro sistema tan sólo tendríamos que usar las
técnicas genéricas de visualización basadas en el paradigma orientada a objetos
ya que desde el código fuente hasta los patrones del alto nivel se representan
usando esta herramienta.
En el prototipo ya se ofrece una visualización jerárquica de los elementos de
un código fuente. Para permitir visualización a nivel de diseño símplemente
sería seguir ampliando el trabajo en esta área con gráficos de dependencia de
bloques, etc.
Lenguajes de diseño
Hasta el momento se ha expuesto que el proceso de creación de lenguajes de
programación usando nuestro sistema es más cómoda y abordable. Crear un
lenguaje nuevo parecido a alguno existente sería muy sencillo. Por tanto, se
abren vías para la creación de un lenguaje de programación que use las
abstracciones usadas a nivel de diseño. Por ejemplo, basandonos en el lenguaje
125
Sobre la información, su estructura y su-gestión
Java, se podrían incorporar palabras reservadas a los atributos de las clases para
poder indicar relación de asociación o composición; el uso de las secuencias
mediante una palabra reservada que denotase el concepto podríamos abstraer
al usuario de la clase concreta usada para implementar dicha secuencia. Los
programas escritos en este lenguaje de diseño no serían ejecutables
eficientemente, pero en la construcción de prototipos aliviarían mucho la tarea
del programador. Además, al ser un lenguaje de programación permitiría una
traducción a otro lenguaje, por ejemplo Java, de forma asistida. En ese proceso
de traducción se podrían tomar, de forma interactiva, las decisiones relativas a
los detalles de implementación.
Restricción de lenguajes
En ingeniería del software normalmente se limitan las capacidades de los
lenguajes de programación para hacer software de calidad. Por ejemplo, el
número de parámetros de un método en Java es ilimitado, pero para crear
programas de cálidad el número de parámetros no debe superar unos
determinados umbrales.
Esta forma de ampliar las restricciones semánticas que se imponen a un
lenguaje se podrían configurar fácilmente usando nuestro sistema. La
estructura orientada a objetos de un código fuente representa un código fuente
válido estructuralmente con las referencias enlazadas a los elementos que
referencias.
Así, las comprobaciones semánticas son llevadas a cabo por nuevos objetos
configurables asociados a cada uno de los objetos del código fuente. Para incluir
nuestras propias restricciones se podría crear una nueva capa observadora. Esta
capa observadora, que cubriría a todos los elementos del código fuente, sería la
encargada de comprobar si los elementos del código fuente cumplen nuevas
restricciones, de forma que la compilación se haría en varias fases. Algunas
126
Sobre la información, su estructura y su-gestión
fases serían obligatorias y otras fases serían recomendables para crear software
de calidad aunque no serían obligatorias para crear el programa.
Documentación de programas
La documentación de los programas es también una parte fundamental de
los productos entregados por la aplicación bajo un método de ingeniería del
software. Este punto está muy relacionado con la gestión de los comentarios del
código fuente anterior. Sin embargo, sería como documentar también los
patrones de diseño y los patrones de análisis.
En ocasiones es útil obtener la documentación de los programas en formatos
como XML, HTML, documentos de Word, etc... para la distribución por la vías
“convencionales”. Por tanto se requiere que a partir de un código fuente y sus
comentarios asociados se transformen en estas otras representaciones. La labor
de transformación la llevaría a cabo otra capa observadora que obtuviese la
información necesaria de los objetos que representan el código fuente y la
trataría para obtener de ella XML, HTML, PDF, etc...
5. Asistencia a la educación
Hemos comentado anteriormente que este sistema se podría usar para
construir entornos de desarrollo equivalentes para distintos lenguajes de
programación. Como se expuso anteriormente, se podría crear un entorno de
desarrollo que permitiese desarrollar código en distintos lenguajes de
programación. Este entorno de desarrollo multilenguaje sería muy bien recibido
en el área de educación dado que a lo largo de una carrera se enseñan multitud
de lenguajes de programación, lo que obliga al alumno a estar constantemente
aprendiendo como usar las herramientas asociadas a cada lenguaje de
programación y le hace desviarse del objetivo principal, aprender a usar el
lenguaje de programación. En este sentido la aplicación del sistema propuesto
127
Sobre la información, su estructura y su-gestión
sería bastante útil. Existen otros espectos en educación en los que el uso del
sistema propuesto aporta cosas útiles. Veremos cada uno de ellos en los
siguientes apartados.
Visualización del software
Se entiende por visualización del software la representación gráfica de los
conceptos y abstracciones presentes en la ejecución del software. De una forma
un poco menos formal se podría decir que la visualización del software es
dibujar las variables, la ejecución de las instrucciones, las estructuras de datos a
medida que se va ejecutando un programa o algoritmo. La visualización del
software es un recurso objeto de estudio desde hace muchos años que se
empieza a usar en la enseñanza.
Una de las grandes dificultades en el uso de la visualización del software es
su creación. El desarrollo de representaciones gráficas de los programas en
ejecución no es una tarea sencilla. Además, generalmente las visualizaciones se
crean específicamente para un determinado algoritmo, es decir, es complejo
crear un sistema genérico y automático de visualización de cualquier programa.
Con el uso de nuestro sistema, la construcción de visualización del software
es una tarea tremendamente sencilla. Los elementos de un lenguaje de
programación se representan por objetos y el proceso de interpretación de un
código fuente también se traduce en la instanciación de objetos. Por tanto, para
crear la visualización del proceso de ejecución de un código, símplemente
tendríamos que usar las técnicas habituales de representación gráfica orientadas
a objetos.
128
Sobre la información, su estructura y su-gestión
Lenguajes adaptativos
La programación orientada a objetos como evolución de las técnicas
anteriores de programación a traído más reusabilidad, modularidad,
abstracción, etc... al desarrollo del software. Ahora los profesores de las carreras
de informática se plantean cuando es el momento adecuado de enseñar a
programar orientado a objetos. Existe una gran corriente de profesores
innovadores que piensan que la enseñanza de la programación se debería hacer
de manera progresiva. Esta enseñanza se basaría en la evolución que han
sufrido los lenguajes de programación y la justificación que ha ido propiciando
dicha evolución, para que el propio alumno sea consciente de que la tecnología
orientada a objetos no es el último paradigma y sepan encontrar en él los
puntos débiles que le harán evolucionar al cabo de los años.
La implantación de esta idea tiene algunos problemas prácticos. Se acepta
ámpliamente que para enseñar a programar es necesario un lenguaje de
programación con el que los alumnos ejecuten sus diseños. Si enseñamos a
programar de manera evolutiva, necesitaremos que nuestro lenguaje de
programación vaya evolucionando a la par que las explicaciones. Este lenguaje
evolutivo en las fases tempranas, cercanas a la programación en ensamblador
tendría instrucciones de tipo GOTO. En la última fase tendría el concepto de
clase. Los códigos fuente escritos en el lenguaje usando las herramientas de las
primeras fases, no compilarían cuando el lenguaje se encuentre en la fase
orientada a objetos. No tenemos que olvidar que ciertas herramientas del
lenguaje, no han cambiado desde el momento que aparecieron, como por
ejemplo las expresiones, de forma que el lenguaje debería soportarlas sin
variación desde que aparecen.
Construir un entorno de desarrollo que permita soportar esta idea sería
bastante duro desde el punto de vista tradicional. Habría que construir varios
compiladores a la vez que compartan algunas partes y en otras partes difieran.
129
Sobre la información, su estructura y su-gestión
El uso de nuestro sistema aportaría numerosas facilidades en la construcción
del sistema. El estudio pormenorizado de la creación de este sistema supera los
límites de este TFC.
Lenguajes parciales
Otra necesidad imperiosa en la docencia de un lenguaje es posibilitar la
experimentación con partes del lenguaje sin obligar a la edición de un programa
completo. Por ejemplo, en la enseñanza/aprendizaje de la expresiones o
definición de registros, disponer del lenguaje parcial que aborde únicamente los
aspectos a tratar. Estas vistas parciales del lenguaje permitiría la creación de
herramientas didácticas que determinen la corrección sintáctica y semántica de
partes aisladas de un programa, facilitando el acercamiento a los niveles
cognitivos iniciales de la taxonomía de Bloom (compresión, aplicación y
análisis).
6. Interfaces gráficos de usuario
La creación de los interfaces gráficos de usuario ha evolucionado bastante
con la llegada de la tecnología orientada a objetos. De hecho, la difusión al gran
público de la tecnología orientada a objetos se ha introducido por los entornos
de desarrollo como VisualBasic y Delphi y su modelo de componente software.
Una aplicación con una buena interfaz gráfica representa gráficamente las
opciones y servicios disponibles. Una típica aplicación para windows tiene
varios menús con opciones, agrupadas por funcionalidad. Poseen una barra de
acceso rápido con elementos que también son accesibles a través de los menús y
luego el elemento o elementos gestionados por el programa. En un programa
tipo Word, el elemento es un documento de texto. En una aplicación de
gráficos, los elementos son gráficos. En una aplicación basada en base de datos
los elementos gestionados son informes y ventanas de acceso y búsqueda. A
130
Sobre la información, su estructura y su-gestión
muy grandes rasgos, la interfaz gráfica es un reflejo de lo que está ocurriendo
por dentro en la propia aplicación, de la lógica y la estructura de la información.
La interfaz gráfica es una representación del estado y acciones de nuestros
programas. En ingeniería del software, las técnicas de análisis basadas en
prototipos es crear pantallas de nuestro futuro programa y enseñárselas al
cliente. El cliente no sabe programar, pero con las pantallas sabe la información
que gestiona en el programa y las formas que hay de acceder a ella.
Por tanto, podríamos estudiar más a fondo la manera de crear el interfaz
gráfico de usuario partiendo de las clases de nuestro programa de manera
asistida. Este estudio mostraría una problemática asociada que, de nuevo, no es
objetivo pero posibilita la propuesta de este TFC: el uso del sistema propuesto
para representar el código fuente y la posibilidad de añadir patrones de diseño
y análisis es un buen punto de partida para comenzar dicho estudio.
7. La eficiencia, el inconveniente y sus
innumerables desventajas
El sistema propuesto adolece en un punto frente a los métodos tradicionales
de construcción de compiladores e intérpretes, la eficiencia. Los compiladores
tradicionales, no representan en memoria el código fuente, si no que a medida
que le van analizando de la cadena de caracteres realizan todo el trabajo
posible. Lo hacen para reducir la cantidad de memoria exigida y para ser lo
mas rápidos posibles. El sistema propuesto en su parte de interpretación
también es muy poco eficiente, ya que está constantemente instanciando objetos
para representar cosas insignificantes del código. Por tanto sabemos que
nuestro sistema es más lento y consume más memoria.
La interpretación se hace principalmente por motivos de visualización del
software, por experimentación en la creación de nuevos lenguajes y para
131
Sobre la información, su estructura y su-gestión
depuración en partes específicas del código. Obviamente para estas funciones el
punto menos crítico es la eficiencia. En visualización del software las personas
tienen que ver el desarrollo, lo cual hace que tenga que ser lento. En la
experimentación con lenguajes la interpretación va a requerir un seguimiento
exhaustivo que no requiere de mucha velocidad. En cuanto a la depuración en
determinadas partes del código, generalmente se reduce a una pequeña parte
del código que es admisible que sea algo más lenta.
Por otro lado, la capacidad de interpretación no es parte fundamental del
sistema propuesto: se puede eliminar del sistema en la versión de la aplicación
para explotación tras su desarrollo. Se puede sustituir por un traductor a código
máquina o a un lenguaje de programación que posteriormente se compile. Por
ejemplo, a nuestro prototipo se le podría incorporar la capacidad de traducción
a C++. Tras la traducción se invocaría el compilador de C++ y tendríamos
nuestro eficiente programa ejecutable. En el caso de lenguajes necesariamente
interpretados
(p.e.
Lisp,
Java
o
Smalltalk
por
características
de
metaprogramación o portabilidad entre otras) se pueden generar la
representaciones intermedias adecuadas para la eficiencia (p.e. grafos
simbólicos para Lisp o bytecodes para Java).
El uso de memoria ya no es un problema. Las técnicas de construcción de
compiladores tradicionales no han cambiado en muchos años. Antes la
memoria era un bien escaso que había que gestionar bajo condiciones extremas.
Actualmente, con las posibilidades de memoria virtual de los sistemas
operativos y la gran cantidad de memoria de cualquier ordenador convencional
ya no es prohibitivo el uso de mucha memoria. Además, en la construcción de
un entorno de desarrollo integrado, si nuestro compilador estuviese basado en
las técnicas tradicionales, sería la parte que menos recursos necesitaría. Dicho
de otra forma, no tiene sentido hacer un compilador altamente eficiente, con un
uso de memoria mínimo y que requiera un gran tiempo de desarrollo, si luego
132
Sobre la información, su estructura y su-gestión
el propio interfáz gráfico del entorno va a ocupar muchísima más memoria que
el propio compilador.
En cuanto a la velocidad de compilación es muy discutible que se viera
afectada. Actualmente el elemento mínimo de compilación es el fichero de
texto. De forma que si cambiamos una solo línea en un fichero fuente hay que
recompilar todo el fichero fuente. Con unas pocas mejoras en nuestro sistema,
la creación de objetos que representan los elementos del código, se haría
solamente para los elementos que cambian y no para todo el texto fuente, lo que
reduciría enormemente el tiempo de compilación. Es más, con el sistema
propuesto, la creación de objetos y la comprobación semántica se podría hacer
en segundo plano a medida que el usuario teclea el programa. Los errores
podrían aparecer subrayados en rojo instantes después de haber escrito la línea,
lo que sin duda haría que el usuario los corrigiese mucho más deprisa que si
tiene que volver a esa línea después de haber escrito muchas más y haber
ejecutado el compilador.
Por supuesto no tenemos que olvidar la programación visual. Muy sencilla
de añadir a nuestro sistema y que posiblemente reduciría el tiempo de análisis
léxico-sintáctico porque ya no hay texto que analizar. Es posible que la
programación visual en todas las fases de desarrollo de un programa aumente
el tiempo de desarrollo total. Pero la combinación de la programación visual y
la programación textual reduciría los tiempos totales de desarrollo. Lo cual hace
que la velocidad del compilador sea un punto indiferente si se consigue que se
desarrollen los programas más rápido.
Ya hemos visto que no supone ningún problema que nuestra forma para
construir un compilador sea ineficiente. Vemos que se puede usar sin
inconvenientes. Por tanto veamos ahora que desventajas supondría mantener el
compilador usando las técnicas tradicionales.
133
Sobre la información, su estructura y su-gestión
Las herramientas para la construcción de compiladores o intérpretes sólo
generan el analizador léxico-sintáctico, por lo tanto, las rutinas semánticas hay
que programarlas para cada nuevo lenguaje que creemos. Por tanto el
desarrollo se hace más largo y complejo.
La incorporación de cualquier traducción diferente de la que se programara
inicialmente supondría modificar partes del código existente y acoplar
totalmente todas las traducciones, lo que hace que el código tenga menos
calidad por ser más complejo de depurar y con más posibilidades de que la
parte que funcionaba deje de hacerlo.
La búsqueda de elementos concretos del código con consultas del estilo “las
variables con el nombre X dentro de cierto ámbito“ sería prácticamente
inviable.
Podríamos seguir enumerando muchas funciones que serían muy difíciles de
obtener usando los métodos tradicionales de creación de compiladores.
134
V. Conclusiones
Considero que se han cumplido los objetivos de este Trabajo Fin de Carrera.
Se han sentado las bases para la construcción de un sistema que modela un
lenguaje de programación. Se han puesto de manifiesto las grandes
posibilidades de usar el sistema propuesto.
A partir de aquí se abren muchas posibilidades. Se podría hacer un Trabajo
Fin de Carrera por cada uno de los apartados del capítulo 4. Lo que sería muy
interesante, porque permitiría comprobar empíricamente cómo las ideas tienen
aplicación.
Para finalizar la exposición de este Trabajo Fin de Carrera mostramos un
prototipo del modelo propuesto. Hay que destacar que el prototipo no es una
implementación directa y funcional del modelo propuesto. El prototipo que
mostramos ha servido para experimentar y tomar decisiones sobre el modelo.
Por este motivo, existen puntos del modelo propuesto que no se ven reflejados
en el prototipo. Por contra, ciertas partes del prototipo no se han incluido en el
modelo. Esto se debe a que hay ciertos elementos que consideramos que son
detalles de una implementación particular. Otro motivo que hace que en el
135
Sobre la información, su estructura y su-gestión
prototipo aparezcan partes no comentadas en el modelo es por considerar que
son temas que no hemos estudiado con la profundidad suficiente debido a la
esfuerzo que supondría y que está fuera del ámbito de este Trabajo Fin de
Carrera.
136
VI. Bibliografía
Fernández, L.; Arroyo, F. Programación Orientada a Objetos. Publicaciones
de la E.U.I., 1998
Frutos, J.A; López, J.; Pérez, J.G. Teoría de Lenguajes de Programación.
Publicaciones de la E.U.I., 1998
Pérez, J.G. Implementación de un Subconjunto de Pascal. Publicaciones de la
E.U.I., 1998
Stasko, J.; Domingue, J.; Brown, M.H.; y Price, B.A. (eds.), Software
Visualization: Programming as a Multimedia Experience, MIT Press, 1998.
Novática, Marzo — Abril 2001 de ATI. http://www.ati.es/novatica/
Budd, T. Introducción a la Programación Orientada a Objetos. Addison
Wesley, 1994
Santos, E.; García, A.; Alonso, S.; Alarcón, P.P.; Garbajosa, J; Bases de Datos.
Publicaciones de la E.U.I. 1998
Booch, G.; Jacobson, I.; Rumbaugh, J.; El Lenguaje Unificado de Modelado.
Addison Wesley, 1999
Booch, G.; Jacobson, I.; Rumbaugh, J.; El lenguaje unificado de modelado.
Manual de referencia. Addison Wesley, 1999
137
Sobre la información, su estructura y su-gestión
Booch, G.; Jacobson, I.; Rumbaugh, J.; El proceso unificado de desarrollo de
software. Addison Wesley, 1999
Fowler, M.; Scott, K; UML Gota a Gota. Addison Wesley, 1999
Schildt, H.; Ansi C a su alcance. Mc Graw Hill, 1991
Barros, B.; Foulquie, M.T.; Serradilla, F.; Programación Funcional en LISP.
Publicaciones de la E.U.I., 1992
Fernández, L.; Arroyo, F.; Yela, A.; Programación Funcional. Programación
en CAML. Publicaciones de la E.U.I., 1997
Tannenbaum, A; Metadata Solutions: Using Metamodels, Repositories, XML,
and Enterprise Portals to Generate Information on Demand. Addison Wesley,
2002
Fayyad, U; Grinstein, G.; Wierse, A.; Information Visualization in Data
Mining and Knowledge Discovery. 2001
Détienne, F.; Software design - cognitive aspects. Frank Bott, 2002
Dorsey, P.; Hudicka, J.; Oracle8 Database Design Using UML Object
Modeling. Osborne McGraw-Hill (Oracle Press), 1998
Santos, E.; Estructuras de datos-I, Publicaciones de la E.U.I, 2000
Campione, M.; Walrath, K.; Huml, A; The Java(TM) Tutorial: A Short Course
on the Basics (3rd Edition). Addison-Wesley, 2000
Walrath, K.; Campione, M.; The JFC Swing Tutorial: A Guide to Constructing
GUIs. Addison-Wesley, 1999
Eckstein, R.; Loy, M.; Wood, D.; Java Swing. O'Reilly & Associates, 1998
Fernández, L; Tecnología Orientada a Objetos aplicada al desarrollo de la
Traducción de Lenguajes. Proyecto Fin de Carrera. Universidad de Málaga,
1999
Joyanes, L.; Fundamentos de Programación. Algoritmos y Estructuras de
Datos. McGraw-Hill, 1988
Hurtado, T.; Ingeniería del software. Publicaciones E.U.I., 1995
Jaworski, J.; Java 1.2 Al descubierto. Prentice Hall, 1998
Zaks, R.; Programación en Pascal, Turbo Pascal. Anaya Multimedia, 1986
138
Sobre la información, su estructura y su-gestión
Joyanes Aguilar, L.; Programación en Turbo-Borland Pascal 7. Osborne-
McGraw-Hill, 1997
Fernández, L.; 185 enunciados resueltos en Pascal. Publicaciones E.U.I., 1995
Borland International; Turbo Pascal, version 6.0 Programmer's guide, Scotts
Valley : Borland, 1990
Grogono, P.; Programming in Pascal, Addison-Wesley, 1984
Lakshman, B.; Oracle and Java development, Sams, cop. 2002
Arnold, K.; El lenguaje de programación Java, Pearson Educación, 2001
139
Anexo A. Fuentes
package tfc;
public interface AccesoAmbito {
public GestorAmbito getAmbito();
}
package tfc;
import javax.swing.UIManager;
import java.awt.*;
public class AppPrueba2 {
boolean packFrame = false;
//Construct the application
public AppPrueba2() {
Frame2 frame = new Frame2();
//Validate frames that have preset sizes
//Pack frames that have useful preferred size info, e.g. from their layout
if (packFrame) {
frame.pack();
}
else {
frame.validate();
}
//Center the window
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
}
140
Sobre la información, su estructura y su-gestión
//Main method
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
e.printStackTrace();
}
new AppPrueba2();
}
}
package tfc;
import java.util.*;
import java.io.PrintWriter;
public class Asignacion extends ElementoCodigo implements Sentencia {
protected RefVariable parteIzquierda;
protected Evaluable parteDerecha;
public Asignacion(RefVariable parteIzquierda, Evaluable parteDerecha) {
this.parteIzquierda = parteIzquierda;
this.parteDerecha = parteDerecha;
}
public RefVariable getParteIzquierda(){
return parteIzquierda;
}
public Evaluable getParteDerecha() {
return parteDerecha;
}
public String getRepTextual(){
return parteIzquierda.getRepTextual() + " := " +
parteDerecha.getRepTextual();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
parteIzquierda.resuelveReferencias(ambito,errores);
parteDerecha.resuelveReferencias(ambito,errores);
}
private int codigo;
public void convertirEnEjecutable(PrintWriter salida, GestorIdentificadores
gestor){
this.codigo = gestor.getCodigo();
this.parteIzquierda.convertirEnEjecutable(salida,gestor);
this.parteDerecha.convertirEnEjecutable(salida,gestor);
salida.println("//------------------------------------------------------------------------");
salida.println("class "+this.getNombreEjecucion()+" extends
AsignacionEjecucion {");
salida.println(" public EvaluableEjecucionI getParteDerecha(){");
salida.println("
return new
"+parteDerecha.getNombreEjecucion()+"(); ");
salida.println(" }");
salida.println(" public RefVariableEjecucion
getRefVariableEjecucion() {");
salida.println("
return new
"+parteIzquierda.getNombreEjecucion()+"(); ");
salida.println(" }");
141
Sobre la información, su estructura y su-gestión
salida.println("}");
}
public String getNombreEjecucion(){
return "AsignacionEjecucion"+codigo;
}
}
package tfc;
import java.io.PrintWriter;
public abstract class AsignacionEjecucion extends InstruccionEjecucion {
public void ejecuta(InformacionAccesible i, PrintWriter salida){
salida.println("Inicio ejecución asignación");
salida.println("Inicio evaluación parte derecha");
Valor valor = this.getParteDerecha().evaluate(i,salida);
salida.println("Fin evaluación parte derecha, el valor
es:"+valor.getRepTextual());
this.getRefVariableEjecucion().getItemDeInformacion(i).setValor(
valor , salida);
salida.println("Fin ejecución asignación");
}
public abstract EvaluableEjecucionI getParteDerecha();
public abstract RefVariableEjecucion getRefVariableEjecucion();
}
package tfc;
import javax.swing.tree.*;
public class AsignacionTreeNode extends InstruccionTreeNode {
Asignacion asignacion;
RefVariableTreeNode parteIzquierda;
EvaluableTreeNodeI parteDerecha;
public ElementoCodigoInterfaz getElementoCodigo(){
return asignacion;
}
public AsignacionTreeNode(Asignacion asignacion) {
this.setModeloRecursivo(asignacion);
}
public AsignacionTreeNode(){
setCabecera("Asignacion -> ");
}
public int getIndexOfChild(Object child){
if (child == parteIzquierda){
return 0;
} else {
return 1;
}
}
142
Sobre la información, su estructura y su-gestión
public void valueForPathChanged(TreePath path, Object newValue){
// De momento no se permite la edición.
}
public boolean isLeaf(){
return false;
}
public int getChildCount(){
return 2;
}
public Object getChild(int index){
switch (index) {
case 0: return parteIzquierda;
case 1: return parteDerecha;
default: return null;
}
}
public void setModelo(Object asignacion){
this.asignacion = (Asignacion) asignacion;
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
Class clase;
Class claseTreeNode;
try {
clase = asignacion.getParteIzquierda().getClass();
claseTreeNode = Class.forName( clase.getName() + "TreeNode" );
parteIzquierda =
(RefVariableTreeNode)claseTreeNode.newInstance();
parteIzquierda.setModeloRecursivo(
asignacion.getParteIzquierda() );
clase = asignacion.getParteDerecha().getClass();
claseTreeNode = Class.forName( clase.getName() + "TreeNode" );
parteDerecha =
(EvaluableTreeNodeI)claseTreeNode.newInstance();
parteDerecha.setModeloRecursivo( asignacion.getParteDerecha()
);
} catch (ClassNotFoundException e){
System.out.println(e.getMessage());
e.printStackTrace();
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
package tfc;
public class Booleano extends Enumerado {
static Booleano yo = null;
public static Booleano getInstance() {
if (yo == null){
yo = new Booleano();
}
return yo;
}
private Booleano(){
143
Sobre la información, su estructura y su-gestión
this.nombre = new Nombre("boolean");
this.representacion = "Verdadero o Falso, si o no";
valores.add( new ValorOrdinal("true",this));
valores.add( new ValorOrdinal("false",this));
}
public Valor getValorAleatorio(){
return new ValorSimple("true",this);
}
}
package tfc;
public class Caracter extends Enumerado {
static Caracter yo = null;
public static Caracter getInstance() {
if (yo == null){
yo = new Caracter();
}
return yo;
}
private Caracter(){
this.setNombre(new Nombre("char"));
this.representacion = "Caracteres";
for(int i=0; i<256;i++){
valores.add( new
ValorOrdinal("/'"+Character.forDigit(i,10)+"/'",this));
}
}
public Valor getValorAleatorio(){
return new ValorSimple("a",this);
}
}
package tfc;
public class CargadorClaseDirectorioTrabajo extends ClassLoader {
public CargadorClaseDirectorioTrabajo() {
super();
}
// Busca la clase en el directorio de trabajo
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
try {
java.io.File ficheroF = new
java.io.File(System.getProperty("user.dir")+
java.io.File.separator+name+".class");
java.io.FileInputStream fichero = new
java.io.FileInputStream(ficheroF);
byte[] bytes = new byte[ (int)ficheroF.length() ];
fichero.read(bytes);
fichero.close();
144
Sobre la información, su estructura y su-gestión
return bytes;
} catch(Exception e){
e.printStackTrace();
}
return null;
}
}
package tfc;
import java.io.*;
public class CompiladorJava {
String rutaCompilador;
public CompiladorJava(String rutaCompilador) {
this.rutaCompilador = rutaCompilador;
}
// Compila el programa pasado en el String y le deja compilado en el
directorio
// indicado. La clase debe pertenecer al paquete por defecto. El directorio
// indicado será añadido al PATH para poder ejecutar la clase mediante
reflection.
// Para que posteriormente funcione el reflection, el classpath debe estar
puesto
// en el directorio actual (de trabajo).
public Class compila(String programa, String nombreClase) throws
ErrorCompilacionException {
File f = new File(System.getProperty("user.dir") + File.separator +
nombreClase+".java");
if (f.exists()){
f.delete();
}
boolean excepcion = false;
String resultado = "";
try{
f.createNewFile();
java.io.PrintWriter writer = new java.io.PrintWriter( new
java.io.FileOutputStream(f));
f.createNewFile();
writer.print( programa );
writer.flush();
writer.close();
Process proceso = Runtime.getRuntime().exec(rutaCompilador+"
\""+System.getProperty("user.dir") +
File.separator + nombreClase+".java\"");
LineNumberReader flujoSalida = new LineNumberReader( new
InputStreamReader( new java.io.BufferedInputStream(proceso.getInputStream())));
LineNumberReader flujoError = new LineNumberReader( new
InputStreamReader( new java.io.BufferedInputStream(proceso.getErrorStream())));
145
Sobre la información, su estructura y su-gestión
do {
String salida = flujoSalida.readLine();
if (salida == null) { break; }
resultado += salida+"\n";
} while (true);
do {
String salida = flujoError.readLine();
if (salida == null) { break; }
resultado += salida+"\n";
} while (true);
excepcion = !resultado.equals("");
} catch( Exception e){
e.printStackTrace();
}
if (excepcion) {
throw new ErrorCompilacionException(resultado);
} else {
try {
ClassLoader cargador = new
CargadorClaseDirectorioTrabajo();
return cargador.loadClass(nombreClase);
} catch(Exception e2){
e2.printStackTrace();
}
}
return null;
}
}
package tfc;
public class Condicional extends ElementoCodigo implements Sentencia {
Evaluable evaluable;
Sentencia parteThen = null;
Sentencia parteElse = null;
public Condicional(Evaluable evaluable, Sentencia ejecutable) {
this.evaluable = evaluable;
this.parteThen = ejecutable;
}
public void setParteElse(Sentencia parteElse){
this.parteElse = parteElse;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores) {
evaluable.resuelveReferencias(ambito,errores);
parteThen.resuelveReferencias(ambito,errores);
if (parteElse != null) parteElse.resuelveReferencias(ambito,errores);
}
public String getRepTextual(){
String resultado = "";
resultado += "IF " + evaluable.getRepTextual() + " THEN \n";
resultado += parteThen.getRepTextual();
resultado += (parteElse == null)?"": "ELSE\n" +
parteElse.getRepTextual();
return resultado;
}
146
Sobre la información, su estructura y su-gestión
public Evaluable getEvaluable(){
return evaluable;
}
public Sentencia getParteThen(){
return parteThen;
}
public Sentencia getParteElse(){
return parteElse;
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
// POR HACER
}
}
package tfc;
import java.io.PrintWriter;
public abstract class CondicionalEjecucion extends InstruccionEjecucion {
public void ejecuta(InformacionAccesible i, PrintWriter salida){
salida.println("Inicio ejecucion if");
salida.println("Inicio evalacion expresion");
Valor v = this.evaluaExpresion(i);
salida.println("Fin evaluacion expresion. Valor:
"+v.getRepTextual());
// Ojo, este .equals debería de ser un == pero por comodidad se hace
equals
if ( v.equals( Booleano.getInstance().getValor("true") ) ){
salida.println("Inicio ejecucion parte then");
this.ejecutaParteThen(i);
salida.println("Fin ejecucion parte then");
} else {
salida.println("Inicio ejecucion parte else");
this.ejecutaParteElse(i);
salida.println("Fin ejecucion parte else");
}
}
public abstract Valor evaluaExpresion(InformacionAccesible i);
public abstract void ejecutaParteThen(InformacionAccesible i);
public abstract void ejecutaParteElse(InformacionAccesible i);
}
package tfc;
import javax.swing.tree.TreePath;
public class CondicionalTreeNode extends InstruccionTreeNode {
Condicional condicional;
EvaluableTreeNodeI evaluable;
EjecutableTreeNodeI parteThen;
EjecutableTreeNodeI parteElse = null;
public CondicionalTreeNode() {
}
public boolean isLeaf() {
147
Sobre la información, su estructura y su-gestión
return false;
}
public Object getChild(int parm1) {
switch (parm1) {
case 0: return evaluable;
case 1: return parteThen;
default: return parteElse;
}
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
evaluable = (EvaluableTreeNodeI)TreeNode.getTreeNode(
condicional.getEvaluable() );
evaluable.setCabecera("Expresion -> ");
parteThen = (EjecutableTreeNodeI)TreeNode.getTreeNode(
condicional.getParteThen() );
parteThen.setCabecera("Then ");
Sentencia parteElseAux = condicional.getParteElse();
if (parteElseAux != null){
parteElse = (EjecutableTreeNodeI)TreeNode.getTreeNode(
parteElseAux );
parteElse.setCabecera("Else ");
}
}
public int getIndexOfChild(Object parm1) {
if (parm1 == evaluable){
return 0;
} else if (parm1 == parteThen){
return 1;
} else {
return 2;
}
}
public int getChildCount() {
return (parteElse == null)? 2: 3;
}
public void valueForPathChanged(TreePath parm1, Object parm2) {
}
public ElementoCodigoInterfaz getElementoCodigo() {
return condicional;
}
public void setModelo(Object obj) {
this.condicional = (Condicional)obj;
}
}
package tfc;
public class DefConstante extends ElementoCodigo implements Definicion,
ElementoCodigoEjecucion {
protected Nombre nombre;
protected Valor valor;
public DefConstante( Nombre nombre, Valor valor ) {
this.nombre = nombre;
this.valor = valor;
}
public Nombre getNombre(){
return nombre;
}
public String getRepTextual(){
148
Sobre la información, su estructura y su-gestión
return nombre+":"+valor.getRepTextual();
}
public Valor getValor(){
return valor;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
// POR HACER
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
this.getValor().convertirEnEjecutable(salida,gestor);
salida.println("class "+this.getNombreEjecucion()+" extends
ItemDeInformacion {");
salida.println(" public "+this.getNombreEjecucion()+"(){");
salida.println(" }");
salida.println(" public void creacion(java.io.PrintWriter salida){
");
salida.println("
salida.println(\"Reservada memoria
constante\");");
salida.println("
this.setValor( new
"+this.getValor().getNombreEjecucion()+"().evaluate(null,salida),salida);");
salida.println("
}");
salida.println("}");
}
}
package tfc;
public abstract class DefConstanteEjecucion extends Ejecucion {
Valor valor;
public void creacion(){
valor = this.getValor();
}
public Valor getValor(){
return valor;
}
}
package tfc;
import javax.swing.tree.*;
public class DefConstanteTreeNode extends TreeNode {
DefConstante defConstante;
NombreTreeNode nombre;
ValorSimpleTreeNode valor;
public ElementoCodigoInterfaz getElementoCodigo(){
return defConstante;
}
public DefConstanteTreeNode(DefConstante defConstante) {
this.setModeloRecursivo(defConstante);
}
public DefConstanteTreeNode() {
this.setCabecera("Definicion de Constante -> ");
}
149
Sobre la información, su estructura y su-gestión
public void setModelo(Object defConstante){
this.defConstante = (DefConstante) defConstante;
}
public int getIndexOfChild(Object child){
if (child == nombre){
return 0;
} else {
return 1;
}
}
public void valueForPathChanged(TreePath path, Object newValue){
// De momento no se permite la edición.
}
public boolean isLeaf(){
return false;
}
public int getChildCount(){
return 2;
}
public Object getChild(int index){
switch (index) {
case 0: return nombre;
case 1: return valor;
default: return null;
}
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
nombre =
(NombreTreeNode)TreeNode.getTreeNode(defConstante.getNombre());
valor =
(ValorSimpleTreeNode)TreeNode.getTreeNode(defConstante.getValor());
}
public String toString(){
return defConstante.toString();
}
}
package tfc;
public class DefFuncion extends DefSubprograma {
protected RefTipo tipoRetorno;
public DefFuncion(Nombre s, AccesoAmbito ambitoSuperior){
super(s,ambitoSuperior);
}
public DefFuncion(){}
public void setTipo(RefTipo refTipo){
this.tipoRetorno = refTipo;
}
protected String getRTCabecera(){
// El tipo de retorno va al final de la declaracion, pero de momento
lo pongo
// así por comodidad.
return "FUNCTION "+ this.tipoRetorno.getRepTextual() + ":"+
this.getNombre();
}
150
Sobre la información, su estructura y su-gestión
protected void setTipoRetorno(RefTipo tipoRetorno){
this.tipoRetorno = tipoRetorno;
}
public DefTipo getDefTipo(){
return tipoRetorno.getDefTipo();
}
}
package tfc;
public abstract class DefFuncionEjecucion extends DefSubprogramaEjecucion {
Valor v;
public DefFuncionEjecucion() {
}
public Valor getValor(){
return v;
}
protected void setValor(Valor v){
this.v = v;
}
}
package tfc;
public abstract class DefFuncionOperador extends DefFuncion {
public DefFuncionOperador(Nombre n){
super(n,null);
}
}
package tfc;
public class DefFuncionOperadorAnd extends DefFuncionOperador {
public DefFuncionOperadorAnd() {
super(new Nombre("AND"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Booleano.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Booleano.getInstance())));
this.setTipo( new RefTipo( new Nombre("booleano")));
}
}
package tfc;
public class DefFuncionOperadorDivision extends DefFuncionOperador {
public DefFuncionOperadorDivision() {
super(new Nombre("DIV"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
151
Sobre la información, su estructura y su-gestión
public class DefFuncionOperadorIgualdad extends DefFuncionOperador {
public DefFuncionOperadorIgualdad() {
super(new Nombre("="));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("boolean")));
}
}
package tfc;
public class DefFuncionOperadorMod extends DefFuncionOperador {
public DefFuncionOperadorMod() {
super(new Nombre("MOD"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
public class DefFuncionOperadorMultiplicacion extends DefFuncionOperador {
public DefFuncionOperadorMultiplicacion() {
super(new Nombre("*"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
public class DefFuncionOperadorNegacion extends DefFuncionOperador {
public DefFuncionOperadorNegacion() {
super(new Nombre("-"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
public class DefFuncionOperadorNot extends DefFuncionOperador {
public DefFuncionOperadorNot() {
super(new Nombre("NOT"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Booleano.getInstance())));
this.setTipo( new RefTipo( new Nombre("boolean")));
}
}
package tfc;
public class DefFuncionOperadorOr extends DefFuncionOperador {
152
Sobre la información, su estructura y su-gestión
public DefFuncionOperadorOr() {
super(new Nombre("OR"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Booleano.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Booleano.getInstance())));
this.setTipo( new RefTipo( new Nombre("boolean")));
}
}
package tfc;
public class DefFuncionOperadorResta extends DefFuncionOperador {
public DefFuncionOperadorResta() {
super(new Nombre("-"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
public class DefFuncionOperadorSuma extends DefFuncionOperador {
public DefFuncionOperadorSuma() {
super(new Nombre("+"));
this.getParametros().addElement( new DefParametro(new Nombre("a"),new
RefTipo(Entero.getInstance())));
this.getParametros().addElement( new DefParametro(new Nombre("b"),new
RefTipo(Entero.getInstance())));
this.setTipo( new RefTipo( new Nombre("integer")));
}
}
package tfc;
public class DefFuncionTreeNode extends DefSubprogramaTreeNode {
public DefFuncionTreeNode() {
}
public DefFuncionTreeNode(DefFuncion defFuncion) {
super(defFuncion);
}
}
package tfc;
public interface Definicion {
public Nombre getNombre();
}
package tfc;
public class DefinicionNoEncontradaException extends Exception {
Nombre nombre;
String tipoDefinicion;
public DefinicionNoEncontradaException(Nombre nombre, String tipoDefinicion)
{
153
Sobre la información, su estructura y su-gestión
this.nombre = nombre;
this.tipoDefinicion = tipoDefinicion;
}
public String toString(){
return "No se ha encontrado un "+tipoDefinicion+" con el nombre
\""+nombre.toString()+"\".";
}
}
package tfc;
public class DefParametro extends DefVariable {
private boolean pasoPorValor;
public DefParametro(Nombre nombre, RefTipo refTipo) {
this(nombre,refTipo,true);
}
public boolean pasoPorValor(){
return pasoPorValor;
}
public DefParametro(Nombre nombre, RefTipo refTipo,boolean pasoPorValor) {
super(nombre,refTipo);
this.pasoPorValor = pasoPorValor;
}
public String getRepTextual(){
String resultado = super.getRepTextual();
if (!pasoPorValor){
resultado = "VAR "+resultado;
}
return resultado;
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
salida.println("class "+this.getNombreEjecucion()+" extends
ItemDeInformacionParam {");
salida.println(" public "+this.getNombreEjecucion()+"(){}");
salida.println("}");
}
}---package tfc;
public class DefProcedimiento extends DefSubprograma {
public DefProcedimiento(Nombre s, AccesoAmbito ambitoSuperior){
super(s,ambitoSuperior);
}
public DefProcedimiento(Nombre s){
super(s);
}
protected String getRTCabecera(){
return "PROCEDURE "+ this.getNombre();
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
this.subprogramas.convertirEnEjecutable(salida,gestor);
this.constantes.convertirEnEjecutable(salida,gestor);
this.variables.convertirEnEjecutable(salida,gestor);
//this.parametros.convertirEnEjecutable(salida,gestor);
154
Sobre la información, su estructura y su-gestión
int codigoInformacion =
InformacionAccesible.convertirEnEjecutable(salida,gestor,constantes,parametros,vari
ables);
this.cuerpo.convertirEnEjecutable(salida,gestor);
salida.println("//---------------------------------------------------------------------------------------");
salida.println("class "+this.getNombreEjecucion()+" extends
DefProcedimientoEjecucion {");
salida.println(" public "+this.getNombreEjecucion()+"() {}");
salida.println(" public InformacionAccesible
getInformacionAccesible(InformacionAccesible ambitoSuperior, java.io.PrintWriter
salida){");
salida.println("
InformacionAccesible i = new
InformacionAccesible"+codigoInformacion+"();");
salida.println("
i.creacion(ambitoSuperior,salida);");
salida.println("
return i;");
salida.println(" }");
salida.println(" public InformacionAccesible
getInformacionAccesible(ParametrosActualesValoresEItems p, InformacionAccesible
inf, java.io.PrintWriter salida){");
salida.println("
InformacionAccesible i = new
InformacionAccesible"+codigoInformacion+"();");
salida.println("
i.creacion(p,inf,salida);");
salida.println("
return i;");
salida.println(" }");
salida.println(" public void
ejecutaInstrucciones(InformacionAccesible i, java.io.PrintWriter salida){");
salida.println("
EjecutableEjecucionI e;");
salida.println("
e = new
"+this.cuerpo.getNombreEjecucion()+"();");
salida.println("
e.ejecuta(i,salida);");
salida.println("
e.fin();");
salida.println(" }");
salida.println("}");
salida.println("//---------------------------------------------------------------------------------------");
}
}
package tfc;
public abstract class DefProcedimientoEjecucion extends DefSubprogramaEjecucion {
public DefProcedimientoEjecucion() {
}
}
package tfc;
public class DefProcedimientoTreeNode extends DefSubprogramaTreeNode {
public DefProcedimientoTreeNode() {
}
public DefProcedimientoTreeNode(DefProcedimiento defProcedimiento) {
super(defProcedimiento);
}
}
package tfc;
import javax.swing.*;
import javax.swing.event.*;
public class DefPrograma extends ElementoCodigo implements Definicion,
AccesoAmbito, ElementoCodigoEjecucion {
155
Sobre la información, su estructura y su-gestión
protected
protected
protected
protected
protected
protected
protected
Nombre nombre;
SecuenciaDefConstante constantes;
SecuenciaDefVariable variables;
SecuenciaDefSubprograma subprogramas;
SecuenciaDefTipo tipos;
InstruccionCompuesta cuerpo;
GestorAmbito ambito;
protected DefPrograma(){}
public DefPrograma(Nombre nombre){
this.nombre = nombre;
this.constantes = new SecuenciaDefConstante();
this.variables = new SecuenciaDefVariable();
this.subprogramas = new SecuenciaDefSubprograma();
this.tipos = new SecuenciaDefTipo();
this.cuerpo = new InstruccionCompuesta();
setAmbito(new
GestorAmbito(tipos,variables,constantes,subprogramas,new
SecuenciaDefParametro(),new Predefinidos()));
}
public GestorAmbito getAmbito(){
return ambito;
}
public void setCuerpo(InstruccionCompuesta cuerpo){
this.cuerpo = cuerpo;
}
protected void setAmbito(GestorAmbito ambito){
this.ambito = ambito;
}
public SecuenciaDefConstante getConstantes(){
return constantes;
}
public SecuenciaDefVariable getVariables(){
return variables;
}
public SecuenciaDefSubprograma getSubprogramas(){
return subprogramas;
}
public SecuenciaDefTipo getTipos(){
return tipos;
}
public InstruccionCompuesta getCuerpo(){
return cuerpo;
}
public String getRepTextual() {
String resultado ="";
resultado += getRTCabecera() + '\n';
resultado += getRTCuerpo() + ".";
return resultado;
}
protected String getRTCabecera(){
return "PROGRAM "+ this.nombre +";";
}
protected String getRTCuerpo(){
String resultado = "";
156
Sobre la información, su estructura y su-gestión
for(int i=0; i<subprogramas.getSize(); i++) {
resultado +=
((DefSubprograma)subprogramas.getElementAt(i)).getRepTextual() + ";"+'\n';
}
if (tipos.size() != 0){
resultado += "TYPE"+'\n';
for(int i=0; i<tipos.getSize(); i++) {
resultado +=
((DefTipo)tipos.getElementAt(i)).getRepTextual() + ";"+'\n';
}
}
if (constantes.size() != 0){
resultado += "CONST"+'\n';
for(int i=0; i<constantes.getSize(); i++) {
resultado +=
((DefConstante)constantes.getElementAt(i)).getRepTextual() + ";"+'\n';
}
}
if (variables.size() != 0){
resultado += "VAR"+'\n';
for(int i=0; i<variables.getSize(); i++) {
resultado +=
((DefVariable)variables.getElementAt(i)).getRepTextual() + ";"+'\n';
}
}
for(int i=0; i<constantes.getSize(); i++) {
resultado +=
((DefSubprograma)subprogramas.getElementAt(i)).getRepTextual() + ";"+'\n';
}
resultado += cuerpo.getRepTextual();
return resultado;
}
public Nombre getNombre(){
return nombre;
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
salida.println("import tfc.*;");
this.setCodigo(gestor);
this.subprogramas.convertirEnEjecutable(salida,gestor);
this.constantes.convertirEnEjecutable(salida,gestor);
this.variables.convertirEnEjecutable(salida,gestor);
int codigoInformacion =
InformacionAccesible.convertirEnEjecutable(salida,gestor,constantes,new
SecuenciaDefParametro(),variables);
this.cuerpo.convertirEnEjecutable(salida,gestor);
salida.println("//---------------------------------------------------------------------------------------");
salida.println("public class "+this.getNombreEjecucion()+" extends
DefProgramaEjecucion {");
salida.println(" public "+this.getNombreEjecucion()+"() {}");
salida.println(" public InformacionAccesible
getInformacionAccesible(InformacionAccesible ambitoSuperior, java.io.PrintWriter
salida){");
157
Sobre la información, su estructura y su-gestión
salida.println("
InformacionAccesible i = new
InformacionAccesible"+codigoInformacion+"();");
salida.println("
i.creacion(ambitoSuperior,salida);");
salida.println("
return i;");
salida.println(" }");
salida.println(" public void
ejecutaInstrucciones(InformacionAccesible i, java.io.PrintWriter salida){");
salida.println("
EjecutableEjecucionI e;");
salida.println("
e = new
"+this.cuerpo.getNombreEjecucion()+"();");
salida.println("
e.ejecuta(i,salida);");
salida.println("
e.fin();");
salida.println(" }");
salida.println("}");
salida.println("//---------------------------------------------------------------------------------------");
}
public ErroresReferencias resuelveReferencias(){
ErroresReferencias errores = new ErroresReferencias();
this.resuelveReferencias(ambito,errores);
return errores;
}
public void resuelveReferencias(GestorAmbito ambitoSuperior,
ErroresReferencias errores){
System.out.println("Ambito del programa
"+this.getNombre().toString()+"\n"+ambito.getRepTextual());
constantes.resuelveReferencias(ambito,errores);
variables.resuelveReferencias(ambito,errores);
tipos.resuelveReferencias(ambito,errores);
subprogramas.resuelveReferencias(ambito,errores);
cuerpo.resuelveReferencias(ambito,errores);
}
}
package tfc;
import java.io.PrintWriter;
public abstract class DefProgramaEjecucion extends Ejecucion implements
EjecutableEjecucionI {
public void ejecuta(InformacionAccesible inf, PrintWriter salida){
salida.println("Inicio ejecución programa");
InformacionAccesible i = this.getInformacionAccesible(new
InformacionAccesiblePredefinida(), salida);
this.ejecutaInstrucciones(i,salida);
i.liberacion(salida);
salida.println("Fin ejecución programa");
}
public void fin(){
}
public abstract InformacionAccesible
getInformacionAccesible(InformacionAccesible ambitoSuperior, java.io.PrintWriter
salida);
public abstract void ejecutaInstrucciones(InformacionAccesible i,
java.io.PrintWriter salida);
}
158
Sobre la información, su estructura y su-gestión
package tfc;
import
import
import
import
javax.swing.tree.*;
javax.swing.event.*;
java.lang.reflect.*;
java.util.HashMap;
public class DefProgramaTreeModel implements TreeModel {
protected DefProgramaTreeNode defProgramaModelo2;
protected EventListenerList listenerList = new EventListenerList();
public DefProgramaTreeModel(DefPrograma defPrograma) {
this.defProgramaModelo2 = new DefProgramaTreeNode(defPrograma);
}
public void addTreeModelListener(TreeModelListener l) {
listenerList.add(TreeModelListener.class, l);
}
public void removeTreeModelListener(TreeModelListener l) {
listenerList.remove(TreeModelListener.class, l);
}
public int getIndexOfChild(Object parent, Object child){
return ((TreeNode)parent).getIndexOfChild(child);
}
public void valueForPathChanged(TreePath path, Object newValue){
}
public boolean isLeaf(Object node){
return ((TreeNode)node).isLeaf();
}
public int getChildCount(Object parent){
return ((TreeNode)parent).getChildCount();
}
public Object getChild(Object parent, int index){
return ((TreeNode)parent).getChild(index);
}
public Object getRoot(){
return defProgramaModelo2;
}
}
package tfc;
import javax.swing.tree.*;
public class DefProgramaTreeNode extends TreeNode {
DefPrograma defPrograma;
NombreTreeNode nombre;
SecuenciaDefTipoTreeNode tipos;
SecuenciaDefConstanteTreeNode constantes;
SecuenciaDefVariableTreeNode variables;
SecuenciaDefSubprogramaTreeNode subprogramas;
InstruccionCompuestaTreeNode cuerpo;
public DefProgramaTreeNode(DefPrograma defPrograma) {
this.setModeloRecursivo(defPrograma);
159
Sobre la información, su estructura y su-gestión
}
public DefProgramaTreeNode() {
}
public ElementoCodigoInterfaz getElementoCodigo(){
return defPrograma;
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
switch (index) {
case 0: return nombre;
case 1: return tipos;
case 2: return constantes;
case 3: return variables;
case 4: return subprogramas;
case 5: return cuerpo;
default: return null;
}
}
public int getIndexOfChild(Object child) {
if (child == nombre){
return 0;
} else if (child == tipos){
return 1;
} else if (child == constantes){
return 2;
} else if (child == variables){
return 3;
} else if (child == subprogramas){
return 4;
} else if (child == cuerpo){
return 5;
}
return -1;
}
public int getChildCount() {
return 6;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
this.defPrograma = (DefPrograma) obj;
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
nombre = new NombreTreeNode(defPrograma.getNombre());
nombre.setCabecera("Nombre: ");
tipos = new SecuenciaDefTipoTreeNode(defPrograma.getTipos());
tipos.setCabecera("Tipos");
constantes = new
SecuenciaDefConstanteTreeNode(defPrograma.getConstantes());
constantes.setCabecera("Constantes");
variables = new
SecuenciaDefVariableTreeNode(defPrograma.getVariables());
variables.setCabecera("Variables");
subprogramas = new
SecuenciaDefSubprogramaTreeNode(defPrograma.getSubprogramas());
subprogramas.setCabecera("Subprogrmas");
160
Sobre la información, su estructura y su-gestión
cuerpo = new InstruccionCompuestaTreeNode(defPrograma.getCuerpo());
cuerpo.setCabecera("Cuerpo");
}
public String toString(){
return "Programa";
}
}
package tfc;
import javax.swing.DefaultListModel;
public abstract class DefSubprograma extends DefPrograma implements Definicion {
protected SecuenciaDefParametro parametros;
protected DefSubprograma(){}
protected DefSubprograma(Nombre nombre, AccesoAmbito ambitoSuperior){
this.nombre = nombre;
this.constantes = new SecuenciaDefConstante();
this.variables = new SecuenciaDefVariable();
this.subprogramas = new SecuenciaDefSubprograma();
this.tipos = new SecuenciaDefTipo();
this.cuerpo = new InstruccionCompuesta();
GestorAmbito ambito = null;
if (ambitoSuperior != null) {
ambito = ambitoSuperior.getAmbito();
}
setAmbito(new
GestorAmbito(tipos,variables,constantes,subprogramas,new
SecuenciaDefParametro(),ambito));
this.parametros = new SecuenciaDefParametro();
}
protected DefSubprograma(Nombre s){
super(s);
this.parametros = new SecuenciaDefParametro();
}
public SecuenciaDefParametro getParametros(){
return parametros;
}
protected String getRTParametros(){
String resultado = "";
resultado += "(";
if (parametros.getSize() > 0) {
for (int i=0; i < parametros.getSize() - 1; i++){
resultado += ((DefParametro)
parametros.getElementAt(i)).getRepTextual()+";";
}
resultado += ((DefParametro)
parametros.lastElement()).getRepTextual();
}
resultado += ");";
return resultado;
}
public String getRepTextual() {
String resultado ="";
resultado += getRTCabecera() + " " + getRTParametros() + ";"+'\n';
resultado += getRTCuerpo();
return resultado;
}
161
Sobre la información, su estructura y su-gestión
public boolean compatible(ParametrosActuales p){
return p.compatible(parametros);
}
public void resuelveReferencias(GestorAmbito ambitoSuperior,
ErroresReferencias errores){
System.out.println(this.getNombre().toString()+"\n"+ambito.getRepTextual());
// this.ambito.superior = ambitoSuperior;
constantes.resuelveReferencias(ambito,errores);
variables.resuelveReferencias(ambito,errores);
tipos.resuelveReferencias(ambito,errores);
subprogramas.resuelveReferencias(ambito,errores);
cuerpo.resuelveReferencias(ambito,errores);
}
}
package tfc;
import java.io.PrintWriter;
public abstract class DefSubprogramaEjecucion extends DefProgramaEjecucion {
public DefSubprogramaEjecucion() {
}
public void ejecuta(InformacionAccesible i, PrintWriter salida){
this.ejecuta(null,i,salida);
}
public void ejecuta(ParametrosActualesValoresEItems p, InformacionAccesible
inf, PrintWriter salida){
salida.println("Inicio ejecución procedimiento");
InformacionAccesible i = this.getInformacionAccesible(p,inf,salida);
this.ejecutaInstrucciones(i,salida);
i.liberacion(salida);
salida.println("Fin ejecución procedimiento");
}
public abstract InformacionAccesible
getInformacionAccesible(ParametrosActualesValoresEItems p, InformacionAccesible
inf, PrintWriter salida);
public abstract void ejecutaInstrucciones(InformacionAccesible i,
PrintWriter salida);
public abstract InformacionAccesible
getInformacionAccesible(InformacionAccesible ambitoSuperior, PrintWriter salida);
}
package tfc;
public class DefSubprogramaTreeNode extends DefProgramaTreeNode {
public DefSubprogramaTreeNode(DefSubprograma defSubprograma) {
super(defSubprograma);
}
public DefSubprogramaTreeNode() {
}
}
package tfc;
162
Sobre la información, su estructura y su-gestión
public abstract class DefTipo extends ElementoCodigo implements Definicion {
protected Nombre nombre;
protected String representacion;
public Nombre getNombre(){
return nombre;
}
protected void setNombre(Nombre nombre){
this.nombre = nombre;
}
}
package tfc;
public abstract class DefTipoPredefinido extends DefTipo {
}
package tfc;
// Sin implementar todavía
import javax.swing.tree.TreePath;
public class DefTipoTreeNode extends TreeNode {
DefTipo defTipo;
public ElementoCodigoInterfaz getElementoCodigo(){
return defTipo;
}
public DefTipoTreeNode(DefTipo defTipo) {
this.defTipo = defTipo;
setCabecera("Definicion de Tipo ->");
}
public DefTipoTreeNode() {
setCabecera("Definicion de Tipo ->");
}
public boolean isLeaf() {
return true;
}
public Object getChild(int index) {
return null;
}
public void setModeloRecursivo(Object obj) {
}
public int getIndexOfChild(Object child) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
}
}
package tfc;
public class DefVariable extends ElementoCodigo implements Definicion {
Nombre nombre;
RefTipo refTipo;
163
Sobre la información, su estructura y su-gestión
public DefVariable(Nombre nombre, RefTipo refTipo) {
this.nombre = nombre;
this.refTipo = refTipo;
}
public RefTipo getRefTipo(){
return refTipo;
}
public Nombre getNombre(){
return nombre;
}
public String getRepTextual(){
return nombre+" : "+refTipo.getRepTextual();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
refTipo.resuelveReferencias(ambito,errores);
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
salida.println("class "+this.getNombreEjecucion()+" extends
ItemDeInformacion {");
salida.println(" public "+this.getNombreEjecucion()+"(){}");
salida.println("}");
}
}
package tfc;
import javax.swing.tree.*;
public class DefVariableTreeNode extends TreeNode {
DefVariable defVariable;
NombreTreeNode nombre;
RefTipoTreeNode refTipo;
public ElementoCodigoInterfaz getElementoCodigo(){
return defVariable;
}
public DefVariableTreeNode(DefVariable defVariable) {
this.setModeloRecursivo(defVariable);
}
public DefVariableTreeNode() {
this.setCabecera("Definicion de Variable -> ");
}
public void setModelo(Object defVariable){
this.defVariable = (DefVariable) defVariable;
}
public int getIndexOfChild(Object child){
if (child == nombre){
return 0;
} else {
return 1;
}
}
public void valueForPathChanged(TreePath path, Object newValue){
// De momento no se permite la edición.
}
public boolean isLeaf(){
164
Sobre la información, su estructura y su-gestión
return false;
}
public int getChildCount(){
return 2;
}
public Object getChild(int index){
switch (index) {
case 0: return nombre;
case 1: return refTipo;
default: return null;
}
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
nombre = new NombreTreeNode(defVariable.getNombre());
refTipo = new RefTipoTreeNode(defVariable.getRefTipo());
}
public String toString(){
return defVariable.toString();
}
}
package tfc;
public class Ejecucion {
public Ejecucion() {
}
}
package tfc;
import java.io.PrintWriter;
public interface EjecutableEjecucionI {
public void ejecuta(InformacionAccesible i, PrintWriter salida);
public void fin();
}
package tfc;
public interface EjecutableTreeNodeI extends TreeNodeI {
public void setCabecera(String cabecera);
}
package tfc;
import java.util.Vector;
import javax.swing.event.*;
public abstract class ElementoCodigo implements ElementoCodigoInterfaz {
GestorAmbito ambitoSuperior;
int codigo;
public String getRepTextual(){
return super.toString();
}
public String toString(){
return this.getRepTextual();
}
public void setAmbitoSuperior(GestorAmbito ambitoSuperior){
this.ambitoSuperior = ambitoSuperior;
165
Sobre la información, su estructura y su-gestión
}
public void setCodigo(GestorIdentificadores gestor){
this.codigo = gestor.getCodigo();
}
public String getNombreEjecucion(){
StringBuffer nombreCompleto = new
StringBuffer(this.getClass().getName());
int tamañoPaquete = this.getClass().getpackage().getName().length();
return
nombreCompleto.substring(tamañoPaquete+1,nombreCompleto.length())+"Ejecucion"+this.
codigo;
}
public int getCodigo(){
return codigo;
}
}
package tfc;
import java.io.PrintWriter;
public interface ElementoCodigoEjecucion extends ElementoCodigoInterfaz {
public
gestor);
public
public
public
}
package tfc;
void convertirEnEjecutable(PrintWriter salida, GestorIdentificadores
String getNombreEjecucion();
void setCodigo(GestorIdentificadores gestor);
int getCodigo();
import java.io.PrintWriter;
public interface ElementoCodigoInterfaz {
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores);
}
package tfc;
public class Entero extends Ordinal {
static Entero yo = null;
public static Entero getInstance() {
if (yo == null) {
yo = new Entero();
}
return yo;
}
private Entero(){
this.setNombre(new Nombre("integer"));
this.representacion = "Números enteros (integer)";
}
public Valor getValorAleatorio(){
return new ValorSimple("0",this);
}
}
package tfc;
public abstract class Enumerado extends Ordinal {
166
Sobre la información, su estructura y su-gestión
}
package tfc;
public class ErrorCompilacionException extends Exception {
public ErrorCompilacionException(String s) {
super(s);
}
}
package tfc;
public class ErroresCompTipos {
public ErroresCompTipos() {
}
}
package tfc;
import java.util.Vector;
public class ErroresReferencias {
protected Vector referencias = new Vector();
public ErroresReferencias() {
}
public void addError(Referencia ref){
referencias.add(ref);
}
public boolean hayErrores(){
return (referencias.size() != 0);
}
public String toString(){
String resultado = "";
for(int i=0; i<referencias.size(); i++){
Referencia ref = (Referencia)referencias.get(i);
resultado += "No encontrado un "+ref.getClass().getName() +"
de nombre "+ref.getNombre()+'\n';
}
return resultado;
}
}
package tfc;
import java.io.PrintWriter;
public interface Evaluable extends ElementoCodigoEjecucion {
public DefTipo getDefTipo();
public String getRepTextual();
}
package tfc;
import java.io.PrintWriter;
public interface EvaluableEjecucionI {
public Valor evaluate(InformacionAccesible i, PrintWriter salida);
}
package tfc;
public abstract class EvaluableInstancia {
167
Sobre la información, su estructura y su-gestión
public static Evaluable getEvaluable(Nombre nombre, GestorAmbito ambito)
throws DefinicionNoEncontradaException {
Evaluable e;
try {
DefConstante d = ambito.getDefConstante(nombre,true);
e = new RefConstante(d);
} catch (DefinicionNoEncontradaException e1) {
try {
DefVariable v = ambito.getDefVariable(nombre,true);
e = new RefVariable(v);
} catch (DefinicionNoEncontradaException e2){
try {
DefSubprograma s =
ambito.getDefSubprograma(nombre,new ParametrosActuales(),true);
e = new RefFuncion((DefFuncion)s);
} catch(ClassCastException e4){
throw new
DefinicionNoEncontradaException(nombre,"evaluable");
}
}
}
return e;
}
}
package tfc;
public class EvaluableTreeNode {
public EvaluableTreeNode() {
}
}
package tfc;
public interface EvaluableTreeNodeI extends TreeNodeI {
public void setCabecera(String cabecera);
}
package tfc;
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;
public class Frame2 extends JFrame {
DefPrograma defPrograma;
JPanel contentPane;
BorderLayout borderLayout1 = new BorderLayout();
JToolBar jToolBar1 = new JToolBar();
JSplitPane jSplitPane1 = new JSplitPane();
JButton jButton3 = new JButton();
Pascal pascal; // Analizador Sintáctico.
JScrollPane jScrollPane4 = new JScrollPane();
JSplitPane jSplitPane2 = new JSplitPane();
JScrollPane jScrollPane3 = new JScrollPane();
GestorCompilacion gestorCompilacion = new GestorCompilacion();
boolean compilado = false;
boolean resuelto = false;
JSplitPane jSplitPane3 = new JSplitPane();
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JButton jButton5 = new JButton();
private JTextArea jTextArea3 = new JTextArea();
private JTextArea jTextArea1 = new JTextArea();
private JTextArea jTextArea2 = new JTextArea();
private JTree jTree1 = new JTree();
168
Sobre la información, su estructura y su-gestión
//Construct the frame
public Frame2() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
//Component initialization
private void jbInit() throws Exception {
//setIconImage(Toolkit.getDefaultToolkit().createImage(Frame2.class.getResou
rce("[Your Icon]")));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(556, 486));
this.setTitle("Frame Title");
jButton3.setText("Compilación");
jButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton3_actionPerformed(e);
}
});
jSplitPane2.setOrientation(JSplitPane.VERTICAL_SPLIT);
jSplitPane3.setOrientation(JSplitPane.VERTICAL_SPLIT);
jButton5.setText("Ejecucion");
jButton5.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton5_actionPerformed(e);
}
});
jTextArea1.addInputMethodListener(new
java.awt.event.InputMethodListener() {
public void inputMethodTextChanged(InputMethodEvent e) {
jTextArea1_inputMethodTextChanged(e);
}
public void caretPositionChanged(InputMethodEvent e) {
}
});
jTextArea1.setToolTipText("");
jTextArea1.setText("program hola; \nconst\nkk=5;\nvar\n
i:integer;\n\n\tprocedure hola(c:integer);\n" +
"
begin\n
c:=456;\n\tend;\nbegin \n i:=5; hola(i);\nend.");
jTree1.setModel(null);
contentPane.add(jToolBar1, BorderLayout.SOUTH);
jToolBar1.add(jButton3, null);
jToolBar1.add(jButton5, null);
contentPane.add(jSplitPane1, BorderLayout.CENTER);
jSplitPane1.add(jSplitPane2, JSplitPane.LEFT);
jSplitPane2.add(jScrollPane4, JSplitPane.RIGHT);
jScrollPane4.getViewport().add(jTextArea2, null);
jSplitPane2.add(jScrollPane3, JSplitPane.LEFT);
jScrollPane3.getViewport().add(jTree1, null);
jSplitPane1.add(jSplitPane3, JSplitPane.RIGHT);
jSplitPane3.add(jScrollPane1, JSplitPane.BOTTOM);
jScrollPane1.getViewport().add(jTextArea3, null);
jSplitPane3.add(jScrollPane2, JSplitPane.TOP);
jScrollPane2.getViewport().add(jTextArea1, null);
jSplitPane1.setDividerLocation(250);
jSplitPane2.setDividerLocation(150);
jSplitPane3.setDividerLocation(200);
}
//Overridden so we can exit when window is closed
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
169
Sobre la información, su estructura y su-gestión
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
void jButton3_actionPerformed(ActionEvent e) {
try {
gestorCompilacion.analizaSintácticamente(jTextArea1.getText());
defPrograma = gestorCompilacion.getDefPrograma();
jTree1.setModel( new DefProgramaTreeModel(defPrograma));
ErroresReferencias errores =
gestorCompilacion.resuelveReferencias();
if (errores.hayErrores()) {
jTextArea2.setText( errores.toString() );
jButton5.setEnabled(false);
} else {
jTextArea2.setText("Programa correcto");
jButton5.setEnabled(true);
}
} catch (ParseException p){
jTextArea2.append(p.getMessage());
jButton5.setEnabled(false);
} catch (TokenMgrError p){
jTextArea2.append(p.getMessage());
jButton5.setEnabled(false);
}
}
void jTextArea1_inputMethodTextChanged(InputMethodEvent e) {
if (compilado) {
jTextArea2.setText("");
jTree1.setEnabled(false);
jButton5.setEnabled(false);
}
}
void jTextArea1_caretPositionChanged(InputMethodEvent e) {
}
void jButton5_actionPerformed(ActionEvent e) {
// Aqui hay que generar el fichero y compilarle.
StringWriter stringWriter = new StringWriter();
PrintWriter flujoSalida = new PrintWriter(stringWriter);
defPrograma.convertirEnEjecutable(flujoSalida, new
GestorIdentificadores());
CompiladorJava comp = new CompiladorJava("javac");
try {
Class clase = comp.compila( new
String(stringWriter.getBuffer()), "DefProgramaEjecucion0" );
Object programa = clase.newInstance();
StringWriter stringWriter2 = new StringWriter();
PrintWriter flujoSalida2 = new PrintWriter(stringWriter2);
((DefProgramaEjecucion)programa).ejecuta(null, flujoSalida2);
jTextArea3.setText(stringWriter2.getBuffer().toString());
} catch (ErrorCompilacionException e2){
e2.printStackTrace();
} catch (IllegalAccessException e2){
e2.printStackTrace();
} catch (InstantiationException e2){
170
Sobre la información, su estructura y su-gestión
e2.printStackTrace();
}
}
}
package tfc;
import java.util.Vector;
import java.util.Iterator;
import javax.swing.AbstractListModel;
public class GestorAmbito {
GestorAmbito superior;
SecuenciaDefTipo tipos;
SecuenciaDefVariable variables;
SecuenciaDefConstante constantes;
SecuenciaDefSubprograma subprogramas;
SecuenciaDefParametro parametros;
public GestorAmbito(SecuenciaDefTipo tipos, SecuenciaDefVariable variables,
SecuenciaDefConstante constantes,
SecuenciaDefSubprograma subprogramas,
SecuenciaDefParametro parametros, GestorAmbito superior){
this.setDefiniciones(tipos,variables,constantes,subprogramas,parametros,supe
rior);
}
protected GestorAmbito(){
}
protected void setDefiniciones(SecuenciaDefTipo tipos, SecuenciaDefVariable
variables,
SecuenciaDefConstante
constantes, SecuenciaDefSubprograma subprogramas, SecuenciaDefParametro parametros,
GestorAmbito superior){
this.tipos = tipos;
this.variables = variables;
this.constantes = constantes;
this.subprogramas = subprogramas;
this.superior = superior;
this.parametros = parametros;
}
public DefTipo getDefTipo(Nombre nombre, boolean recursivo) throws
DefinicionNoEncontradaException {
DefTipo t = (DefTipo)this.getDefinicion(nombre.toString(),tipos);
if (t == null) {
if (!recursivo || superior == null){
throw new
DefinicionNoEncontradaException(nombre,"tipo");
} else {
return superior.getDefTipo(nombre,true);
}
} else {
return t;
}
}
public DefVariable getDefVariable(Nombre nombre, boolean recursivo) throws
DefinicionNoEncontradaException {
DefVariable t =
(DefVariable)this.getDefinicion(nombre.toString(),variables);
if (t == null) {
171
Sobre la información, su estructura y su-gestión
t =
(DefVariable)this.getDefinicion(nombre.toString(),parametros);
if (t == null) {
if (!recursivo || superior == null){
throw new
DefinicionNoEncontradaException(nombre,"variable o parámetro");
} else {
return superior.getDefVariable(nombre,true);
}
} else {
return t;
}
} else {
return t;
}
}
public DefConstante getDefConstante(Nombre nombre, boolean recursivo) throws
DefinicionNoEncontradaException {
DefConstante t =
(DefConstante)this.getDefinicion(nombre.toString(),constantes);
if (t == null) {
if (!recursivo || superior == null){
throw new
DefinicionNoEncontradaException(nombre,"constante");
} else {
return superior.getDefConstante(nombre,true);
}
} else {
return t;
}
}
public DefSubprograma getDefSubprograma(Nombre nombre, ParametrosActuales p,
boolean recursivo) throws DefinicionNoEncontradaException {
DefSubprograma t;
posicion = 0; // Esto es una ñapa y una "patá" a la programacion
estructurada y por tanto a la
// programacion orientada a objetos, pero es para hacer una prueba,
hay que retocarlo.
do {
t =
(DefSubprograma)this.getDefinicion(nombre.toString(),subprogramas,false);
if (t == null) {
if (!recursivo || superior == null){
throw new
DefinicionNoEncontradaException(nombre,"subprograma");
} else {
return
superior.getDefSubprograma(nombre,p,true);
}
}
} while ( !t.compatible(p) );
return t;
}
private Definicion getDefinicion(String nombre, Secuencia definiciones) {
return getDefinicion(nombre, definiciones, true);
}
172
Sobre la información, su estructura y su-gestión
private int posicion = 0;
private Definicion getDefinicion(String nombre, Secuencia definiciones,
boolean nuevaBusqueda) {
boolean seguir = true;
Definicion t = null;
if (nuevaBusqueda) {
posicion = 0;
}
while ( posicion < definiciones.getSize() && seguir ) {
t = (Definicion)definiciones.getElementAt(posicion);
seguir = !( nombre.equalsIgnoreCase( t.getNombre().toString()
) ) ;
posicion++;
}
if (seguir){
return null;
} else {
return t;
}
}
public String
String
salida
salida
getRepTextual(){
salida = "";
+= "Subprogramas\n";
+= this.subprogramas.getRepTextual()+"\n";
salida += "Variables\n";
salida += this.variables.getRepTextual()+"\n";
salida += "Constantes\n";
salida += this.constantes.getRepTextual()+"\n";
salida += "Parametros\n";
salida += this.parametros.getRepTextual()+"\n";
salida += "Tipos\n";
salida += this.tipos.getRepTextual()+"\n";
salida += "Superior\n";
salida += (this.superior == null)?"No
tiene":this.superior.getRepTextual();
return salida.toString();
}
}
package tfc;
import java.io.StringReader;
public class GestorCompilacion {
private boolean sintacticoCorrecto;
private Pascal analisisSintactico = new Pascal(new StringReader(""));
private DefPrograma defPrograma;
public GestorCompilacion() {
sintacticoCorrecto = false;
}
public DefPrograma getDefPrograma(){
return defPrograma;
}
173
Sobre la información, su estructura y su-gestión
public void analizaSintácticamente(String fuente) throws ParseException,
TokenMgrError {
analisisSintactico.ReInit( new StringReader(fuente));
defPrograma = analisisSintactico.programa();
sintacticoCorrecto = true;
}
public boolean isSintacticoCorrecto(){
return sintacticoCorrecto;
}
public ErroresReferencias resuelveReferencias() {
if (sintacticoCorrecto) {
return defPrograma.resuelveReferencias();
} else {
return null;
}
}
}
package tfc;
public class GestorIdentificadores {
int numero;
int nAmbito;
public GestorIdentificadores() {
numero = 0;
nAmbito = 0;
}
public int getCodigo(){
numero++;
return numero-1;
}
public int setNumInformacion(){
nAmbito++;
return nAmbito;
}
public int getNumInformacion(){
return nAmbito;
}
}
package tfc;
import java.util.Vector;
import java.io.PrintWriter;
public abstract class InformacionAccesible extends Ejecucion {
protected InformacionAccesible ambitoSuperior;
protected Vector v = new Vector();
protected void setAmbitoSuperior(InformacionAccesible ambitoSuperior){
this.ambitoSuperior = ambitoSuperior;
}
public void creacion(InformacionAccesible inf, PrintWriter salida){
creacion(null,inf,salida);
}
public abstract void creacion(ParametrosActualesValoresEItems p,
InformacionAccesible inf, PrintWriter salida);
public abstract ItemDeInformacion getItem(int codigo);
public void liberacion(PrintWriter salida) {
for (int i=0; i<v.size(); i++){
174
Sobre la información, su estructura y su-gestión
((ItemDeInformacion)v.get(i)).liberacion(salida);
}
}
static int codigo;
public static void setCodigo(GestorIdentificadores gestor){
codigo = gestor.getCodigo();
}
public static String getNombreEjecucion(){
return "InformacionAccesible"+codigo;
}
public static int convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor,
SecuenciaDefConstante constantes, SecuenciaDefParametro
parametros, SecuenciaDefVariable variables){
setCodigo(gestor);
salida.println("class "+getNombreEjecucion()+" extends
InformacionAccesible {");
salida.println(" public void
creacion(ParametrosActualesValoresEItems p, InformacionAccesible ambitoSuperior,
java.io.PrintWriter salida){ ");
salida.println("ItemDeInformacion item;");
for(int i=0; i<constantes.getSize();i++){
salida.println("item = new
"+constantes.getDefConstanteAt(i).getNombreEjecucion()+"();");
salida.println("item.creacion(salida);");
salida.println("v.add(item);");
}
salida.println("ItemDeInformacionParam item2;");
for(int i=0; i<parametros.getSize();i++){
salida.println("item2 = new
"+parametros.getDefParametroAt(i).getNombreEjecucion()+"();");
salida.println("item2.creacion(p,"+i+",salida);");
salida.println("v.add(item2);");
}
for(int i=0; i<variables.getSize();i++){
salida.println("item = new
"+variables.getDefVariableAt(i).getNombreEjecucion()+"();");
salida.println("item.creacion(salida);");
salida.println("v.add(item);");
}
salida.println(" }");
salida.println(" public ItemDeInformacion getItem(int codigo){");
salida.println("
switch(codigo){ ");
int j=0;
for(int i=0; i<constantes.getSize();i++){
salida.println("case
"+constantes.getDefConstanteAt(i).getCodigo()+": return
(ItemDeInformacion)v.get("+j+");");
j++;
}
for(int i=0; i<parametros.getSize();i++){
salida.println("case
"+parametros.getDefParametroAt(i).getCodigo()+": return
(ItemDeInformacion)v.get("+j+");");
j++;
}
for(int i=0; i<variables.getSize();i++){
salida.println("case
"+variables.getDefVariableAt(i).getCodigo()+": return
(ItemDeInformacion)v.get("+j+");");
j++;
}
175
Sobre la información, su estructura y su-gestión
salida.println(" default: return ambitoSuperior.getItem(codigo);");
salida.println("
}");
salida.println(" }");
salida.println("}");
return codigo;
}
}
package tfc;
import java.io.PrintWriter;
public class InformacionAccesiblePredefinida extends InformacionAccesible {
public void creacion(ParametrosActualesValoresEItems p, InformacionAccesible
ambitoSuperior, PrintWriter salida){
ItemDeInformacion maxint = new ItemDeInformacion();
maxint.setValor(Entero.getInstance().getValor("1000000000"),salida);
v.add(maxint);
}
public ItemDeInformacion getItem(int codigo){
return (ItemDeInformacion)v.get(0);
}
}
package tfc;
public abstract class InformacionAccesibleSubprograma extends InformacionAccesible
{
public InformacionAccesibleSubprograma() {
}
public void creacion(InformacionAccesible ambitoSuperior){
throw new Error("Que cojones haces ejecutando este método");
}
public abstract void creacion(ParametrosActualesValoresEItems p,
InformacionAccesible ambitoSuperior);
}
package tfc;
import javax.swing.DefaultListModel;
public class InstruccionCompuesta extends ElementoCodigo implements Sentencia {
protected SecuenciaEjecutable instrucciones;
public InstruccionCompuesta(){
this.instrucciones = new SecuenciaEjecutable();
}
public SecuenciaEjecutable getInstrucciones(){
return instrucciones;
}
public String getRepTextual(){
String resultado = "";
resultado += "BEGIN"+'\n';
for(int i=0; i < instrucciones.getSize(); i++){
resultado +=
(instrucciones.getEjecutableAt(i)).getRepTextual() + ";\n";
}
resultado += "END";
return resultado;
}
176
Sobre la información, su estructura y su-gestión
public void resuelveReferencias(GestorAmbito ambitoSuperior,
ErroresReferencias errores){
instrucciones.resuelveReferencias(ambitoSuperior,errores);
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
for(int i=0; i<this.instrucciones.getSize();i++){
((Sentencia)this.instrucciones.get(i)).convertirEnEjecutable(salida,gestor);
}
this.setCodigo(gestor);
salida.println("//----------------------------------------------------------------------------------");
salida.println("class "+this.getNombreEjecucion()+" extends
SecuenciaEjecutableEjecucion { ");
public "+this.getNombreEjecucion()+"(){
salida.println("
");
salida.println("
this.setNElementos("+this.instrucciones.getSize()+");
");
}
salida.println("
");
salida.println("
public void ejecutaYFin(int
i,InformacionAccesible inf, java.io.PrintWriter salida) {
");
salida.println("
EjecutableEjecucionI e;
");
salida.println("
switch(i){
");
for(int i=0; i<this.instrucciones.getSize();i++){
case "+i+": {
salida.println("
");
salida.println("
e = new
"+((Sentencia)this.instrucciones.get(i)).getNombreEjecucion()+"();
");
salida.println("
e.ejecuta(inf,salida);
");
salida.println("
e.fin();
");
} break;
salida.println("
");
}
salida.println("
salida.println("
}");
salida.println(" }");
}");
}
}
package tfc;
import javax.swing.tree.*;
import javax.swing.DefaultListModel;
public class InstruccionCompuestaTreeNode extends TreeNode {
InstruccionCompuesta instruccionCompuesta;
DefaultListModel instrucciones = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return instruccionCompuesta;
177
Sobre la información, su estructura y su-gestión
}
public InstruccionCompuestaTreeNode(InstruccionCompuesta
instruccionCompuesta) {
this.setModeloRecursivo(instruccionCompuesta);
}
public InstruccionCompuestaTreeNode() {
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return instrucciones.get(index);
}
public int getIndexOfChild(Object child) {
return instrucciones.indexOf(child);
}
public int getChildCount() {
return instrucciones.getSize();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
instruccionCompuesta = (InstruccionCompuesta) obj;
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
SecuenciaEjecutable aux = instruccionCompuesta.getInstrucciones();
for(int i=0; i<aux.getSize();i++){
instrucciones.addElement( TreeNode.getTreeNode(aux.get(i)) );
}
}
public String toString(){
return cabecera;
}
}
package tfc;
import java.io.PrintWriter;
public abstract class InstruccionEjecucion extends Ejecucion implements
EjecutableEjecucionI {
public abstract void ejecuta(InformacionAccesible i, PrintWriter salida);
public void fin(){
}
}
package tfc;
import javax.swing.tree.TreePath;
public abstract class InstruccionTreeNode extends TreeNode implements
EjecutableTreeNodeI {
}
package tfc;
public class InstruccionVacia extends ElementoCodigo implements Sentencia {
public InstruccionVacia() {
178
Sobre la información, su estructura y su-gestión
}
public String getRepTextual(){
return "";
}
public void resuelveReferencias(GestorAmbito parm1, ErroresReferencias
parm2) {
// No hay referencias en este elemento.
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
salida.println("class "+this.getNombreEjecucion()+" implements
EjecutableEjecucionI {");
salida.println("public void ejecuta(InformacionAccesible i,
java.io.PrintWriter salida){}");
salida.println("public void fin(){}");
salida.println("}");
}
}
package tfc;
import javax.swing.tree.TreePath;
public class InstruccionVaciaTreeNode extends InstruccionTreeNode {
InstruccionVacia instruccion;
public InstruccionVaciaTreeNode() {
}
public boolean isLeaf() {
return true;
}
public Object getChild(int parm1) {
return null;
}
public void setModeloRecursivo(Object obj) {
this.instruccion = (InstruccionVacia) obj;
this.setCabecera("Instruccion Vacia");
}
public int getIndexOfChild(Object parm1) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath parm1, Object parm2) {
}
public ElementoCodigoInterfaz getElementoCodigo() {
return instruccion;
}
public void setModelo(Object obj) {
this.instruccion = (InstruccionVacia) obj;
}
}
package tfc;
import java.io.PrintWriter;
public class ItemDeInformacion extends Ejecucion implements EvaluableEjecucionI {
Valor valor;
public void creacion(PrintWriter salida){
salida.println("Reservada memoria variable");
valor = Entero.getInstance().getValorAleatorio();
179
Sobre la información, su estructura y su-gestión
}
public Valor evaluate(InformacionAccesible i, PrintWriter salida){
salida.println("El valor es "+valor.getRepTextual());
return valor;
}
public void setValor(Valor valor, PrintWriter salida){
if (this.valor == null){
salida.println("No tenía valor y ahora tiene el valor
"+valor.getRepTextual());
} else {
salida.println("Tenía el valor "+this.valor.getRepTextual()+"
y ahora tiene el valor "+valor.getRepTextual());
}
this.valor = valor;
}
public void liberacion(PrintWriter salida){
salida.println("Liberada memoria variable");
}
}
package tfc;
public class ItemDeInformacionParam extends ItemDeInformacion {
public ItemDeInformacionParam() {
}
public void creacion(ParametrosActualesValoresEItems p,int
i,java.io.PrintWriter salida){
Valor v = p.getValor(i);
salida.println("Reservada memoria variable con el
valor"+v.getRepTextual());
valor = v;
}
}
package tfc;
public class Nombre implements ElementoCodigoInterfaz {
String nombre;
public Nombre(String nombre) {
this.nombre = nombre;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores) {
}
public String toString(){
return nombre;
}
public boolean equalsIgnoreCase( Nombre n){
return nombre.equalsIgnoreCase(n.toString());
}
}
package tfc;
import javax.swing.tree.*;
// La clase String debería de ser la clase nombre, ya lo cambiaremos.
public class NombreTreeNode extends TreeNode {
Nombre nombre;
public ElementoCodigoInterfaz getElementoCodigo(){
180
Sobre la información, su estructura y su-gestión
return nombre;
}
public NombreTreeNode() {
setCabecera("Nombre -> ");
}
public NombreTreeNode(Nombre nombre) {
this.setModelo(nombre);
setCabecera("Nombre -> ");
}
public boolean isLeaf() {
return true;
}
public Object getChild(int index) {
return null;
}
public int getIndexOfChild(Object child) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
this.nombre = (Nombre) obj;
}
public void setModeloRecursivo(Object obj){
this.nombre = (Nombre) obj;
}
}
package tfc;
public class NoRefVariableException extends Exception {
public NoRefVariableException() {
}
}
package tfc;
import java.util.Vector;
import java.util.Iterator;
public abstract class Ordinal extends TipoSimple {
Vector valores = new Vector(100,10);
public int getNumeroOrden(ValorSimple v){
return valores.indexOf(v);
}
}
package tfc;
import java.util.*;
public class ParametrosActuales extends ElementoCodigo {
Vector refVariables = new Vector();
Vector generadoresValor = new Vector();
public ParametrosActuales() {
}
public void addRefVariable(RefVariable rV){
refVariables.add(rV);
181
Sobre la información, su estructura y su-gestión
generadoresValor.add(null);
}
public void addEvaluable(Evaluable gV){
refVariables.add(null);
generadoresValor.add(gV);
}
public String getRepTextual(){
String resultado = "";
if ( refVariables.size() != 0){
for(int i=0; i < refVariables.size() - 1; i++){
if (refVariables.get(i) != null){
resultado +=
((ElementoCodigo)refVariables.get(i)).getRepTextual()+",";
} else {
resultado +=
((ElementoCodigo)generadoresValor.get(i)).getRepTextual()+",";
}
}
if (refVariables.lastElement() != null){
resultado +=
((ElementoCodigo)refVariables.lastElement()).getRepTextual();
} else {
resultado +=
((ElementoCodigo)generadoresValor.lastElement()).getRepTextual();
}
}
return resultado;
}
public boolean compatible(SecuenciaDefParametro paramFormales){
if (paramFormales.size() != refVariables.size()){
return false;
} else {
boolean seguir = true;
int i=0;
while( i < refVariables.size() && seguir){
seguir = !(
(!((DefParametro)paramFormales.get(i)).pasoPorValor()) && (refVariables.get(i) ==
null ));
Evaluable parametroActual =
(Evaluable)((refVariables.get(i) == null)? generadoresValor.get(i) :
refVariables.get(i));
seguir = seguir && (
((DefParametro)paramFormales.get(i)).getRefTipo().getDefTipo() ==
parametroActual.getDefTipo() );
i++;
}
return seguir;
}
}
RefVariable getRefVariable(int pos) throws NoRefVariableException {
if (refVariables.get(pos) != null ){
return (RefVariable)refVariables.get(pos);
} else {
throw new NoRefVariableException();
}
}
Evaluable getGeneradorValor(int pos) {
if (refVariables.get(pos) != null ){
return (Evaluable)refVariables.get(pos);
} else {
return (Evaluable)generadoresValor.get(pos);
}
}
182
Sobre la información, su estructura y su-gestión
int getSize(){
return refVariables.size();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
for(int i=0; i<refVariables.size();i++){
RefVariable refVariable = (RefVariable)refVariables.get(i);
if (refVariable != null) {
refVariable.resuelveReferencias(ambito,errores); }
}
for(int i=0; i<generadoresValor.size();i++){
Evaluable evaluable = (Evaluable)generadoresValor.get(i);
if (evaluable != null) {
evaluable.resuelveReferencias(ambito,errores); }
}
}
}
package tfc;
// De momento esta clase va a albergar simplemete valores para aquellos parámetros
que sean por valor
// e ItemDeInformacion a aquellos parámetros por referencia.
public abstract class ParametrosActualesEjecucion extends Ejecucion {
public ParametrosActualesEjecucion() {
}
public abstract ParametrosActualesValoresEItems
getValoresEItems(InformacionAccesible i);
public void fin(){
}
}
package tfc;
import javax.swing.DefaultListModel;
import javax.swing.tree.TreePath;
public class ParametrosActualesTreeNode extends TreeNode {
ParametrosActuales parametrosActuales;
DefaultListModel parametros = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return parametrosActuales;
}
public ParametrosActualesTreeNode() {
setCabecera("Parametros actuales -> ");
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
for(int i=0; i<parametrosActuales.getSize();i++){
parametros.addElement(
TreeNode.getTreeNode(parametrosActuales.getGeneradorValor(i)) );
}
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return parametros.get(index);
183
Sobre la información, su estructura y su-gestión
}
public int getIndexOfChild(Object child) {
return parametros.indexOf(child);
}
public int getChildCount() {
return parametros.getSize();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
this.parametrosActuales = (ParametrosActuales)obj;
}
}
package tfc;
import java.util.Vector;
public class ParametrosActualesValoresEItems extends Ejecucion {
Vector v = new Vector();
public ParametrosActualesValoresEItems() {
}
public void addValor(Valor valor){
v.add(valor);
}
public void addItemDeInformacion(ItemDeInformacion item){
v.add(item);
}
public Valor getValor(int i){
try {
return (Valor)v.get(i);
} catch(ClassCastException e){
return null;
}
}
public ItemDeInformacion getItemDeInformacion(int i){
try {
return (ItemDeInformacion)v.get(i);
} catch(ClassCastException e){
return null;
}
}
}
package tfc;
import javax.swing.DefaultListModel;
public class Predefinidos extends GestorAmbito {
public Predefinidos() {
setDefiniciones(tipos(), variables(), constantes(), subprogramas(),
new SecuenciaDefParametro(), null);
}
private SecuenciaDefTipo tipos(){
SecuenciaDefTipo tipos;
tipos = new SecuenciaDefTipo();
184
Sobre la información, su estructura y su-gestión
tipos.addElement(
tipos.addElement(
tipos.addElement(
tipos.addElement(
return tipos;
Entero.getInstance() );
Real.getInstance() );
Booleano.getInstance() );
Caracter.getInstance() );
}
private SecuenciaDefVariable variables(){
return new SecuenciaDefVariable();
}
private SecuenciaDefConstante constantes(){
SecuenciaDefConstante constantes;
constantes = new SecuenciaDefConstante();
DefConstante c;
c = new DefConstante( new Nombre("true") ,
Booleano.getInstance().getValor("true") );
constantes.addElement( c );
c = new DefConstante( new Nombre("false") ,
Booleano.getInstance().getValor("false") );
constantes.addElement( c );
return constantes;
}
private SecuenciaDefSubprograma subprogramas(){
SecuenciaDefSubprograma subprogramas = new SecuenciaDefSubprograma();
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
subprogramas.addElement(
return subprogramas;
new
new
new
new
new
new
new
new
new
new
DefFuncionOperadorSuma() );
DefFuncionOperadorResta() );
DefFuncionOperadorNegacion() );
DefFuncionOperadorMultiplicacion() );
DefFuncionOperadorDivision() );
DefFuncionOperadorMod() );
DefFuncionOperadorAnd() );
DefFuncionOperadorOr() );
DefFuncionOperadorNot() );
DefFuncionOperadorIgualdad() );
}
}
package tfc;
public class Real extends TipoSimple {
static Real yo = null;
public static Real getInstance() {
if (yo == null) {
yo = new Real();
}
return yo;
}
private Real(){
this.setNombre(new Nombre("real"));
this.representacion = "Números reales (double o float)";
}
}
package tfc;
public class RefConstante extends Referencia implements Evaluable {
DefConstante c;
185
Sobre la información, su estructura y su-gestión
public RefConstante(DefConstante c) {
this.nombre = c.getNombre(); // Revisar este constructor.
this.c = c;
}
public RefConstante(Nombre nombre, GestorAmbito ambito) throws
DefinicionNoEncontradaException {
this.nombre = nombre;
this.c = ambito.getDefConstante(nombre, true);
}
public Definicion getDefinicion() {
return c;
}
public DefConstante getDefConstante(){
return c;
}
public DefTipo getDefTipo(){
return c.getValor().getDefTipo();
}
public Valor getValor(){
return c.getValor();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
try {
ambito.getDefConstante(this.getNombre(),true);
} catch (DefinicionNoEncontradaException e){
errores.addError(this);
}
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.codigo = gestor.getCodigo();
salida.println("//------------------------------------------------------------------------");
salida.println("class "+this.getNombreEjecucion()+" extends
RefConstanteEjecucion {");
salida.println(" public ItemDeInformacion
getItemDeInformacion(InformacionAccesible i){");
salida.println("
return
i.getItem("+this.getDefConstante().getCodigo()+");");
salida.println(" }");
salida.println("}");
}
}
package tfc;
import java.io.PrintWriter;
public abstract class RefConstanteEjecucion implements EvaluableEjecucionI {
public RefConstanteEjecucion() {
}
public Valor evaluate(InformacionAccesible i, PrintWriter salida) {
salida.println("Inicio de evaluación de la Constante");
Valor valor = this.getItemDeInformacion(i).evaluate(i,salida);
salida.println("Fin de la evaluacion de la Constante");
return valor;
}
186
Sobre la información, su estructura y su-gestión
public abstract ItemDeInformacion getItemDeInformacion(InformacionAccesible
i);
}
package tfc;
import javax.swing.tree.*;
public class RefConstanteTreeNode extends ReferenciaTreeNode implements
EvaluableTreeNodeI {
RefConstante refConstante;
public ElementoCodigoInterfaz getElementoCodigo(){
return refConstante;
}
public RefConstanteTreeNode(RefConstante refConstante) {
this.setModeloRecursivo(refConstante);
}
public RefConstanteTreeNode() {
setCabecera("Referencia a Constante -> ");
}
public void setModelo(Object obj) {
this.refConstante = (RefConstante) obj;
}
public void setModeloRecursivo(Object obj){
this.refConstante = (RefConstante) obj;
}
public String toString(){
return cabecera + refConstante.getRepTextual() +
this.textoEnlazado();
}
public Referencia getReferencia(){
return refConstante;
}
}
package tfc;
public abstract class Referencia extends ElementoCodigo {
protected Nombre nombre;
boolean enlazado = false; // Indica si se ha resuelto la referencia o no.
public String getRepTextual(){
return nombre.toString();
}
public Nombre getNombre(){
return nombre;
}
public Object getChild(int i){
return null;
}
public abstract Definicion getDefinicion();
public boolean isEnlazado(){
return enlazado;
}
}
package tfc;
import javax.swing.tree.TreePath;
public abstract class ReferenciaTreeNode extends TreeNode {
public ReferenciaTreeNode() {
187
Sobre la información, su estructura y su-gestión
}
public boolean isLeaf() {
return true;
}
public int getChildCount(){
return 0;
}
public Object getChild(int i){
return null;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public int getIndexOfChild(Object o){
return -1;
}
public String textoEnlazado(){
String salida = "";
if (this.getReferencia().isEnlazado()) {
salida = " E";
} else {
salida = " NE";
}
return salida;
}
public abstract Referencia getReferencia();
}
package tfc;
public class RefFuncion extends RefSubprograma implements Evaluable {
protected DefFuncion defFuncion = null;
public RefFuncion(Nombre nombre) {
super(nombre);
}
public RefFuncion(DefFuncion defFuncion){
super(defFuncion.getNombre());
this.defFuncion = defFuncion;
}
public DefSubprograma getDefSubprograma() {
return defFuncion;
}
public Definicion getDefinicion() {
return defFuncion;
}
public DefFuncion getDefFuncion(){
return defFuncion;
}
public DefTipo getDefTipo(){
return defFuncion.getDefTipo();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
if (this.getDefinicion() == null){
try {
paramActuales.resuelveReferencias(ambito,errores);
188
Sobre la información, su estructura y su-gestión
defFuncion =
(DefFuncion)ambito.getDefSubprograma(this.getNombre(),paramActuales,true);
enlazado = true;
} catch (DefinicionNoEncontradaException e){
errores.addError(this);
}
}
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
// POR HACER
}
}
package tfc;
public abstract class RefFuncionEjecucion extends RefSubprogramaEjecucion
implements EvaluableEjecucionI {
Valor salidaFuncion;
public RefFuncionEjecucion() {
}
public Valor getValor(){
return salidaFuncion;
}
}
package tfc;
public class RefFuncionOperador extends RefFuncion {
protected DefFuncionOperador defFuncionOperador;
public RefFuncionOperador(Nombre nombre) {
super(nombre);
}
public DefSubprograma getDefSubprograma() {
return defFuncionOperador;
}
public Definicion getDefinicion() {
return defFuncionOperador;
}
public DefFuncion getDefFuncion(){
return defFuncionOperador;
}
public DefFuncionOperador getDefFuncionOperador(){
return defFuncionOperador;
}
public String getRepTextual(){
if (paramActuales.getSize() == 1){
return "( "+this.getNombre()+" " +
paramActuales.getGeneradorValor(0).toString()+" )";
} else {
return "( "+paramActuales.getGeneradorValor(0).toString() +"
"+getNombre()+" "+
paramActuales.getGeneradorValor(1).toString()+"
)";
}
}
}
package tfc;
189
Sobre la información, su estructura y su-gestión
public class RefFuncionOperadorTreeNode extends RefFuncionTreeNode {
public RefFuncionOperadorTreeNode() {
setCabecera("Funcion con operador -> ");
}
}
package tfc;
public class RefFuncionTreeNode extends RefSubprogramaTreeNode implements
EvaluableTreeNodeI {
RefFuncion refFuncion;
public void setModelo(Object obj) {
this.refFuncion = (RefFuncion)obj;
}
public RefSubprograma getRefSubprograma() {
return refFuncion;
}
}
package tfc;
public class RefProcedimiento extends RefSubprograma implements Sentencia {
protected DefProcedimiento defProcedimiento;
public RefProcedimiento(Nombre nombre) {
super(nombre);
}
public DefProcedimiento getDefProcedimiento(){
return defProcedimiento;
}
public DefSubprograma getDefSubprograma() {
return defProcedimiento;
}
public Definicion getDefinicion() {
return defProcedimiento;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
if (this.getDefinicion() == null){
try {
paramActuales.resuelveReferencias(ambito,errores);
defProcedimiento =
(DefProcedimiento)ambito.getDefSubprograma(this.getNombre(),paramActuales,true);
enlazado = true;
} catch (DefinicionNoEncontradaException e){
errores.addError(this);
}
}
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
salida.println("class "+this.getNombreEjecucion()+" extends
RefSubprogramaEjecucion {");
salida.println(" public "+this.getNombreEjecucion()+"(){}");
salida.println(" public void
ejecutaSubprograma(ParametrosActualesValoresEItems p,InformacionAccesible inf,
java.io.PrintWriter salida){");
salida.println("
DefProcedimientoEjecucion e = new
"+this.defProcedimiento.getNombreEjecucion()+"();");
salida.println("
e.ejecuta(p,inf,salida);");
190
Sobre la información, su estructura y su-gestión
salida.println(" }");
salida.println(" public ParametrosActualesValoresEItems
evaluaParametros(InformacionAccesible inf,java.io.PrintWriter salida){");
salida.println("
ParametrosActualesValoresEItems p = new
ParametrosActualesValoresEItems();");
salida.println("
Valor v; EvaluableEjecucionI e;");
for (int i=0; i< defProcedimiento.getParametros().size();i++){
salida.println("
e = new
"+this.getParamActuales().getGeneradorValor(i).getNombreEjecucion()+"();");
salida.println("
v = e.evaluate(inf,salida);");
salida.println("
e.fin();");
salida.println("
p.addValor(v);
");
}
salida.println("
return p;");
salida.println(" }");
salida.println("}");
}
}
package tfc;
public abstract class RefProcedimientoEjecucion extends RefSubprogramaEjecucion
implements EjecutableEjecucionI {
public RefProcedimientoEjecucion() {
}
}
package tfc;
public class RefProcedimientoTreeNode extends RefSubprogramaTreeNode {
RefProcedimiento refProcedimiento;
public void setModelo(Object obj) {
this.refProcedimiento = (RefProcedimiento)obj;
}
public RefSubprograma getRefSubprograma() {
return refProcedimiento;
}
}
package tfc;
import javax.swing.DefaultListModel;
public abstract class RefSubprograma extends Referencia {
protected ParametrosActuales paramActuales;
protected RefSubprograma(Nombre nombre){
this.nombre = nombre;
this.paramActuales = new ParametrosActuales();
}
public abstract Definicion getDefinicion();
public abstract DefSubprograma getDefSubprograma();
public String getRepTextual(){
String resultado = "";
resultado += this.getNombre();
if (paramActuales.getSize() > 0){
resultado += "(";
resultado += paramActuales.getRepTextual();
resultado += ")";
}
return resultado;
}
191
Sobre la información, su estructura y su-gestión
public ParametrosActuales getParamActuales(){
return paramActuales;
}
}
package tfc;
public abstract class RefSubprogramaEjecucion implements EjecutableEjecucionI {
public RefSubprogramaEjecucion() {
}
public void ejecuta(InformacionAccesible inf, java.io.PrintWriter salida){
System.out.println("Inicio ejecución llamada. Evaluacion
parámetros");
ParametrosActualesValoresEItems p =
this.evaluaParametros(inf,salida);
this.ejecutaSubprograma(p,inf,salida);
System.out.println("Fin de la llamada");
}
public void fin(){
}
public abstract void ejecutaSubprograma(ParametrosActualesValoresEItems
p,InformacionAccesible inf, java.io.PrintWriter salida);
public abstract ParametrosActualesValoresEItems
evaluaParametros(InformacionAccesible inf, java.io.PrintWriter salida);
}
package tfc;
public abstract class RefSubprogramaTreeNode extends ReferenciaTreeNode {
NombreTreeNode nombre;
ParametrosActualesTreeNode paramActuales;
public RefSubprogramaTreeNode() {
}
public ElementoCodigoInterfaz getElementoCodigo(){
return getRefSubprograma();
}
public Referencia getReferencia() {
return getRefSubprograma();
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
switch(index){
case 0: return nombre;
default: return paramActuales;
}
}
public int getIndexOfChild(Object child) {
if (child == nombre) {
return 0;
} else {
return 1;
}
}
public int getChildCount() {
192
Sobre la información, su estructura y su-gestión
return 2;
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
nombre = new NombreTreeNode( getRefSubprograma().getNombre() );
paramActuales = (ParametrosActualesTreeNode) TreeNode.getTreeNode(
getRefSubprograma().getParamActuales() );
}
public abstract RefSubprograma getRefSubprograma();
}
package tfc;
public class RefTipo extends Referencia {
DefTipo t;
public RefTipo(DefTipo t) {
this.nombre = t.getNombre();
this.t = t;
}
public RefTipo(Nombre nombre, GestorAmbito ambito) throws
DefinicionNoEncontradaException {
this.nombre = nombre;
this.t = ambito.getDefTipo(nombre, true);
}
public RefTipo(Nombre nombre){
this.nombre = nombre;
}
public Definicion getDefinicion(){
return t;
}
public DefTipo getDefTipo(){
return t;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
try {
t = ambito.getDefTipo(this.getNombre(),true);
enlazado = true;
} catch (DefinicionNoEncontradaException e){
errores.addError(this);
}
}
}
package tfc;
import javax.swing.tree.*;
public class RefTipoTreeNode extends ReferenciaTreeNode {
RefTipo refTipo;
public ElementoCodigoInterfaz getElementoCodigo(){
return refTipo;
}
public RefTipoTreeNode(RefTipo refTipo) {
this.setModeloRecursivo(refTipo);
}
public RefTipoTreeNode() {
}
193
Sobre la información, su estructura y su-gestión
public boolean isLeaf() {
return true;
}
public Object getChild(int index) {
return null;
}
public int getIndexOfChild(Object child) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
refTipo = (RefTipo) obj;
}
public void setModeloRecursivo(Object obj){
refTipo = (RefTipo) obj; // Las hojas no necesitan ponerse el modelo
recursivo
}
public String toString(){
return cabecera + refTipo.getNombre() + this.textoEnlazado();
}
public Referencia getReferencia(){
return refTipo;
}
}
package tfc;
public class RefVariable extends Referencia implements Evaluable {
DefVariable v = null;
public RefVariable(Nombre nombre){
this.nombre = nombre;
}
public RefVariable(DefVariable defVariable){
v = defVariable;
this.nombre = defVariable.getNombre();
}
public Definicion getDefinicion(){
return v;
}
public DefVariable getDefVariable(){
return v;
}
public DefTipo getDefTipo(){
return v.getRefTipo().getDefTipo();
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
try {
194
Sobre la información, su estructura y su-gestión
System.out.println(ambito.getRepTextual());
v = ambito.getDefVariable(this.getNombre(),true);
enlazado = true;
} catch (DefinicionNoEncontradaException e){
errores.addError(this);
}
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.codigo = gestor.getCodigo();
salida.println("//------------------------------------------------------------------------");
salida.println("class "+this.getNombreEjecucion()+" extends
RefVariableEjecucion {");
salida.println(" public ItemDeInformacion
getItemDeInformacion(InformacionAccesible i){");
salida.println("
return
i.getItem("+this.getDefVariable().getCodigo()+");");
salida.println(" }");
salida.println("}");
}
}
package tfc;
import java.io.PrintWriter;
public abstract class RefVariableEjecucion implements EvaluableEjecucionI {
public RefVariableEjecucion() {
}
public Valor evaluate(InformacionAccesible i, PrintWriter salida) {
salida.println("Inicio de evaluación de la variable");
Valor valor = this.getItemDeInformacion(i).evaluate(i,salida);
salida.println("Fin de la evaluacion de la variable");
return valor;
}
public abstract ItemDeInformacion getItemDeInformacion(InformacionAccesible
i);
}
package tfc;
import javax.swing.tree.*;
public class RefVariableTreeNode extends ReferenciaTreeNode implements
EvaluableTreeNodeI {
RefVariable refVariable;
public ElementoCodigoInterfaz getElementoCodigo(){
return refVariable;
}
public RefVariableTreeNode(RefVariable refVariable) {
this.setModeloRecursivo(refVariable);
}
public RefVariableTreeNode() {
setCabecera("Referencia a Variable -> ");
}
public void setModelo(Object obj) {
this.refVariable = (RefVariable) obj;
}
public void setModeloRecursivo(Object obj){
195
Sobre la información, su estructura y su-gestión
this.refVariable = (RefVariable) obj;
}
public String toString(){
return cabecera + refVariable.getRepTextual() + this.textoEnlazado();
}
public Referencia getReferencia(){
return refVariable;
}
}
package tfc;
import
import
import
import
javax.swing.AbstractListModel;
java.util.Vector;
java.util.Enumeration;
javax.swing.event.*;
public abstract class Secuencia extends AbstractListModel implements
ElementoCodigoInterfaz {
protected Vector delegate = new Vector();
GestorAmbito ambitoSuperior;
public Secuencia() {
}
public void resuelveReferencias(GestorAmbito ambito ,ErroresReferencias
errores){
for(int i=0; i<this.size();i++){
ElementoCodigoInterfaz elem;
elem = (ElementoCodigoInterfaz)this.getElementAt(i);
elem.resuelveReferencias(ambito,errores);
}
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String toString() {
return delegate.toString();
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
196
Sobre la información, su estructura y su-gestión
fireIntervalRemoved(this, index, index);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Enumeration elements() {
return delegate.elements();
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaDefConstante extends Secuencia {
public SecuenciaDefConstante() {
}
public DefConstante getDefConstanteAt(int index) {
return (DefConstante)delegate.elementAt(index);
}
public void copyInto(DefConstante anArray[]) {
delegate.copyInto(anArray);
}
public boolean contains(DefConstante elem) {
return delegate.contains(elem);
}
197
Sobre la información, su estructura y su-gestión
public int indexOf(DefConstante elem) {
return delegate.indexOf(elem);
}
public int indexOf(DefConstante elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(DefConstante elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(DefConstante elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public DefConstante elementAt(int index) {
return (DefConstante)delegate.elementAt(index);
}
public DefConstante firstElement() {
return (DefConstante)delegate.firstElement();
}
public DefConstante lastElement() {
return (DefConstante)delegate.lastElement();
}
public void setElementAt(DefConstante obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void insertElementAt(DefConstante obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(DefConstante obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(DefConstante obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
}
public DefConstante get(int index) {
return (DefConstante)delegate.elementAt(index);
}
public DefConstante set(int index, DefConstante element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (DefConstante)rv;
}
198
Sobre la información, su estructura y su-gestión
public void add(int index, DefConstante element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public DefConstante remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (DefConstante)rv;
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
for(int i=0; i<this.getSize();i++){
((DefConstante)this.get(i)).convertirEnEjecutable(salida,gestor);
}
}
public String getRepTextual(){
StringBuffer salida = new StringBuffer();
for(int i=0; i<this.size(); i++){
salida.append(this.getDefConstanteAt(i).getRepTextual()+", ");
}
return salida.toString();
}
}
package tfc;
public class SecuenciaDefConstanteEjecucion extends SecuenciaEjecucion {
public SecuenciaDefConstanteEjecucion() {
}
}
package tfc;
import javax.swing.DefaultListModel;
import javax.swing.tree.TreePath;
public class SecuenciaDefConstanteTreeNode extends TreeNode {
SecuenciaDefConstante secuenciaDefConstante;
DefaultListModel secuenciaDefConstanteModelo = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return secuenciaDefConstante;
}
public SecuenciaDefConstanteTreeNode(SecuenciaDefConstante
secuenciaDefConstante) {
this.setModeloRecursivo(secuenciaDefConstante);
}
public SecuenciaDefConstanteTreeNode() {
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return secuenciaDefConstanteModelo.get(index);
}
public int getIndexOfChild(Object child) {
return secuenciaDefConstanteModelo.indexOf(child);
}
public int getChildCount() {
return secuenciaDefConstanteModelo.getSize();
}
public void valueForPathChanged(TreePath path, Object newValue) {
199
Sobre la información, su estructura y su-gestión
}
public void setModelo(Object obj) {
secuenciaDefConstante = (SecuenciaDefConstante) obj;
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
for (int i=0; i<secuenciaDefConstante.getSize();i++){
secuenciaDefConstanteModelo.addElement(
TreeNode.getTreeNode(secuenciaDefConstante.getDefConstanteAt(i)));
}
}
public String toString(){
return this.cabecera;
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaDefParametro extends Secuencia {
private Vector delegate = new Vector();
public SecuenciaDefParametro() {
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
public DefParametro getDefParametroAt(int index) {
return (DefParametro)delegate.elementAt(index);
}
public void copyInto(DefParametro anArray[]) {
delegate.copyInto(anArray);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
200
Sobre la información, su estructura y su-gestión
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Enumeration elements() {
return delegate.elements();
}
public boolean contains(DefParametro elem) {
return delegate.contains(elem);
}
public int indexOf(DefParametro elem) {
return delegate.indexOf(elem);
}
public int indexOf(DefParametro elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(DefParametro elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(DefParametro elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public DefParametro elementAt(int index) {
return (DefParametro)delegate.elementAt(index);
}
public DefParametro firstElement() {
return (DefParametro)delegate.firstElement();
}
public DefParametro lastElement() {
return (DefParametro)delegate.lastElement();
}
public void setElementAt(DefParametro obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
}
public void insertElementAt(DefParametro obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(DefParametro obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(DefParametro obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
201
Sobre la información, su estructura y su-gestión
}
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String toString() {
return delegate.toString();
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public DefParametro get(int index) {
return (DefParametro)delegate.elementAt(index);
}
public DefParametro set(int index, DefParametro element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (DefParametro)rv;
}
public void add(int index, DefParametro element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public DefParametro remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (DefParametro)rv;
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
for(int i=0; i<this.getSize();i++){
((DefParametro)this.get(i)).convertirEnEjecutable(salida,gestor);
}
}
public String getRepTextual(){
202
Sobre la información, su estructura y su-gestión
StringBuffer salida = new StringBuffer();
for(int i=0; i<this.size(); i++){
salida.append(this.getDefParametroAt(i).getRepTextual()+", ");
}
return salida.toString();
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaDefSubprograma extends Secuencia {
private Vector delegate = new Vector();
public SecuenciaDefSubprograma() {
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
public DefSubprograma getDefSubprogramaAt(int index) {
return (DefSubprograma)delegate.elementAt(index);
}
public void copyInto(DefSubprograma anArray[]) {
delegate.copyInto(anArray);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Enumeration elements() {
return delegate.elements();
203
Sobre la información, su estructura y su-gestión
}
public boolean contains(DefSubprograma elem) {
return delegate.contains(elem);
}
public int indexOf(DefSubprograma elem) {
return delegate.indexOf(elem);
}
public int indexOf(DefSubprograma elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(DefSubprograma elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(DefSubprograma elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public DefSubprograma elementAt(int index) {
return (DefSubprograma)delegate.elementAt(index);
}
public DefSubprograma firstElement() {
return (DefSubprograma)delegate.firstElement();
}
public DefSubprograma lastElement() {
return (DefSubprograma)delegate.lastElement();
}
public void setElementAt(DefSubprograma obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
}
public void insertElementAt(DefSubprograma obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(DefSubprograma obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(DefSubprograma obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
204
Sobre la información, su estructura y su-gestión
}
}
public String toString() {
return delegate.toString();
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public DefSubprograma get(int index) {
return (DefSubprograma)delegate.elementAt(index);
}
public DefSubprograma set(int index, DefSubprograma element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (DefSubprograma)rv;
}
public void add(int index, DefSubprograma element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public DefSubprograma remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (DefSubprograma)rv;
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
for(int i=0; i<this.getSize();i++){
((DefSubprograma)this.get(i)).convertirEnEjecutable(salida,gestor);
}
}
public String getRepTextual(){
StringBuffer salida = new StringBuffer();
for(int i=0; i<this.size(); i++){
salida.append(this.getDefSubprogramaAt(i).getRepTextual()+",
");
}
return salida.toString();
}
}
package tfc;
205
Sobre la información, su estructura y su-gestión
import javax.swing.DefaultListModel;
import javax.swing.tree.TreePath;
public class SecuenciaDefSubprogramaTreeNode extends TreeNode {
SecuenciaDefSubprograma secuenciaDefSubprograma;
DefaultListModel secuenciaDefSubprogramaModelo = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return secuenciaDefSubprograma;
}
public SecuenciaDefSubprogramaTreeNode(SecuenciaDefSubprograma
secuenciaDefSubprograma) {
this.setModeloRecursivo(secuenciaDefSubprograma);
}
public SecuenciaDefSubprogramaTreeNode() {
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return secuenciaDefSubprogramaModelo.get(index);
}
public int getIndexOfChild(Object child) {
return secuenciaDefSubprogramaModelo.indexOf(child);
}
public int getChildCount() {
return secuenciaDefSubprogramaModelo.getSize();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
secuenciaDefSubprograma = (SecuenciaDefSubprograma) obj;
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
for (int i=0; i<secuenciaDefSubprograma.getSize();i++){
secuenciaDefSubprogramaModelo.addElement(
TreeNode.getTreeNode( secuenciaDefSubprograma.getDefSubprogramaAt(i)));
}
}
public String toString(){
return cabecera;
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaDefTipo extends Secuencia {
private Vector delegate = new Vector();
public SecuenciaDefTipo() {
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
206
Sobre la información, su estructura y su-gestión
public DefTipo getDefTipoAt(int index) {
return (DefTipo)delegate.elementAt(index);
}
public void copyInto(DefTipo anArray[]) {
delegate.copyInto(anArray);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Enumeration elements() {
return delegate.elements();
}
public boolean contains(DefTipo elem) {
return delegate.contains(elem);
}
public int indexOf(DefTipo elem) {
return delegate.indexOf(elem);
}
public int indexOf(DefTipo elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(DefTipo elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(DefTipo elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public DefTipo elementAt(int index) {
return (DefTipo)delegate.elementAt(index);
}
public DefTipo firstElement() {
return (DefTipo)delegate.firstElement();
207
Sobre la información, su estructura y su-gestión
}
public DefTipo lastElement() {
return (DefTipo)delegate.lastElement();
}
public void setElementAt(DefTipo obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
}
public void insertElementAt(DefTipo obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(DefTipo obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(DefTipo obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String toString() {
return delegate.toString();
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public DefTipo get(int index) {
return (DefTipo)delegate.elementAt(index);
}
public DefTipo set(int index, DefTipo element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (DefTipo)rv;
}
public void add(int index, DefTipo element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
208
Sobre la información, su estructura y su-gestión
public DefTipo remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (DefTipo)rv;
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public String getRepTextual(){
StringBuffer salida = new StringBuffer();
for(int i=0; i<this.size(); i++){
salida.append(this.getDefTipoAt(i).getRepTextual()+", ");
}
return salida.toString();
}
}
package tfc;
import javax.swing.DefaultListModel;
import javax.swing.tree.TreePath;
public class SecuenciaDefTipoTreeNode extends TreeNode {
SecuenciaDefTipo secuenciaDefTipo;
DefaultListModel secuenciaDefTipoModelo = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return secuenciaDefTipo;
}
public SecuenciaDefTipoTreeNode(SecuenciaDefTipo secuenciaDefTipo) {
this.setModeloRecursivo(secuenciaDefTipo);
setCabecera("Tipos");
}
public SecuenciaDefTipoTreeNode() {
setCabecera("Tipos");
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return secuenciaDefTipoModelo.get(index);
}
public int getIndexOfChild(Object child) {
return secuenciaDefTipoModelo.indexOf(child);
}
public int getChildCount() {
return secuenciaDefTipoModelo.getSize();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
secuenciaDefTipo = (SecuenciaDefTipo) obj;
}
public void setModeloRecursivo(Object obj) {
209
Sobre la información, su estructura y su-gestión
this.setModelo(obj);
for (int i=0; i<secuenciaDefTipo.getSize();i++){
secuenciaDefTipoModelo.addElement( TreeNode.getTreeNode(
secuenciaDefTipo.getDefTipoAt(i)));
}
}
public String toString(){
return cabecera;
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaDefVariable extends Secuencia {
private Vector delegate = new Vector();
public SecuenciaDefVariable() {
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
public DefVariable getDefVariableAt(int index) {
return (DefVariable)delegate.elementAt(index);
}
public void copyInto(DefVariable anArray[]) {
delegate.copyInto(anArray);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
210
Sobre la información, su estructura y su-gestión
public Enumeration elements() {
return delegate.elements();
}
public boolean contains(DefVariable elem) {
return delegate.contains(elem);
}
public int indexOf(DefVariable elem) {
return delegate.indexOf(elem);
}
public int indexOf(DefVariable elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(DefVariable elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(DefVariable elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public DefVariable elementAt(int index) {
return (DefVariable)delegate.elementAt(index);
}
public DefVariable firstElement() {
return (DefVariable)delegate.firstElement();
}
public DefVariable lastElement() {
return (DefVariable)delegate.lastElement();
}
public void setElementAt(DefVariable obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
}
public void insertElementAt(DefVariable obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(DefVariable obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(DefVariable obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
211
Sobre la información, su estructura y su-gestión
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String toString() {
return delegate.toString();
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public DefVariable get(int index) {
return (DefVariable)delegate.elementAt(index);
}
public DefVariable set(int index, DefVariable element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (DefVariable)rv;
}
public void add(int index, DefVariable element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public DefVariable remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (DefVariable)rv;
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String getRepTextual(){
StringBuffer salida = new StringBuffer();
for(int i=0; i<this.size(); i++){
salida.append(this.getDefVariableAt(i).getRepTextual()+", ");
}
return salida.toString();
}
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
for(int i=0; i<this.getSize();i++){
((DefVariable)this.get(i)).convertirEnEjecutable(salida,gestor);
}
}
212
Sobre la información, su estructura y su-gestión
}
package tfc;
public class SecuenciaDefVariableEjecucion extends Secuencia {
public SecuenciaDefVariableEjecucion() {
}
public ItemDeInformacion getDefVariableEjecucionAt(int index) {
return (ItemDeInformacion)delegate.elementAt(index);
}
public void copyInto(ItemDeInformacion anArray[]) {
delegate.copyInto(anArray);
}
public boolean contains(ItemDeInformacion elem) {
return delegate.contains(elem);
}
public int indexOf(ItemDeInformacion elem) {
return delegate.indexOf(elem);
}
public int indexOf(ItemDeInformacion elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(ItemDeInformacion elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(ItemDeInformacion elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public ItemDeInformacion elementAt(int index) {
return (ItemDeInformacion)delegate.elementAt(index);
}
public ItemDeInformacion firstElement() {
return (ItemDeInformacion)delegate.firstElement();
}
public ItemDeInformacion lastElement() {
return (ItemDeInformacion)delegate.lastElement();
}
//
//
//
public void setElementAt(ItemDeInformacion obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void insertElementAt(ItemDeInformacion obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(ItemDeInformacion obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(ItemDeInformacion obj) {
int index = indexOf(obj);
213
Sobre la información, su estructura y su-gestión
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
//
}
public ItemDeInformacion get(int index) {
return (ItemDeInformacion)delegate.elementAt(index);
}
//
//
//
//
public ItemDeInformacion set(int index, ItemDeInformacion element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (ItemDeInformacion)rv;
}
public void add(int index, ItemDeInformacion element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public ItemDeInformacion remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (ItemDeInformacion)rv;
}
public void libera(java.io.PrintWriter salida){
for (int i=0; i<this.size();i++){
this.getDefVariableEjecucionAt(i).libera();
(this.remove(i)).liberacion(salida);
}
}
}
package tfc;
import javax.swing.DefaultListModel;
import javax.swing.tree.TreePath;
public class SecuenciaDefVariableTreeNode extends TreeNode {
SecuenciaDefVariable secuenciaDefVariable;
DefaultListModel secuenciaDefVariableModelo = new DefaultListModel();
public ElementoCodigoInterfaz getElementoCodigo(){
return secuenciaDefVariable;
}
public SecuenciaDefVariableTreeNode(SecuenciaDefVariable
secuenciaDefVariable) {
this.setModeloRecursivo(secuenciaDefVariable);
setCabecera("Variables");
}
public SecuenciaDefVariableTreeNode() {
setCabecera("Variables");
}
public boolean isLeaf() {
return false;
}
public Object getChild(int index) {
return secuenciaDefVariableModelo.get(index);
}
public int getIndexOfChild(Object child) {
return secuenciaDefVariableModelo.indexOf(child);
}
public int getChildCount() {
return secuenciaDefVariableModelo.getSize();
}
214
Sobre la información, su estructura y su-gestión
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
secuenciaDefVariable = (SecuenciaDefVariable) obj;
}
public void setModeloRecursivo(Object obj) {
this.setModelo(obj);
for (int i=0; i<secuenciaDefVariable.getSize();i++){
secuenciaDefVariableModelo.addElement(
TreeNode.getTreeNode(secuenciaDefVariable.getDefVariableAt(i)));
}
}
public String toString(){
return cabecera;
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
public abstract class SecuenciaEjecucion extends Ejecucion {
int nElementos;
public int getNElementos(){
return nElementos;
}
public void setNElementos(int nElementos){
this.nElementos = nElementos;
}
}
package tfc;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.event.*;
public class SecuenciaEjecutable extends Secuencia {
public SecuenciaEjecutable() {
}
public int getSize() {
return delegate.size();
}
public Object getElementAt(int index) {
return delegate.elementAt(index);
}
public Sentencia getEjecutableAt(int index) {
return (Sentencia)delegate.elementAt(index);
}
public void copyInto(Sentencia anArray[]) {
delegate.copyInto(anArray);
}
public void trimToSize() {
delegate.trimToSize();
}
public void ensureCapacity(int minCapacity) {
delegate.ensureCapacity(minCapacity);
}
215
Sobre la información, su estructura y su-gestión
public void setSize(int newSize) {
int oldSize = delegate.size();
delegate.setSize(newSize);
if (oldSize > newSize) {
fireIntervalRemoved(this, newSize, oldSize-1);
}
else if (oldSize < newSize) {
fireIntervalAdded(this, oldSize, newSize-1);
}
}
public int capacity() {
return delegate.capacity();
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Enumeration elements() {
return delegate.elements();
}
public boolean contains(Sentencia elem) {
return delegate.contains(elem);
}
public int indexOf(Sentencia elem) {
return delegate.indexOf(elem);
}
public int indexOf(Sentencia elem, int index) {
return delegate.indexOf(elem, index);
}
public int lastIndexOf(Sentencia elem) {
return delegate.lastIndexOf(elem);
}
public int lastIndexOf(Sentencia elem, int index) {
return delegate.lastIndexOf(elem, index);
}
public Sentencia elementAt(int index) {
return (Sentencia)delegate.elementAt(index);
}
public Sentencia firstElement() {
return (Sentencia)delegate.firstElement();
}
public Sentencia lastElement() {
return (Sentencia)delegate.lastElement();
}
public void setElementAt(Sentencia obj, int index) {
delegate.setElementAt(obj, index);
fireContentsChanged(this, index, index);
}
public void removeElementAt(int index) {
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
}
216
Sobre la información, su estructura y su-gestión
public void insertElementAt(Sentencia obj, int index) {
delegate.insertElementAt(obj, index);
fireIntervalAdded(this, index, index);
}
public void addElement(Sentencia obj) {
int index = delegate.size();
delegate.addElement(obj);
fireIntervalAdded(this, index, index);
}
public boolean removeElement(Sentencia obj) {
int index = indexOf(obj);
boolean rv = delegate.removeElement(obj);
if (index >= 0) {
fireIntervalRemoved(this, index, index);
}
return rv;
}
public void removeAllElements() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
public String toString() {
return delegate.toString();
}
public Object[] toArray() {
Object[] rv = new Object[delegate.size()];
delegate.copyInto(rv);
return rv;
}
public Sentencia get(int index) {
return (Sentencia)delegate.elementAt(index);
}
public Sentencia set(int index, Sentencia element) {
Object rv = delegate.elementAt(index);
delegate.setElementAt(element, index);
fireContentsChanged(this, index, index);
return (Sentencia)rv;
}
public void add(int index, Sentencia element) {
delegate.insertElementAt(element, index);
fireIntervalAdded(this, index, index);
}
public Sentencia remove(int index) {
Object rv = delegate.elementAt(index);
delegate.removeElementAt(index);
fireIntervalRemoved(this, index, index);
return (Sentencia)rv;
}
public void clear() {
int index1 = delegate.size()-1;
delegate.removeAllElements();
if (index1 >= 0) {
fireIntervalRemoved(this, 0, index1);
}
}
217
Sobre la información, su estructura y su-gestión
public void removeRange(int fromIndex, int toIndex) {
for(int i = toIndex; i >= fromIndex; i--) {
delegate.removeElementAt(i);
}
fireIntervalRemoved(this, fromIndex, toIndex);
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
for(int i=0; i<this.size();i++){
this.getEjecutableAt(i).resuelveReferencias(ambito,errores);
}
}
}
package tfc;
public abstract class SecuenciaEjecutableEjecucion extends SecuenciaEjecucion
implements EjecutableEjecucionI {
public SecuenciaEjecutableEjecucion() {
}
public void ejecuta(InformacionAccesible inf, java.io.PrintWriter salida){
for(int i=0; i<this.getNElementos();i++){
this.ejecutaYFin(i,inf,salida);
}
}
public void fin(){
}
protected abstract void ejecutaYFin(int i, InformacionAccesible inf,
java.io.PrintWriter salida);
}
package tfc;
public interface Sentencia extends ElementoCodigoEjecucion {
public String getRepTextual();
}
package tfc;
public abstract class TipoCompuesto extends DefTipo {
}
package tfc;
public abstract class TipoSimple extends DefTipo {
public ValorSimple getValor(String representacion){
return new ValorSimple(representacion,this);
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
}
}
package tfc;
public class TiposIncompatiblesException extends Exception {
public TiposIncompatiblesException() {
}
218
Sobre la información, su estructura y su-gestión
}
package tfc;
import javax.swing.tree.*;
public abstract class TreeNode implements TreeNodeI {
String cabecera = "";
public
public
public
public
public
abstract void setModelo(Object obj);
abstract void setModeloRecursivo(Object obj);
abstract boolean isLeaf();
abstract int getChildCount();
void setCabecera(String cabecera){
this.cabecera = cabecera;
}
public static TreeNodeI getTreeNode( ElementoCodigoInterfaz elemento){
Class clase;
Class claseTreeNode;
TreeNodeI salida = null;
try {
clase = elemento.getClass();
claseTreeNode = Class.forName( clase.getName() + "TreeNode" );
salida = (TreeNodeI)claseTreeNode.newInstance();
salida.setModeloRecursivo( elemento );
} catch (ClassNotFoundException e){
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
} catch (InstantiationException e){
e.printStackTrace();
}
return salida;
}
public abstract ElementoCodigoInterfaz getElementoCodigo();
public String toString(){
return cabecera + this.getElementoCodigo().toString();
}
}
package tfc;
import javax.swing.tree.TreePath;
public interface TreeNodeI {
public abstract void setModelo(Object obj);
public abstract void setModeloRecursivo(Object obj);
public abstract boolean isLeaf();
public abstract int getIndexOfChild(Object child);
public abstract void valueForPathChanged(TreePath path, Object newValue);
public abstract int getChildCount();
public abstract Object getChild(int index);
}
package tfc;
public abstract class Valor extends ElementoCodigo implements Evaluable {
DefTipo defTipo;
public DefTipo getDefTipo(){
219
Sobre la información, su estructura y su-gestión
return defTipo;
}
public void setDefTipo(DefTipo defTipo){
this.defTipo = defTipo;
}
public void resuelveReferencias(GestorAmbito ambito, ErroresReferencias
errores){
}
public boolean equals(Valor v){
return (this.getDefTipo() == v.getDefTipo());
}
}
package tfc;
public class ValorOrdinal extends ValorSimple {
public ValorOrdinal(String representacion, DefTipo defTipo){
super(representacion,defTipo);
}
}
package tfc;
public class ValorReal extends ValorSimple {
public ValorReal(String representacion) {
super(representacion, Real.getInstance() );
}
public double getDouble(){
return Double.parseDouble(representacion);
}
}
package tfc;
public class ValorSimple extends Valor {
String representacion;
public ValorSimple(String representacion, DefTipo defTipo){
this.representacion = representacion;
this.setDefTipo(defTipo);
}
public String getRepTextual(){
return representacion;
}
String getRepresentacion(){
return representacion;
}
public boolean equals(ValorSimple v){
return super.equals(v) && ( this.getRepresentacion().equals(
v.getRepresentacion() ));
}
public void convertirEnEjecutable(java.io.PrintWriter salida,
GestorIdentificadores gestor){
this.setCodigo(gestor);
salida.println("//-------------------------------------------------------------------");
220
Sobre la información, su estructura y su-gestión
salida.println("class "+this.getNombreEjecucion()+" implements
EvaluableEjecucionI {" );
salida.println(" public "+this.getNombreEjecucion()+"() {}");
salida.println(" public Valor evaluate(InformacionAccesible i,
java.io.PrintWriter salida){");
salida.println("
return
Entero.getInstance().getValor(\""+this.getRepTextual()+"\");");
salida.println(" }");
salida.println("}");
}
}
package tfc;
import javax.swing.tree.*;
public class ValorSimpleTreeNode extends TreeNode implements EvaluableTreeNodeI {
ValorSimple valorSimple;
public ElementoCodigoInterfaz getElementoCodigo(){
return valorSimple;
}
public ValorSimpleTreeNode(ValorSimple valorSimple) {
this.setModeloRecursivo(valorSimple);
setCabecera("Valor Simple -> ");
}
public ValorSimpleTreeNode() {
setCabecera("Valor Simple -> ");
}
public boolean isLeaf() {
return true;
}
public Object getChild(int index) {
return null;
}
public int getIndexOfChild(Object child) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
valorSimple = (ValorSimple) obj;
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
}
public String toString(){
return cabecera + valorSimple.getRepTextual();
}
}
package tfc;
import javax.swing.tree.*;
public class ValorTreeNode extends TreeNode {
221
Sobre la información, su estructura y su-gestión
Valor valor;
public ValorTreeNode() {
}
public ElementoCodigoInterfaz getElementoCodigo(){
return valor;
}
public boolean isLeaf() {
return true;
}
public Object getChild(int index) {
return null;
}
public int getIndexOfChild(Object child) {
return -1;
}
public int getChildCount() {
return 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void setModelo(Object obj) {
this.valor = (Valor) obj;
}
public void setModeloRecursivo(Object obj){
this.setModelo(obj);
}
public String toString(){
return cabecera + valor.getRepTextual();
}
}
222
Anexo B. Especificación en JavaCC
options {
STATIC = true;
IGNORE_CASE = true;
}
PARSER_BEGIN(Pascal)
package tfc;
import javax.swing.JTextArea;
import javax.swing.DefaultListModel;
import java.util.Vector;
public class Pascal {
}
PARSER_END(Pascal)
SKIP :
{
" "
| "\t"
| "\n"
| "\r"
}
TOKEN :
{
|
|
|
|
|
|
|
|
|
|
|
|
<AND: "AND">
<CONST: "CONST">
<ELSE: "ELSE">
<MOD:"MOD">
<OR:"OR">
<RECORD:"RECORD">
<VAR:"VAR">
<ARRAY:"ARRAY">
<DIV:"DIV">
<END:"END">
<NOT:"NOT">
<PROCEDURE:"PROCEDURE">
<FUNCTION:"FUNCTION">
223
Sobre la información, su estructura y su-gestión
|
|
|
|
|
|
|
|
|
|
<THEN:"THEN">
<WHILE:"WHILE">
<BEGIN:"BEGIN">
<DO:"DO">
<IF:"IF">
<OF:"OF">
<PROGRAM:"PROGRAM">
<TYPE:"TYPE">
<ID: ["a"-"z","A"-"Z"] ( ["a"-"z","A"-"Z","0"-"9"] )*>
<NUMERO: (["0"-"9"])+ >
}
DefPrograma programa() :
{
Nombre n;
DefPrograma defPrograma;
}
{
<PROGRAM> n=id() ";"
{
defPrograma = new DefPrograma(n);
}
bloque(defPrograma) "."
{
return defPrograma;
}
}
void bloque(DefPrograma defPrograma) :
{
InstruccionCompuesta instruccion;
}
{
declaracion(defPrograma) instruccion = grupoSent(defPrograma.getAmbito()) {
defPrograma.setCuerpo(instruccion); }
}
void declaracion(DefPrograma defPrograma) :
{}
{
[ defCte(defPrograma.getConstantes(),defPrograma.getAmbito()) ]
[ defTip(defPrograma.getTipos(),defPrograma.getAmbito()) ]
[ defVar(defPrograma.getVariables(),defPrograma.getAmbito()) ]
[ defSubprograma(defPrograma.getSubprogramas(),defPrograma) ]
}
void defSubprograma(SecuenciaDefSubprograma secuenciaDefSubprograma, AccesoAmbito
ambito) :
{
DefSubprograma defSubprograma;
}
{
(
( defSubprograma = defProcedimiento(ambito) | defSubprograma = defFuncion(ambito)
)
{ secuenciaDefSubprograma.addElement(defSubprograma); }
)+
}
void defCte(SecuenciaDefConstante secuenciaDefConstante, GestorAmbito ambito) :
{}
{
<CONST> (unaConst(secuenciaDefConstante,ambito) ";")+
}
void unaConst(SecuenciaDefConstante secuenciaDefConstante, GestorAmbito ambito) :
{
Token t;
Valor v;
}
{
t=<ID> "=" v=valorSimple() { secuenciaDefConstante.addElement(new
DefConstante(new Nombre(t.image),v));}
224
Sobre la información, su estructura y su-gestión
}
void defTip(SecuenciaDefTipo secuenciaDefTipo, GestorAmbito ambito) :
{}
{
<TYPE> (unTipo() ";")+
}
void unTipo() :
{}
{
<ID> "=" tipoEstr()
}
void tipoEstr() :
{}
{
( estrTab() | estrReg() )
}
void estrTab() :
{}
{
<ARRAY> "[" valorSimple() ".." valorSimple() "]" <OF> <ID>
}
void estrReg() :
{}
{
<RECORD> listaVar(null) ( ";" listaVar(null) )* <END>
}
void defVar(SecuenciaDefVariable secuenciaDefVariable, GestorAmbito ambito) :
{}
{
<VAR> ( listaVar(secuenciaDefVariable) ";" )+
}
DefProcedimiento defProcedimiento(AccesoAmbito ambito) :
{
Nombre n;
DefProcedimiento procedimiento;
}
{
<PROCEDURE> n=id() { procedimiento = new DefProcedimiento(n,ambito); }
[ "(" parFormales(procedimiento.getParametros()) ")" ] ";" bloque(procedimiento)
";"
{ return procedimiento; }
}
DefFuncion defFuncion(AccesoAmbito ambito) :
{
Nombre n;
DefFuncion funcion;
}
{
<FUNCTION> n=id() { funcion = new DefFuncion(n,ambito); }
[ "(" parFormales(funcion.getParametros()) ")" ] ":" n=id() { funcion.setTipo(
new RefTipo(n)); }";" bloque(funcion) ";"
{ return funcion; }
}
void parFormales(SecuenciaDefParametro secuenciaDefParametro) :
{}
{
listaParam(secuenciaDefParametro) ( ";" listaParam(secuenciaDefParametro) )*
}
void listaParam(SecuenciaDefParametro parametros) :
{
Nombre n;
Vector nombres = new Vector();
boolean pasoPorValor = true;
}
225
Sobre la información, su estructura y su-gestión
{
[ <VAR> { pasoPorValor = false;} ] n=id() { nombres.add(n); }( "," n=id() {
nombres.add(n); } )* ":" n=id()
{
for(int i=0; i<nombres.size(); i++){
parametros.addElement(new DefParametro((Nombre)nombres.get(i),new
RefTipo(n),pasoPorValor));
}
}
}
void listaVar(SecuenciaDefVariable variables) :
{
Nombre n;
Vector nombres = new Vector();
}
{
n=id() { nombres.add(n); }( "," n=id() { nombres.add(n); } )* ":" n=id()
{
for(int i=0; i<nombres.size(); i++){
variables.addElement(new DefVariable((Nombre)nombres.get(i),new
RefTipo(n)));
}
}
}
InstruccionCompuesta grupoSent(GestorAmbito ambito) :
{
InstruccionCompuesta instruccion = new InstruccionCompuesta();
SecuenciaEjecutable secuencia;
Ejecutable ejecutable;
}
{
{
secuencia = instruccion.getInstrucciones();
}
<BEGIN> ejecutable = sentencia(ambito) { secuencia.addElement(ejecutable); }
( ";" ejecutable = sentencia(ambito) { secuencia.addElement(ejecutable); } )*
<END>
{ return instruccion; }
}
Ejecutable sentencia(GestorAmbito ambito) :
{
Ejecutable ejecutable = null;
boolean sentenciaVacia = true;
}
{
[ ( LOOKAHEAD(2)
ejecutable = asignacion(ambito)
| ejecutable = condicional(ambito)
| ejecutable = ciclica(ambito)
| ejecutable = llamada(ambito)
| ejecutable = grupoSent(ambito)) { sentenciaVacia = false; }]
{
if (sentenciaVacia) {
ejecutable = new InstruccionVacia();
}
return ejecutable;
}
}
Asignacion asignacion(GestorAmbito ambito) :
{
Evaluable evaluable;
Nombre n;
}
{
n = id() ":=" evaluable = expresion(ambito) { return new Asignacion(new
RefVariable(n),evaluable); }
}
Ejecutable condicional(GestorAmbito ambito) :
226
Sobre la información, su estructura y su-gestión
{
Condicional condicional;
Evaluable evaluable;
Ejecutable ejecutable;
}
{
<IF> evaluable = expresion(ambito) <THEN> ejecutable = sentencia(ambito) {
condicional = new Condicional(evaluable,ejecutable); }
[ LOOKAHEAD(1) <ELSE> ejecutable = sentencia(ambito) {
condicional.setParteElse(ejecutable); } ] {return condicional;}
}
Ejecutable ciclica(GestorAmbito ambito) :
{}
{
<WHILE> expresion(ambito) <DO> sentencia(ambito) {return null;}
}
RefProcedimiento llamada(GestorAmbito ambito) :
{
Nombre n;
RefProcedimiento refProcedimiento;
}
{
n = id() { refProcedimiento = new RefProcedimiento(n); }[ "("
parLlamada(refProcedimiento.getParamActuales(),ambito) ")" ]
{
return refProcedimiento;
}
}
void parLlamada(ParametrosActuales paramActuales, GestorAmbito ambito) :
{
Referencia referencia;
Evaluable evaluable = null;
}
{
( evaluable = expresion(ambito) {
if (evaluable instanceof RefVariable){
paramActuales.addRefVariable((RefVariable)evaluable);
} else {
paramActuales.addEvaluable(evaluable);
}
}
)
( "," evaluable = expresion(ambito) {
if (evaluable instanceof RefVariable){
paramActuales.addRefVariable((RefVariable)evaluable);
} else {
paramActuales.addEvaluable(evaluable);
}
}
)*
}
Evaluable expresion(GestorAmbito ambito) :
{
Evaluable evaluable1 = null;
Evaluable evaluable2 = null;
Nombre signo = null;
}
{
evaluable1 = exprSimple(ambito) [ signo = opRel() evaluable2 = exprSimple(ambito)
]
{
if (signo == null) {
return evaluable1;
} else {
RefFuncion refFuncion = new RefFuncionOperador(signo);
ParametrosActuales param = refFuncion.getParamActuales();
param.addEvaluable(evaluable1);
param.addEvaluable(evaluable2);
return refFuncion;
227
Sobre la información, su estructura y su-gestión
}
}
}
Evaluable exprSimple(GestorAmbito ambito) :
{
Evaluable evaluable1;
Evaluable evaluable2;
Nombre signo = null;
}
{
// El lookahead este está porque hay un factor puede ser una constante con signo,
pero poniendo
// el lookahead(2) mira si termino corresponde o no a una constante y en este
caso lo asocia a
// la constante.
[ LOOKAHEAD(2) signo=signo() ] evaluable1 = termino(ambito)
{
if (signo != null && !signo.toString().equals("+")){
RefFuncion refFuncion = new RefFuncionOperador(signo);
refFuncion.getParamActuales().addEvaluable(evaluable1);
evaluable1 = refFuncion;
}
}
// Ojo con la asociatividad, no recuerdo si era a la derecha o a la izquierda. La
verdad es que no
// recuerdo exáctamente que significaba cada cosa. Bueno, pongo que se vayan
asociando empezando
// en la izquierda.
( LOOKAHEAD(2) signo=opAdt() evaluable2 = termino(ambito) {
RefFuncion refFuncion = new RefFuncionOperador(signo);
refFuncion.getParamActuales().addEvaluable(evaluable1);
refFuncion.getParamActuales().addEvaluable(evaluable2);
evaluable1 = refFuncion;
} )*
{ return evaluable1; }
}
Evaluable termino(GestorAmbito ambito) :
{
Evaluable evaluable1;
Evaluable evaluable2;
Nombre signo;
}
{
evaluable1 = factor(ambito)
( signo=opMul() evaluable2 = factor(ambito)
{
RefFuncion refFuncion = new RefFuncionOperador(signo);
refFuncion.getParamActuales().addEvaluable(evaluable1);
refFuncion.getParamActuales().addEvaluable(evaluable2);
evaluable1 = refFuncion;
}
)* { return evaluable1; }
}
Evaluable factor(GestorAmbito ambito) :
{
Referencia referencia;
Evaluable evaluable;
Token t;
}
{
(
LOOKAHEAD(2)
evaluable = valorSimple()
228
Sobre la información, su estructura y su-gestión
| evaluable = nombreEval(ambito)
| t=<NOT> evaluable = factor(ambito) {
RefFuncion refFuncion;
refFuncion = new RefFuncionOperador(new Nombre(t.image));
refFuncion.getParamActuales().addEvaluable(evaluable);
evaluable = refFuncion;
}
| "(" evaluable = expresion(ambito) ")"
)
{
return evaluable;
}
}
Evaluable nombreEval(GestorAmbito ambito) :
{
Nombre n;
}
{
n = id() {
try {
return EvaluableInstancia.getEvaluable(n,ambito);
} catch(DefinicionNoEncontradaException e2) {
throw new ParseException("No encontrado una definicion para el nombre
"+n.toString());
}
}
// Ya pondremos acceso a arrays [ selector(ambito) ]
}
void selector(GestorAmbito ambito) :
{}
{
"[" expresion(ambito) "]" | "." <ID>
}
ValorSimple valorSimple() :
{
Token t;
Nombre signo = null;
}
{
[ signo=signo() ] t=<NUMERO> { return Entero.getInstance().getValor( ((signo ==
null)? "":signo.toString()) + t.image ); }
}
Nombre opRel() :
{
Token t;
}
{
(t="<" | t=">" | t="<=" | t=">=" | t="=" | t="<>")
{ return new Nombre(t.image); }
}
Nombre opAdt() :
{
Token t;
Nombre n;
}
{
( n=signo() | t="OR" { n = new Nombre(t.image); } )
{ return n; }
}
Nombre opMul() :
{
Token t;
}
{
(t="*" | t="DIV" | t="MOD" | t="AND")
{ return new Nombre(t.image); }
}
229
Sobre la información, su estructura y su-gestión
Nombre signo() :
{
Token t;
}
{
(t="+" | t="-")
{ return new Nombre(t.image); }
}
Nombre id() :
{
Token t;
}
{
t=<ID> { return new Nombre(t.image);}
}
230
Anexo C. Diagramas UML
En este anexo se muestra un diagrama de cada una de las clases mas
representativas.
231
Sobre la información, su estructura y su-gestión
232
Sobre la información, su estructura y su-gestión
233
Sobre la información, su estructura y su-gestión
234
Sobre la información, su estructura y su-gestión
235
Sobre la información, su estructura y su-gestión
236
Sobre la información, su estructura y su-gestión
237
Sobre la información, su estructura y su-gestión
238
Sobre la información, su estructura y su-gestión
239
Sobre la información, su estructura y su-gestión
240
Sobre la información, su estructura y su-gestión
241
Sobre la información, su estructura y su-gestión
242
Sobre la información, su estructura y su-gestión
243
Sobre la información, su estructura y su-gestión
244
Sobre la información, su estructura y su-gestión
245
Sobre la información, su estructura y su-gestión
246
Sobre la información, su estructura y su-gestión
247
Sobre la información, su estructura y su-gestión
248
Sobre la información, su estructura y su-gestión
249
Sobre la información, su estructura y su-gestión
250
Sobre la información, su estructura y su-gestión
251
Sobre la información, su estructura y su-gestión
252
Sobre la información, su estructura y su-gestión
253
Sobre la información, su estructura y su-gestión
254
Sobre la información, su estructura y su-gestión
255
Sobre la información, su estructura y su-gestión
256
Sobre la información, su estructura y su-gestión
257
Sobre la información, su estructura y su-gestión
258
Sobre la información, su estructura y su-gestión
259
Sobre la información, su estructura y su-gestión
260
Sobre la información, su estructura y su-gestión
261
Sobre la información, su estructura y su-gestión
262
Sobre la información, su estructura y su-gestión
263
Sobre la información, su estructura y su-gestión
264
Sobre la información, su estructura y su-gestión
265
Sobre la información, su estructura y su-gestión
266
Sobre la información, su estructura y su-gestión
267
Sobre la información, su estructura y su-gestión
268
Sobre la información, su estructura y su-gestión
269
Sobre la información, su estructura y su-gestión
270
Sobre la información, su estructura y su-gestión
271
Sobre la información, su estructura y su-gestión
272
Sobre la información, su estructura y su-gestión
273
Sobre la información, su estructura y su-gestión
274
Sobre la información, su estructura y su-gestión
275
Sobre la información, su estructura y su-gestión
276
Sobre la información, su estructura y su-gestión
277
Sobre la información, su estructura y su-gestión
278
Sobre la información, su estructura y su-gestión
279
Sobre la información, su estructura y su-gestión
280
Sobre la información, su estructura y su-gestión
281
Sobre la información, su estructura y su-gestión
282
Descargar