Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > cd14cddf3b3ceaf1193157472227757a > files > 499

parrot-doc-1.6.0-1mdv2010.0.i586.rpm

# Copyright (C) 2001-2007, Parrot Foundation.
# $Id: vtables.pod 40936 2009-09-03 00:21:51Z chromatic $

=head1 NAME

docs/vtables.pod - Parrot Vtables

=head1 Implementing Variable Types with Vtables

This is a guide to creating your own PMC (Polymorphic Container) classes.  It
tells you what you need to write in order to add new variable types to Parrot.

=head2 Overview

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 C<$a> being C<b0>. This is because of the magic of the Perl
increment operator. In Python, on the other hand, you'd get a runtime error.

=over 3

=item *

To be perfectly honest, this is a slightly flawed example, since it's
unlikely that there will be a distinct "Python scalar" PMC class.  The
Python compiler could well infer variables by their type such that C<a>
would be a C<PythonString> and C<b> would be a C<PythonNumber>.  But the
point remains - incrementing a C<PythonString> is very different from
incrementing a C<PerlScalar>.

=back

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:

=over 3

=item 1.

Construct a new PMC in the PerlScalar class.

=item 2.

Call a method setting its string value to C<"a9">.

=item 3.

Call a method to tell it to increment itself.

=back

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.

=head2 Starting out

If you're adding data types to the core of Parrot, you should be creating a
file in the F<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 F<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 PMC file (to be preprocessed into ordinary C
code by the F<tools/build/pmc2c.pl> program) with stubs for all the methods
you need to fill in. Actually, there are more stubs here then you probably
I<need> to fill in. Your PMC isn't going to want to support all these
methods, and in many cases you may want to fall back to default behavior
instead of implementing a dummy method.> The function C<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 non-core
PMC, called a "dynpmc", you need to add the argument C<dynpmc> to the line
that starts with C<pmclass>. Here's an example:

	pmclass FooByNumber dynpmc {
		...

This alerts the PMC compiler that the PMC type should not be hard-coded into
Parrot, and that the PMC definition needs to be loaded in to Parrot
dynamically when the user requires it.

To finish up adding a built-in class:

=over 4

=item 1.

Add src/pmc/YOURCLASS.pmc to the MANIFEST.

=item 2.

Run C<make realclean>, and then run F<Configure.pl> to add your new PMC to
the set of built-in PMCs.

=back

=head2 What You Can and Cannot Do

The usual way to continue from the F<tools/dev/gen_class.pl>-generated
skeleton is to define a structure that will hook onto the C<data>, if your
data type needs to use that, and then also define some user-defined flags.

Flags are accessed by C<< 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
C<Pobj_private0_FLAG> .. C<Pobj_private7_FLAG>.

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,
        ....
    };

To manipulate the flags, use the macros listed in F<pobj.h>.

PMCs also have the ability to store an arbitrary number of user-defined
attribute values using the C<ATTR> keyword. 

=head2 Multimethods

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 C<set_integer> method has multiple forms; the default
C<set_integer> means that you are being called with a PMC, and you should
probably use the C<get_integer> method of the PMC to find its integer value;
C<set_integer_native> means you're being passed an C<INTVAL>. The final form is
slightly special; if the interpreter calls C<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, (C<native> and C<same>) and floating point
numbers.

=head2 Implementing VTABLE Interfaces

The master list of VTABLE interfaces can be found in F<src/vtable.tbl> in
the root directory of the Parrot source, with documentation in
F<docs/pdds/pdd17_pmc.pod>. A few of these are very important, for
instance:

=over 3

=item C<type>

Return the enumeration value of your class.

=item C<name>

Return a string containing your class name.

=item C<init>

Initialization.  Parrot makes exactly one call to either C<init> or
C<init_pmc> at PMC construction time.

=item C<init_pmc>

Alternative entry point for initialization that takes a PMC argument.
Parrot makes exactly one call to either C<init> or C<init_pmc> at PMC
construction time.

NOTE: It is strongly suggested that C<init_pmc(PMCNULL)> be equivalent to
C<init()>.

=item C<is_equal>

True if the passed-in PMC has the same B<value> as you. For instance, a Perl
integer and a Python integer could have the same value, but could not be the
same thing as defined by C<is_same>.

=item C<clone>

Copy your data and state into the passed-in destination PMC.

=back

Others are methods you may or may not need, depending on your type:

=over 3

=item C<morph>

Turn yourself into the specified type.

=item C<destroy>

Do any data shut-down and finalization you need to do. To have this method
called, you must set the C<Pobj_custom_destroy_FLAG>.

=item C<get_integer>

Return an integer representation of yourself.

=item C<get_number>

Return a floating-point representation of yourself.

=item C<get_string>

Return a string representation of yourself (a STRING* object), this should be a
B<copy> of whatever string you are holding, not just a pointer to your own
string so that anything that calls this method can happily modify this value
without making a mess of your guts.

=item C<get_bool>

Return a boolean representation of yourself.

=item C<get_value>

Return your private data as a raw pointer.

=item C<is_same>

True if the passed-in PMC refers to exactly the same B<data> as you. (Contrast
C<is_equal>)

=item C<set_integer>

Set yourself to the passed-in integer value. This is an integer multimethod.

=item C<set_number>

Set yourself to the passed-in float value. This is a floating-point
multimethod.

=item C<set_string>

Set yourself to the passed-in string. This is a string multimethod.

=item C<add>

Fetch the number part of C<value> and add your numeric value to it, storing the
result in C<dest>. (Probably by calling its C<set_integer> or C<set_number>
method) This is a numeric multimethod.

=item C<subtract>

Fetch the number part of C<value> and subtract your numeric value from it,
storing the result in C<dest>. (Probably by calling its C<set_integer> or
C<set_number> method) This is a numeric multimethod.

=item C<multiply>

=item C<divide>

=item C<modulus>

You get the picture.

=item C<concatenate>

Fetch the string part of C<value> and concatenate it to yourself, storing the
result in C<dest>. (Probably by calling its C<set_string> method) This is a
string multimethod.

=item C<logical_or>

=item C<logical_and>

Perform the given short-circuiting logical operations between your boolean
value and the value passed in, storing the result in C<dest>.

=item C<logical_not>

Set yourself to be a logical negation of the value passed in.

=item C<repeat>

Repeat your string representation C<value> times and store the result in
C<dest>.

=back

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 the missing method ever gets 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 C<extends SUPERCLASS> phrase. For example:

  pmclass PackedArray extends Array { ...

See the POD documentation in F<tools/build/pmc2c.pl> for a list of useful
keywords that you may use in the .pmc file.

=cut