parrotcode: Parrot Vtables | |
Contents | Documentation |
docs/vtables.pod - Parrot Vtables
This is a guide to creating your own PMC (Parrot Magic Cookie) classes. It tells you what you need to write in order to add new variable types to Parrot.
The guts of the Parrot interpreter are by design ignorant (or, if you want to be less disparaging, agnostic) of the intricacies of variable type behavior. The standard example is the difference between Perl scalars and Python scalars. In Perl, if you have
$a = "a9";
$a++;
you end up with $a
being b0
. This is because of the magic of the Perl increment operator. In Python, on the other hand, you'd get a runtime error.
a
would be a PythonString
and b
would be a PythonNumber
. But the point remains - incrementing a PythonString
is very different from incrementing a PerlScalar
.Since the behavior is a function of the "type" of the PMC, it's natural to consider the various different types of PMC as classes in an object-oriented system. The Parrot interpreter calls methods on the individual PMC objects to manipulate them. So the example above would translate to something like:
"a9"
.And if you replace PerlScalar with PythonString, you get different behavior but to the fundamental guts of the interpreter, the instructions are the same. PMCs are an abstract virtual class; the interpreter calls a method, the PMC object does the right thing, and the interpreter shouldn't have to care particularly what that right thing happens to be.
Hence, adding a new data type to Parrot is a question of providing methods which implement that data type's expected behavior. Let's now look at how one is supposed to do this.
If you're adding data types to the core of Parrot, (and you've checked with Chip that you're supposed to be doing so) you should be creating a file in the src/pmc/ subdirectory; this is where all the built-in PMC classes live. (And a good source of examples to plunder even if you're not writing a core data type.)
You should almost always start by running tools/dev/gen_class.pl to generate a skeleton for the class. Let's generate a number type for the beautifully non-existent Fooby language:
% perl tools/dev/gen_class.pl FoobyNumber > src/pmc/foobynumber.pmc
This will produce a skeleton C file (to be preprocessed by the tools/build/pmc2c.pl program) with stubs for all the methods you need to fill in. The function init
allows you to set up anything you need to set up.
Now you'll have to do something a little different depending on whether you're writing a built-in class or an extension class. If you're writing a built-in class, then you'll see a reference to enum_class_FoobyNumber
in the init
function. For built-in classes, this is automatically defined in pmc.h when you run Configure.pl. If you're not writing a built-in class, you need to indicate this by using the 'extension' keyword after the 'pmclass YOURCLASS' declaration in src/pmc/YOURCLASS.pmc. Then, change the type of the init
function to return struct _vtable
, and then return temp_base_vtable
instead of assigning to the interpreter specific vtables
array.
To finish up adding a built-in class:
The usual way to continue from the tools/dev/gen_class.pl-generated skeleton is to define a structure that will hook onto the data
, if your data type needs to use that, and then also define some user-defined flags.
Flags are accessed by pmc->flags
. Most of the bits in the flag word are reserved for use by parrot itself, but a number of them have been assigned for general use by individual classes. These are referred to as Pobj_private0_FLAG
.. Pobj_private7_FLAG
. (The '7' may change during the early development of parrot, but will become pretty fixed at some point.)
Normally, you will want to alias these generic bit names to something more meaningful within your class:
enum {
Foobynumber_is_bignum = Pobj_private0_FLAG,
Foobynumber_is_bigint = Pobj_private1_FLAG,
....
};
You're quite at liberty to declare these in a separate header file, but I find it more convenient to keep everything together in foobynumber.c. To manipulate the flags, use the macros listed in pobj.h.
You may also use the cache
union in the PMC structure to remove some extraneous dereferences in your code if that would help.
One slightly (potentially) tricky element of implementing vtables is that several of the vtable functions have variant forms depending on the type of data that they're being called with.
For instance, the set_integer
method has multiple forms; the default set_integer
means that you are being called with a PMC, and you should probably use the get_integer
method of the PMC to find its integer value; set_integer_native
means you're being passed an INTVAL
. The final form is slightly special; if the interpreter calls set_integer_same
, you know that the PMC that you are being passed is of the same type as you. Hence, you can break the class abstraction to save a couple of dereferences - if you want to.
Similar shortcuts exist for strings, (native
and same
) and floating point numbers.
The master list of vtable methods can be found in vtable.tbl in the root directory of the Parrot source, with documentation in docs/pdds/pdd02_vtables.pod. A few of these are very important, for instance:
type
name
init
init
or init_pmc
at PMC construction time.init_pmc
init
or init_pmc
at PMC construction time.init_pmc(PMCNULL)
be equivalent to init()
.is_equal
is_same
.clone
Others are methods you may or may not need, depending on your type:
morph
destroy
Pobj_active_destroy_FLAG
.get_integer
get_number
get_string
get_bool
get_value
is_same
is_equal
)set_integer
set_number
set_string
set_value
add
value
and add your numeric value to it, storing the result in dest
. (Probably by calling its set_integer
or set_number
method) This is a numeric multimethod.subtract
value
and subtract your numeric value from it, storing the result in dest
. (Probably by calling its set_integer
or set_number
method) This is a numeric multimethod.multiply
divide
modulus
concatenate
value
and concatenate it to yourself, storing the result in dest
. (Probably by calling its set_string
method) This is a string multimethod.logical_or
logical_and
dest
.logical_not
repeat
value
times and store the result in dest
.If any method doesn't fit into your class, just don't implement it and don't provide an empty function body. The default class, which all classes inherit from, will throw an exception, if this method would be called.
If your class is a modification of an existing class, you may wish to use inheritance. At the beginning of your vtable specification in src/pmc/YOURCLASS.pmc, add the extends SUPERCLASS
phrase. For example:
pmclass PackedArray extends Array { ...
See the POD documentation in tools/build/pmc2c.pl for a list of useful keywords that you may use in the .pmc file. (Run perldoc -F pmc2c.pl
to view the POD.)
|