Zero es una máquina virtual orientada a objetos y basada en prototipos. La orientación a objetos ofrecida en Zero no es como la de C++, sino la de otros lenguajes como Self. Se crean directamente objetos (prototipos), los cuales se copian, para crear las objetos-instancia que realmente se emplearán. El lenguaje de programación, así como otros muchos lenguajes basados en prototipos, es débilmente tipado. Esto quiere decir que no se hacen comprobaciones de tipos, como en C++ o Java. Una última capacidad de la máquina virtual es que ofrece persistencia, esto es, no es necesario dedicar esfuerzo a indicar cómo guardar la información almacenada en los objetos, sino que éstos pueden guardarse y recuperarse automáticamente.
Los programas para Prowl, para macroensamblador y la propia máquina virtual Zero, necesarios para compilar y ejecutar los objetos creados, más la documentación. Hay binarios para Linux y para Windows.
La máquina virtual Zero es persistente por defecto. Los programas se componen de objetos, y estos se ejecutan después de ser colocados en diferentes contenedores. Así, en un programa como el que sigue a continuación, existen al menos tres contenedores: el contenedor raíz (psRoot), el contenedor donde se ejecutan los programas (Exe), y el contenedor gonde se guarda la librería estándar (IntStdLib).
// Programa Hola, Mundo
object HolaMundo : ConsoleApplication
method + doIt()
{
System.console.write( "¡Hola, Mundo!" );
System.console.lf();
return;
}
endObject
De todos los contenedores anteriores, el contenedor Exe es el único que no es persistente. Esto quiere decir que los objetos situados en él no van a ser guardados para posteriores ejecuciones, por lo que cualquier programa puede ser ejecutado sin temer llenar el almacenamiento persistentes de objetos sin utilidad.
Es posible ejecutar la máquina virtual Zero sin soporte para persistencia. Esto se hace con la opción nops.$ zvm -nops HolaMundoDe esta manera todos los contenedores son no persistentes, por lo que, incluso si un programa emplea objetos persistentes, se encontrará sin ellos, y si los guarda, no se podrán encontrar para la siguiente ejecución.
Así, si se quiere que un objeto sea guardado para posteriores ejecuciones (es decir, hacerlo persistente), es necesario guardarlo en un contenedor que ofrezca persistencia. Sería posible guardarlo en el mismo psRoot, pero es recomendable crear subcontenedores de manera que se pueda organizar el espacio en lugar de llenar la raíz persistente de objetos sin relación entre sí.
Recuperemos el objeto Punto, que ya hizo su primera aparición en la introducción a Zero.
object Punto
attribute + x = 0;
attribute + y = 0;
method + set(a, b)
{
x = a;
y = b;
return;
}
method + toString()
{
reference toret = x.toString();
toret = toret.concat( ", " );
toret = toret.concat( y.toString() );
return toret;
}
endObject
Es posible escribir dos programas con este objeto: uno que lo guarde, y otro que lo recupere. Al fin y al cabo, es de lo que se trata, de que el punto persista. Para guardar el objeto, debemos crear, por lo explicado antes, un contenedor:
object GuardaPunto: ConsoleApplication
method + doIt()
{
reference p = Punto.createChild( "p" );
reference c = ContainerInstance.copy( "PruebaPunto" );
// Rellenar el punto
p.set( 100, 150 );
// Hacerlo persistente
c.add( p );
psRoot.add( c );
return;
}
endObject
Así, se crea un contenedor llamado PruebaPunto, donde se guardará una instancia del punto llamada p. Como la instancia tiene un atributo parent que señala a Punto, este también será guardado. Otro programa será necesario para poder recuperarlo.
object RecuperaPunto: ConsoleApplication
method + doIt()
{
System.console.writeLn( psRoot.PruebaPunto.p );
return;
}
endObject
El acceso al almacenamiento persistente es simple: cada contenedor se accede desde el contenedor en donde reside, ya que al fin y al cabo, sólo son objetos, unos dentro de otros. En el caso de que el contenedor no se encuentre, se lanza una excepción EObjNotFound. Esto se puede probar utilizando la opción nops de la máquina virtual:
$ zvm --nops RecuperaPunto
Uncaught exception:
RecuperaPunto threw EObjectNotFound: in
RecuperaPunto.<a_method>(): Object not found:
'psRoot.PruebaPunto.p'
TopLevel executing TopLevel.doIt() RecuperaPunto executing RecuperaPunto.doIt() SystemStack executing SystemStack.toString()
__Zero_Stack__ executing __Zero_Stack__.toString()
Es posible aprovechar esta circunstancia para conjuntar ambos programas en uno solo. En el caso de que se lance la excepción, el objeto es creado. En otro caso, se accede a él:
// Recupera el punto
object Punto
attribute + x = 0;
attribute + y = 0;
method + set(a, b)
{
x = a;
y = b;
return;
}
method + toString()
{
reference toret =
x.toString();
toret = toret.concat( ", "
);
toret = toret.concat(
y.toString() );
return
toret;
}
endObject
object RecuperaPruebaPersistenciaPunto:
ConsoleApplication
method + crea()
{
reference p =
Punto.createChild( "p" );
reference c =
ContainerInstance.copy( "PruebaPunto" );
// Rellenar el punto
p.set( 100, 150 );
// Hacerlo persistente
c.add( p );
psRoot.add( c );
return;
}
method + recupera()
{
return psRoot.PruebaPunto.p;
onException(
e ) {
System.console.writeLn( "Creando punto..." );
this.crea();
return
psRoot.PruebaPunto.p;
}
}
method + doIt()
{
System.console.writeLn(
this.recupera() );
return;
}
endObject
Un ejemplo podría ser el siguiente programa en PROWL sobre la familia Disney. Tras crear varios prototipos como Persona y Pareja, se crean varios objetos descendientes de estos, como se ve a continuación:
reference donaldYdaisy = Pareja.copy( "" );
reference donald = Persona.copy( "" );
reference daisy = Persona.copy( "" );
reference jorgito = Persona.copy( "" );
reference juanito = Persona.copy( "" );
reference jaimito = Persona.copy( "" );
donald.ponNombre( "Donald" );
donald.ponEmail( "donald@disney.com" );
daisy.ponNombre( "Daisy" );
daisy.ponEmail( "daisy@disney.com" );
jorgito.ponNombre( "Jorgito" );
jaimito.ponNombre( "Jaimito" );
juanito.ponNombre( "Juanito" );
donaldYdaisy.ponPareja( donald, daisy );
donaldYdaisy.ponDependiente( jorgito );
donaldYdaisy.ponDependiente( jaimito );
donaldYdaisy.ponDependiente( juanito);
mascotas.add( "Pluto" );
mascotas.add( "Goofy" );
donaldYdaisy.ponDependiente( mascotas );
reference disney = ContainerInstance.copy( "Disney" );
disney.addRenamedObject( "donaldYdaisy", donaldYdaisy );
psRoot.add( disney );
object RecuperaFamiliaDisney: ConsoleApplication
method + doIt()
{
this.prepare();
System.console.write( "Recuperando info persistente:\n" );
System.console.lf();
System.console.write( psRoot.Disney.donaldYdaisy );
System.console.lf();
return;
}
endObject
$ ./zvm GuardaFamiliaDisney
Preparado para guardar...
Pareja formada por: Donald: donald@disney.com, y Daisy: daisy@disney.com
Dependientes: Jaimito, Juanito, Pluto, Goofy.
Guardado de manera persistente
$ ./zvm RecuperaFamiliaDisneyTal y como se comentaba anteriormente, la máquina virtual Zero puede trabajar, de manera transparente, sin almacenamiento persistente. De esta manera, todos los contenedores son no persistentes, y ningun objeto es guardado. Así:
Recuperando info persistente:
Pareja formada por: Donald: donald@disney.com, y Daisy: daisy@disney.com
Dependientes: Jaimito, Juanito, Pluto, Goofy.
$ ./zvm --nops RecuperaFamiliaDisney
Recuperando info persistente:Así, si se elimina el almacenamiento persistente (basta borrar por completo el subdirectorio ZeroPS):
Uncaught exception:
RecuperaFamiliaDisney threw EObjectNotFound: in
RecuperaFamiliaDisney.<a_method>(): Object not found:
'psRoot.Disney.donaldYdaisy'
TopLevel executing TopLevel.doIt() RecuperaFamiliaDisney executing RecuperaFamiliaDisney.doIt() SystemStack executing SystemStack.toString()
__Zero_Stack__ executing __Zero_Stack__.toString()
$ ./zvm --nops GuardaFamiliaDisney
Preparado para guardar... Pareja formada por: Donald: donald@disney.com, y Daisy: daisy@disney.com Dependientes: Jaimito, Juanito, Pluto, Goofy.
Guardando... Guardado de manera persistente
$ ./zvm RecuperaFamiliaPersistente
Recuperando info persistente:
Uncaught exception:
RecuperaFamiliaDisney threw EObjectNotFound: in
RecuperaFamiliaDisney.<a_method>(): Object not found:
'psRoot.Disney.donaldYdaisy'
TopLevel executing TopLevel.doIt()
RecuperaFamiliaDisney executing RecuperaFamiliaDisney.doIt()
SystemStack executing SystemStack.toString()
__Zero_Stack__ executing __Zero_Stack__.toString()
El almacenamiento persistente se guarda de manera
jerárquica, representando los contenedores dentro de contenedores (por
ejemplo, Disney dentro de Root) aprovechando los directorios de espacio de
archivos:
$ cd ZeroPS
$ ls
Root.zbj
$ cd Root/
$ ls
Disney.zbj IntStdLib.zbj
Así, las instancias Persona serán objetos que deriven de este prototipo, TraitsPersona. Estas instancias, como ya se comentaba, se guardarán en un objeto derivado de Map.object TraitsPersona
attribute + nombre = "Pepito perez";
attribute + edad = 50;
attribute + telefono = "988310000";
method + putNombre(n)
{
nombre = n;
return;
}
method + putEdad(ed)
{
if ( ed isInstanceOf Int ) {
edad = ed;
} else {
throw ETypeMismatch, "edad debe ser un entero";
}
return;
}
method + putTelefono(tlf)
{
telefono = tlf;
return;
}
method + toString()
{
reference toret = nombre;
toret = toret.concat( ": edad " );
toret = toret.concat( edad.toString() );
toret = toret.concat( ", tlf: " );
toret = toret.concat( telefono );
return toret;
}
endObject
object Persona: TraitsPersona
endObject
Para crear una nueva persona, será necesario copiar el prototipo Persona, rellenar los campos, y guardarlos en la estructura de la agenda:object Agenda: Map
endObject
object AplicacionAgenda
//...
method + masPersonas()
{
// ...
toret = Persona.copy( "" );
toret.putNombre( nombre );
toret.putEdad( edad );
toret.putTelefono( telefono );
agenda.add( nombre, toret );
// ...
}
endObject
object AplicacionAgenda
// ...
method + hazPersistente()
/*
Solamente es necesario crear el contenedor la primera vez
*/
{
agenda = psRoot.InfoAplicacionAgenda.Agenda;
System.console.write( "\nArrancando del container ...\n" );
return;
onException( e ) {
System.console.write( "\nArrancando sin datos ...\n" );
// Crearlo: no fue encontrado
reference cnt = Container.createChild( "InfoAplicacionAgenda" );
cnt.add( agenda );
cnt.add( TraitsPersona );
cnt.add( Persona );
psRoot.add( cnt );
}
// ...
endObject