Práctica: El sistema de programación Zero

    Zero es una máquina virtual orientada a objetos y basada en prototipos. Por ahora, no se dispone de un lenguaje, si bien es programable mediante un macrosensamblador, que proporciona casi todos los elementos necesarios presentes en un lenguaje. Necesariamente, el tipo de programación no se corresponde con la de C++, que es un lenguaje de programación híbrido, que permite tanto programación tradicional, dividida en rutinas, como programación orientada a objetos. 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 macroensamblador, 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.

Hay disponible un lenguaje de alto nivel llamado Prowl, un macroensamblador y un ensamblador (en orden de mayor nivel de abstracción a un menor nivel) que generan código a ser consumido por la máquina virtual.

Bibliografía y links de la práctica

    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.

Conceptos necesarios para entender la práctica

Texto de la práctica

      El programa/objeto "¡Hola, Mundo!" en Prowl (guardado como HolaMundo.pwl):

    // Programa Hola, Mundo
     object HolaMundo : ConsoleApplication
        method + doIt()
        {
           System.console.write( "¡Hola, Mundo!" );
           System.console.lf();
           return;
        }
     endObject

Por consola

Compilación:

    Ábrase una consola en el directorio Zero creado con la instalación, y tecléese:

    $ prowl HolaMundo.pwl
    $ za HolaMundo

El archivo debe llamarse de la misma forma que el objeto principal que será ejecutado (en este ejemplo sólo hay un objeto, pero el objeto principal será el que contiene el método doIt()).

La primera orden, con el programa prowl, se encarga de traducir el código fuente a código ensamblador válido de la máquina virtual Zero, listo para ser consumido por za, estando ya preparado el código objeto o bytecode preparado para la ejecución.

Ejecución:

   $ zvm HolaMundo

En este caso se trata de un objeto que deriva de ConsoleApplication, por lo que la máquina virtual buscará automáticamente el método doIt(). Si no deriva de ese objeto, entonces deberá especificarse el método correcto. También es posible, de ser necesario, especificar referencias a objetos como argumentos.

Mediante el editor SciTE

    La forma más sencilla de programar con PROWL para Zero es utilizar el editor SciTE, ya preparado para tratar con programas PROWL, J--, Macroensamblador y Ensamblador Zero (estos dos últimos de mucho más bajo nivel).
    Para ello es necesario haberse bajado el paquete SciTE apropiado para cada sistema. Una vez hecho ésto, entre en la carpeta donde está instalado el sistema Zero, y abra a su vez la carpeta correspondiente a SciTE. Una vez allí, ejecute scite.exe ó scite.sh.
    Una vez hecho ésto, sólo es necesario escoger la opción open (abrir) de SciTE, escoger (o escribir, mediante la opción new (nuevo)), el archivo HolaMundo.pwl, y compilarlo con Tools >> Compile, y ejecutarlo con Tools >> Go.

Información básica sobre programación en Zero

Creación de objetos


object Punto
  attribute + x = 0;
  attribute + y = 0;

  method + ponCoordenadas(a, b)
  {
     x = a;
     y = b;
     return;
  }
endObject


object PruebaPunto : ConsoleApplication
   method + doIt()
   {
      reference miPunto = Punto.copy( "" );
      
      miPunto.ponCoordenadas( 100, 150 );
      return;
   }
endObject

Ejercicios

  1. Tal y como está, el anterior ejemplo no realizará ninguna tarea. Visualiza el punto recién creado por consola.
  2. Lo que sale, sin embargo, es un identificador de la instancia del prototipo Punto, y no la información de la del objeto que se busca. Cambia el parámetro de copy() para que el identificador sea más amigable.
  3. El método que se está ejecutando es el toString() de Object. Este método debe ser sobreescrito en Punto, para que visualice las coordenadas.
  4. Renombra el prototipo Punto como TraitsPunto, y crea un nuevo objeto Punto con sólo los atributos x e y. Observarás que el código funciona igualmente ... ¿qué es lo que ha variado?

Un ejemplo con una jerarquía de prototipos


   // Implementa un ejemplo de polimorfismo
   // "Mamifero" deriva de "Object", pero eso ya se asume por defecto
   object Mamifero
       attribute + numPatas = 4;
       attribute + color = "negro";

       method + esOmnivoro()
       {
         return False;
       }

       method + esSoloCarnivoro()
       {
         return False;
       }

       method + esSoloVegetariano()
       {
         return False;
       }

       method + devColor()
       {
         return color;
       }

       method + devNumPatas()
       {
         return numPatas;
       }
   endObject // Mamifero

   object Vaca : Mamifero
       attribute + color = "marrón";

       method + esSoloVegetariano()
       {
         return True;
       }
   endObject // Vaca

   object Ornitorrinco : Mamifero

       method + esOmnivoro()
       {
         return True;
       }
   endObject // Ornitorrinco

   object PruebaMamiferos : ConsoleApplication

       method + alimentacion(x)
       {
         if ( x isInstanceOf Mamifero ) {
           if ( x.esOmnivoro() ) {
             System.console.write( x );
             System.console.write( " es omnivoro" );
           } else {
             System.console.write( x );
             System.console.write(", no es omnivoro");
           }
         } else {
           System.console.write( x );
           System.console.write( ", no es mamifero" );
         }

         System.console.lf();
         return;
       }

       method + doIt()
       {
         this.alimentacion( Vaca );
         this.alimentacion( Ornitorrinco );
         this.alimentacion( 7789 );
         this.alimentacion( "¡Hola!" );

         return;
       }

   endObject // PruebaMamiferos

Ejercicios

  1. Lee la documentación sobre Prowl y sobre el macroensamblador, y también sobre el ensamblador de Zero. ¿Para qué sirven __this, parent, __gp1, isInstanceOf?. Como ves, en Prowl tienen sus derivados, en algunos casos, o han desaparecido, en otros. ¿Cómo se gestionan las excepciones?

  2. Modifica el ejemplo anterior, añadiendo atributos y añadiendo más objetos derivados de Mamifero, y haciendo que calcule el peso total de un vector de objetos Mamifero (tendrás que leer la documentación sobre la librería estándar).

  3. Siguiendo el ejercicio de polimorfismo de las figuras, rectángulos y círculos, y basándote en el ejemplo superior, realiza un programa que calcule el área de al menos un rectángulo y un círculo, seleccionable por el usuario. Crea métodos que pidan los datos de las figuras por teclado.

  4. Trata de aplicar la posibilidad de la herencia dinámica en algún ejemplo, por ejemplo, en el de los mamíferos, con el ornitorrinco. Es posible que la herencia dinámica ayude a modelar el hecho de que el ornitorrinco puede moverse por el agua y por tierra. Crea un atributo, que pueda ser modificado mediante un par de métodos, para indicar en que medio está. Un tercer método permitirá "avanzar" al ornitorrinco, sin importar el medio. (Solución)

  5. Prowl soporta programación por contrato. Crea, basándote en el vector de la librería estándar, una pila con las necesarias comprobaciones de programación por contrato (Solución).