Many of Parrot's core classes -- such as
ResizablePMCArray -- are written in C,
but you can also write your own classes in PIR.
PIR doesn't have the shiny syntax of high-level object-oriented languages,
but it provides the necessary features to construct well-behaved objects every bit as powerful as those of high-level object systems.
Parrot developers often use the word "PMCs" to refer to the objects defined in C classes and "objects" to refer to the objects defined in PIR. In truth, all PMCs are objects and all objects are PMCs, so the distinction is a community tradition with no official meaning.
newclass opcode defines a new class.
It takes a single argument,
the name of the class to define.
$P0 = newclass 'Foo'
Just as with Parrot's core classes,
new opcode instantiates a new object of a named class.
$P1 = new 'Foo'
In addition to a string name for the class,
new can also instantiate an object from a class object or from a keyed namespace name.
$P0 = newclass 'Foo' $P1 = new $P0 $P2 = new ['Bar';'Baz']
addattribute opcode defines a named attribute -- or instance variable -- in the class:
$P0 = newclass 'Foo' addattribute $P0, 'bar'
setattribute opcode sets the value of a declared attribute.
You must declare an attribute before you may set it.
The value of an attribute is always a PMC,
never an integer,
or string.Though it can be an
$P6 = box 42 setattribute $P1, 'bar', $P6
getattribute opcode fetches the value of a named attribute.
It takes an object and an attribute name as arguments and returns the attribute PMC:
$P10 = getattribute $P1, 'bar'
Because PMCs are containers,
you may modify an object's attribute by retrieving the attribute PMC and modifying its value.
You don't need to call
setattribute for the change to stick:
$P10 = getattribute $P1, 'bar' $P10 = 5
With a created class,
we can use the
new opcode to instantiate an object of that class in the same way we can instantiate a new PMC.
$P0 = newclass "Foo" $P1 = new $P0
Or, if we don't have the class object handy, we can do it by name too:
$P1 = new "Foo"
PMCs have two VTABLE interface functions for dealing with instantiating a new object:
The former is called when a new PMC is created,
the later is called when a new PMC is created with an initialization argument.
In order to iterate over the init_pmc args, you must also provide a get_iter method.
.namespace ["Foo"] .sub 'init' :vtable say "Creating a new Foo" .end .sub 'init_pmc' :vtable .param pmc args print "Creating a new Foo with argument " say args .end .sub 'get_iter' :vtable .param pmc self .local pmc args, iter args = new ['FixedPMCArray'], 1 iter = new ['ArrayIterator'], args .return(iter) .end .namespace .sub 'main' :main $P0 = newclass 'Foo' # instantiate class $P1 = new ['Foo'] # init object $P2 = new ['Foo'], $P1 # init_pmc .end
Methods in PIR are subroutines stored in the class object.
Define a method with the
.sub directive and the
.sub half :method $P0 = getattribute self, 'bar' $P1 = $P0 / 2 .return($P1) .end
This method returns the integer value of the
bar attribute of the object divided by two.
Notice that the code never declares the named variable
Methods always make the invocant object -- the object on which the method was invoked -- available in a local variable called
:method modifier adds the subroutine to the class object associated with the currently selected namespace,
so every class definition file must contain a
Class files for languages may also contain an
.HLL declaration to associate the namespace with the appropriate high-level language:
.HLL 'php' .namespace [ 'Foo' ]
Method calls in PIR use a period (
.) to separate the object from the method name.
The method name is either a literal string in quotes or a string variable.
The method call looks up the method in the invocant object using the string name:
$P0 = $P1.'half'() $S2 = 'double' $P0 = $P1.$S2()
You can also pass a method object to the method call instead of looking it up by string name:
$P2 = get_global 'triple' $P0 = $P1.$P2()
Parrot always treats a PMC used in the method position as a method object,
so you can't pass a
String PMC as the method name.
Methods can have multiple arguments and multiple return values just like subroutines:
($P0, $S1) = $P2.'method'($I3, $P4)
can opcode checks whether an object has a particular method.
It returns 0 (false) or 1 (true):
$I0 = can $P3, 'add'
subclass opcode creates a new class that inherits methods and attributes from another class.
It takes two arguments: the name of the parent class and the name of the new class:
$P3 = subclass 'Foo', 'Bar'
subclass can also take a class object as the parent class instead of a class name:
$P3 = subclass $P2, 'Bar'
addparent opcode also adds a parent class to a subclass.
This is especially useful for multiple inheritance,
subclass opcode only accepts a single parent class:
$P4 = newclass 'Baz' addparent $P3, $P4 addparent $P3, $P5
To override an inherited method in the child class,
define a method with the same name in the subclass.
This example code overrides
who_am_i method to return a more meaningful name:
.namespace [ 'Bar' ] .sub 'who_am_i' :method .return( 'I am proud to be a Bar' ) .end
Object creation for subclasses is the same as for ordinary classes:
$P5 = new 'Bar'
Calls to inherited methods are just like calls to methods defined in the class:
isa opcode checks whether an object is an instance of or inherits from a particular class.
It returns 0 (false) or 1 (true):
$I0 = isa $P3, 'Foo' $I0 = isa $P3, 'Bar'
Object PMC is a core PMC written in C that provides basic object-like behavior.
Every object instantiated from a PIR class inherits a default set of vtable functions from
but you can override them with your own PIR subroutines.
:vtable modifier marks a subroutine as a vtable override.
As it does with methods,
Parrot stores vtable overrides in the class associated with the currently selected namespace:
.sub 'init' :vtable $P6 = new 'Integer' setattribute self, 'bar', $P6 .return() .end
Subroutines acting as vtable overrides must either have the name of an actual vtable function or include the vtable function name in the
.sub foozle :vtable('init') # ... .end
You must call methods on objects explicitly,
but Parrot calls vtable functions implicitly in multiple contexts.
creating a new object with
$P3 = new 'Foo' will call
init with the new
As an example of some of the common vtable overrides,
= operator (or
set opcode) calls
Foo's vtable function
set_integer_native when its left-hand side is a
Foo object and the argument is an integer literal or integer variable:
$P3 = 30
+ operator (or
add opcode) calls
add vtable function when it adds two
$P3 = new 'Foo' $P3 = 3 $P4 = new 'Foo' $P4 = 1774 $P5 = $P3 + $P4 # or: add $P5, $P3, $P4
inc opcode calls
increment vtable function when it increments a
get_string vtable functions to retrieve an integer or string value from a
$I10 = $P5 # get_integer say $P5 # get_string
Classes defined in PIR using the
newclass opcode are instances of the
This PMC contains all the meta-information for the class,
such as attribute definitions,
and its inheritance hierarchy.
inspect provides a way to peek behind the curtain of encapsulation to see what makes a class tick.
When called with no arguments,
inspect returns an associative array containing data on all characteristics of the class that it chooses to reveal:
$P1 = inspect $P0 $P2 = $P1['attributes']
When called with a string argument,
inspect only returns the data for a specific characteristic of the class:
$P0 = inspect $P1, 'parents'
Table 7-1 shows the introspection characteristics supported by