Виртуальные таблицы Паррота
Это руководство по созданию ващих собственных PMC (Parrot Magic Cookie) классов. Оно рассказывает, что необходимо нописать надлежащим образом, что бы добавить новые переменный типы переменных в Паррот.
Внутренности Паррот интерпритатора, в упошенной схеме (или, если ты хочешь меннее принебрежительно, скептически), - это лабиринт поведения типов переменных. Стандартный пример - различия между скалярами Перла и скалярами Питона. В Перле, если ты имеешь
$a = "a9";
$a++;
ты получишь, что $a равно b0. Это благодаря магии оперетора
инкрементирования в Перле. В Питоне, с другой стороны, ты получишь
ошибку выполнения.
a является PythonString, а b - это
PythonNumber. Но загвоздка остатется - инкрементирование PythonString
очень отличается от инкрементирования PerlScalar
Такое поведение является функцией PMC ``типа'', естественно рассматиривать различные типы PMC как классы в объектно-ориентированных системах. Паррот интерпритатор вызывает методы индивидуальных PMC объектов чтобы манипулировать ими. Так что привер, приведенный выше, выгледял бы подобно этому:
"a9".
Вызов метода, чтобы сказать ему инкрементировать себя.
И если ты заменишь PerlScalar на PythonString, ты получишь различное поведение, но для основных внутренностей интерпритатора, инструкции будет такими же самыми. PMC являются абстрактными виртуальными классами; Интрепритатор вызывает методы, PMC объект делает требуемый вещи, и интепритатор не заботится особенно, что это требуемая вешь случается чтобы быть.
Hence, добавление новых типов данный в Паррот -это вопрос об обеспечении методов, ктороые выполняют ожидаемые от типов данных действия. Давайте взлянем, как сделать это.
Если ты добавляешь тип данных в ядро Паррота, (и ты сверяешься с Dan и/или Simon, что вы, как предполагается, делаете) ты должен создать файл в classes/ подкаталоге; это там встроенные PMC классы живут. (И хороший источник примеров, чтобы разграбить, даже если Вы не пишете основной тип данных.)
You should almost always start by running genclass.pl found in the 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
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 classes/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
Parrot_base_vtables array.
To finish up adding a built-in class:
The usual way to continue from the genclass.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.
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; since that's not exactly verbose, here's a better description of the methods that you need to implement:
typenameinitis_equalis_same.
clonemorphdestroyPobj_active_destroy_FLAG.
get_integerget_numberget_stringget_boolget_valueis_sameis_equal)
set_integerset_numberset_stringset_valueaddvalue 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.
subtractvalue 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.
multiplydividemodulusconcatenatevalue and catenate it to yourself,
storing the result in dest. (Probably by calling its set_string
method) This is a string multimethod.
logical_orlogical_anddest.
logical_notmatchvalue and store the result.
repeatvalue 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 through 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
classes/YOURCLASS.pmc, add the 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
perldoc -F pmc2c.pl to view the POD.)