docs/pdds/embedding.pod - Parrot's Embedding and Extending Interface


What we believe people will do when embedding and extending Parrot, why they do it, and how.

{{ NOTE: some of this will later move into pdds 11 & 12, but for now just want to get the stub checked in. }}




Why embed:

Why extend:

Philosophical rules:



Embedding - using libparrot from within another program, likely with a C/NCI/FFI interface

Extending - writing Parrot extensions, likely through C or another language

In practice, there is little difference between the two; mostly in terms of who has control. The necessary interfaces should stay the same.


Implementation details.

Simplicity is the main goal; it should be almost trivial to embed Parrot in an existing application. It must be trivial to do the right thing; the APIs must make it so much easier to work correctly than to make mistakes. This means, in particular, that:

Working with Interpreters ^

It is the external code's duty to create, manage, and destroy interpreters.

Parrot_new( NULL ) returns an opaque pointer to a new interpreter:

        Parrot_Interp Parrot_new(Parrot_Interp parent);

parent can be NULL for the first interpreter created. All subsequent calls to this function should pass an existing interpreter.

Note: it is not clear what happens if you fail to do so; is there a way to detect this in the interface and give a warning?

Parrot_destroy ( interp ) destroys an interpreter and frees its resources.

        void Parrot_destroy(Parrot_Interp);

Note: It is not clear what happens if this interpreter has active children.

Working with Source Code and PBC Files ^

Perhaps the most common case for working with code is loading it from an external file. This may often be PBC, but it must also be possible to load code with any registered compiler. This must be a single-stage operation:

        Parrot_PMC Parrot_load_bytecode( Parrot_Interp, const char *filepath );

        Parrot_PMC Parrot_load_hll_code( Parrot_Interp, const char *compiler,
                                                        const char *filepath );

The PMC returned will be the Sub PMC representing the entry point into the code. That is, it will be the PMC representing the :main subroutine, if one exists, or the first subroutine in the file.

If there is an error -- such that the file does not exist, the compiler is unknown, or there was a compilation or invalid bytecode error -- the PMC should be an Exception PMC instead.

Note: I suppose NULL would work as well; it might be more C-like. Continue considering.

Note also: the current Parrot_readbc() and Parrot_loadbc() exposes the details of packfiles to the external API and uses two operations to perform a single logical operation.

Note: it may be worth reconsidering these names, if Parrot_load_bytecode() can load PBC, PIR, and PASM files without having a compiler named explicitly.

Compiling source code generated or read from the host application is also possible:

         Parrot_PMC Parrot_compile_string( Parrot_Interp, const char *compiler,
                                                      const char *code );

The potential return values are the same as for loading code from disk.

Note: this declaration should move from interpreter.h to embed.h.

Working with PMCs ^


Calling Functions ^


Calling Opcodes ^



It should be possible to register a compiler for an HLL with an interpreter such that it is possible to load source code written in that language or pass source code to an interpreter successfully.


Any associated documents.


List of footnotes to the text.


List of references.