Parrot Virtual Machine/Classes and Objects

Classes and Objects
We briefly discussed some class and object PIR code earlier, and in this chapter we are going to go into more detail about it. As we mentioned before, classes have 4 basic components: A namespace, an initializer, a constructor, and methods. A namespace is important because it tells the virtual machine where to look for the methods of the object when they are called. If I have an object of class "Foo", and I call the "Bar" method on it:

.local pmc myobject myobject = new "Foo" myobject.'Bar'

The virtual machine will see that  is a PMC object of type Foo, and then will look for the method 'Bar' in the namespace 'Foo'. In short, the namespace helps to keep everything together.

Initializers
An initializer is a function that is called at the beginning of the program to set up the class. PIR doesn't have a syntax for declaring information about the class directly, you have to use a series of opcodes and statements to tell Parrot what your class looks like. This means that you need to create the various data fields in your class (called "attributes" here), and set up relationships with other classes.

Initializer functions tend to follow this format:

.namespace

.sub 'onload' :anon :init :load .end

The  flag means that the name of the function will not be stored in the namespace, so you don't end up with all sorts of name pollution. Of course, if the name of the function isn't stored, it can be difficult to make additional calls to this function, although that doesn't matter if we only want to call it once. The  flag causes the function to run as soon as parrot initializes the file, and the   flag causes the function to run as soon as the file is loaded, if it is loaded as an external library. In short: We want this function to run as soon as possible and we only want it to run once.

Notice also that we want the initializer to be declared in the HLL namespace.

Making a New Class
We can make a new class with the keyword. To create a class called "MyClass", we would write an initializer that does the following:

.sub 'initmyclass' :init :load :anon newclass $P0, 'MyClass' .end Also, we can simplify this using PIR syntax:

.sub 'initmyclass' :init :load :anon $P0 = newclass 'MyClass .end

In the initializer, the register $P0 contains a reference to the class object. Any changes or additions that we want to make to the class need to be made to this class reference variable.

Creating new class objects
Once we have a class object, the output of the newclass opcode, we can create or "instantiate" objects of that class. We do this with the new keyword:

.local PMC myobject myobject = new $P0

Or, if we know the name of the class, we can write:

.local PMC myobject myobject = new 'MyClass'

Subclassing
We can set up a subclass/superclass relationship using the  command. For instance, if we want to create a class that is a subclass of the builtin PMC type "ResizablePMCArray", and if we want to call this subclass "List", we would write:

.sub 'onload' :anon :load :init subclass $P0, "ResizablePMCArray", "List" .end

This creates a class called "List" which is a subclass of the "ResizablePMCArray" class. Notice that like the  instruction above, we store a reference to the class in the PMC register $P0. We'll use this reference to modify the class in the sections below.

Adding Attributes
Attributes can be added to the class by using the  keyword with the class reference that we received from the   or   keywords. Here, we create a new class 'MyClass', and add two data fields to it: 'name' and 'value':

.sub 'initmyclass' :init :load :anon newclass $P0, 'MyClass' add_attribute $P0, 'name' add_attribute $P0, 'value' .end

We'll talk about accessing these attributes below.

Methods
Methods, as we mentioned earlier, have three major differences from subroutines: The way they are flagged, the way they are called, and the fact that they have a special  variable. We know already that methods should use the  flag. indicates to Parrot that the other two differences (dot-based calling convention and "self" variable) need to be implemented for the method. Some methods will also use the  flag as well, and we will discuss that below.

We want to create a class for a stack class. The stack has "push" and "pop" methods. Luckily, Parrot has  and   instructions available that can operate on array-like PMCs (like the "ResizablePMCArray" PMC class). However, we need to wrap these PIR instructions into functions or methods so that they can be used from our high-level language (HLL). Here is how we can do that:

.namespace

.sub 'onload' :anon :load :init subclass $P0, "ResizeablePMCArray", "Stack" .end

.namespace ["Stack"]

.sub 'push' :method .param pmc arg push self, arg .end

.sub 'pop' :method pop $P0, self .return($P0) .end

Now, if we had a language compiler for Java on Parrot, we could write something similar to this:

Stack mystack = new Stack; mystack.push(5); System.out.println(mystack.pop);

The example above would print the value "5" at the end. If we look at the same example in a language like Perl 5, we would have:

my $stack = Stack::new; $stack->push(5); print $stack->pop;

This, again, would print out the number "5".

Accessing Attributes
If our class has attributes, we can use the  and   instructions to write and read those attributes, respectively. If we have a class 'MyClass' with data attributes 'name' and 'value', we can write accessors and setter methods for these:

.sub 'set_name' :method .param pmc newname $S0 = 'name' setattribute self, $S0, newname .end

.sub 'set_data' :method .param pmc newdata $S0 = 'data' setattribute self, $S0, newdata .end

.sub 'get_name' :method $S0 = 'name' $P0 = getattribute self, $S0 .return($P0) .end

.sub 'get_value' :method $S0 = 'value' $P0 = getattribute self, $S0 .return($P0) .end

Constructors
The constructor is the function that we call when we use the  keyword. The constructor initializes the data object attributes, and maybe performs some other bookkeeping tasks as well. A constructor must be a method named 'new'. Besides the special name, the constructor is like any other method, and can get or set attributes on the  variable as needed.

Resources

 * http://www.parrotcode.org/docs/pdd/pdd15_objects.html
 * http://www.parrotcode.org/docs/pdd/pdd21_namespaces.html