=head1 TITLE

Parrot Vtables

=head1 Implementing Variable Types with 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.

=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 behaviour. The standard example is the difference between Perl
scalars and Python scalars. In Perl, if you have

    $a = "1a";
    $a++;

you end up with C<$a> being C<1b>. This is a factor of the way Perl
scalars increment themselves. In Python, on the other hand, you'd get a
runtime error. 

=over 3

=item *

To be perfect 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 type-inference variables 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 behaviour 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 whereby the 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<"a1">.

=item 3.

Call a method to tell it to increment itself.

=back

And if you replace PerlScalar with PythonString, you get different
behaviour 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 behaviour. 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, (and you've
checked with Dan and/or Simon that you're supposed to be doing so)
you should be creating a file in the F<classes/> 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<genclass.pl> found in the
F<classes/> subdirectory to generate a skeleton for the class. Let's
generate a number type for the beautifully non-existant Fooby language:

    perl -I../lib genclass.pl FoobyNumber > foobynumber.pmc

This will produce a skeleton C file (to be preprocessed by the
F<pmc2c.pl> program) with stubs for all the methods you need to fill in;
the final function in the file, C<Parrot_FoobyNumber_init>, allows you
to set up anything you need to set up, and creates the vtable structure
containing all the methods.

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
C<enum_class_FoobyNumber> in the C<init> function. This is something
that you need to add to the C<enum> of built-in classes located in
F<pmc.h>. If you're not writing a built-in class, you need to change the
type of the C<init> function to return C<struct _vtable>, and then
return C<temp_base_vtable> instead of assigning to the
C<Parrot_base_vtables> array.

To finish up adding a built-in class:

=over 4

=item 1.

Add classes/YOURCLASS.pmc to MANIFEST.

=item 2.

Add enum_class_YOURCLASS to the enumeration in include/parrot/pmc.h.

=item 3.

Add Parrot_YOURCLASS_class_init to the list in include/parrot/global_setup.h.

=item 4.

Add a call to Parrot_YOURCLASS_class_init() to init_world() in
global_setup.c.

=item 5.

Add your PMC class to the classname hash at the end of global_setup.c.

=item 6.

Rerun perl Configure.pl (or perl Configure.pl --debugging) to
regenerate the makefiles with your new PMC.

=back

=head2 What You Can and Cannot Do

The usual way to continue from the F<genclass.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<PMC_private0_FLAG> .. C<PMC_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 = PMC_private0_FLAG,
        Foobynumber_is_bigint = PMC_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 F<foobynumber.c>.

You may also use the C<cache> union in the PMC structure to remove
some extraneous dereferences in your code if that would help. 

=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 Methods you need to implement

The master list of vtable methods can be found in F<vtable.tbl> in
the root directory of the Parrot source; since that's not exactly
verbose, here's a better description of the methods that you need to
implement:

=over 3

=item C<type>

Return some notion of what 'type' you are; this can be used to
communicate state information between PMCs of the same class.

=item C<name>

Return a string containing your class name.

=item C<init>

Do any data set-up you need to do.

=item C<clone>

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

=item C<morph>

Turn yourself into the specified type.

=item C<destroy>

Do any data shut-down and finalization you need to do.

=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 an floating-point
multimethod.

=item C<set_string>

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

=item C<set_value>

Set your private data to the raw pointer passed in. This will only
be used in exceptional circumstances.

=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 catenate it to yourself,
storing the result in C<dest>. (Probably by calling its C<set_string>
method) This is a string multimethod.

=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<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<match>

Execute the given regular expression on C<value> and store the result.

=item C<repeat>

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

=back

Parrot will provide a set of default methods you can inherit from if
you don't need to do anything special for a given method. These will
be named C<Parrot_default_...>. If you don't want to implement a
specific method, simply say something like

    void logical_not (PMC* value) = default;

and a sensible default will be provided for you.

If your class is a modification of an existing class, you may wish to
use inheritance. At the beginning of your vtable specification in
classes/YOURCLASS.pmc, add the C<extends SUPERCLASS> phrase. For
example:

  pmclass PackedArray extends Array { ...

See the POD documentation in classes/pmc2c.pl for a list of useful
keywords that you may use in the .pmc file. (Run
C<perldoc -F pmc2c.pl> to view the POD.)
