#!/usr/cogent/bin/gamma

/*
 * Gamma implements Object Oriented Programming features which provide a
 * single_inheritance class mechanism with instance variables and
 * methods.  Since Gamma is an interpreter, the object definitions are
 * truly dynamic, allowing for run_time extensibility.  This example
 * provides the simplest of starting points to this key software
 * methodology.
 * 
 * This file is best viewed with a tab width of 4.
 *
 * Demonstrates:
 *		class definitions: attributes and methods.
 *		constructors and destructors
 *		method overloading
 */

/*
 * Define a class of animal, with no default type and a default of 4 legs
 */
class animal
{
	type = "animal";
	num_legs = 4;		// By default, animals have 4 legs
}

/*
 * The constructor for an animal is called when any instance of animal
 * or a subclass of animal is created using a call to 'new'.  Constructors
 * have no arguments.
 */
method animal.constructor()
{
	princ ("A ", class_name(class_of(self)), " is born\n");
}

/*
 * The destructor for an animal is called when any instance of animal
 * or a subclass of animal is deleted by the garbage collector.  There
 * is no explicit deletion mechanism in Gamma.  Destructors have no
 * arguments.
 */
method animal.destructor ()
{
	princ ("A ", class_name(class_of(self)), " dies\n");
}

/*
 * All methods except constructor and destructor are overloaded, meaning
 * that only the method for the nearest class in the ancestry of the
 * instance will be called for any given method name.
 */
method animal.describe ()
{
	princ ("The ", self.type, " has ", self.num_legs, " legs.\n");
}

/*
 * Create a subclass of animal of a particular type.
 */

class cat animal
{
	type = "feline";
}

/*
 * Create another subclass of animal which is itself a superclass
 */

class insect animal
{
	num_wings = 2;
	num_legs = 6;
}

/*
 * Overload the description method for insects so we hear about
 * wings and legs when we ask about insects.
 */

method insect.describe ()
{
	/*
	 * We can explicitly call a method of a superclass using the
	 * 'call' function and naming a superclass.
	 */
	call (self, #animal, #describe);
	princ ("    (oh, and ", self.num_wings, " wings)\n");
}

/*
 * Create a destructor for an insect.  This will be run before the
 * animal destructor.
 */

method insect.destructor ()
{
	princ ("Crunch. ");
}

/*
 * Create a subclass of an insect which is a particular type.
 */

class beetle insect
{
	type = "rhinoceros beetle";
	num_wings = 4;
}

function main ()
{
	local		pet = new (cat);
	local		bug = new (beetle);

	/*
	 * cat gets its describe method from the animal class
	 */
	pet.describe();

	/*
	 * beetle gets its describe method from the insect class
	 */
	bug.describe();

	/*
	 * Since the destructor will be implicitely called by the garbage
	 * collector, we can cause the destructor to occur by removing
	 * all references to the instances (set the variables referencing
	 * the instances to nil), and then explicitly invoke the garbage
	 * collector.  Typically this is not necessary, as the garbage
	 * collector will run when necessary.
	 */
	pet = nil;
	bug = nil;

	gc();
}
