How to Embed Perl in your D program
June 22, 2010 Leave a comment
This post takes influence, and reference from perldoc’s perlembed page.
Do you want to:
Use D from Perl?
Have a look at the Perl Inline API and write your own XS module. :)
Use a Unix program from Perl?
Read about back-quotes and about “system” and “exec” in perlfunc.
Use Perl from Perl?
Read about “do”, “eval”, “require” and “use” in perlfunc.
Use D from D?
Read about “mixin” in the D Language Reference for compile-time interpretation.
If you require run-time interpretation, rethink your design.
Use Perl from D?
Read on…
The D Perl Class Routine
This is made possible because Perl itself is written in C, the Perl library is the collection of compiled C programs, and D is binary compatible with C.
I’ll demonstrate this by showing a bastardized, non-portable version of miniperlmain.c containing the essentials of embedding. But re-written in D using a Class wrapper.
/*
Perl/Interpreter.d
Copyright (C) 2010 by Iain Buclaw
You may distribute under the terms of either the GNU General Public
License or the Artistic License.
*/
module Perl.Interpreter;
/* For alloca, malloc, free */
private import std.c.stdlib;
/* flags in PL_exit_flags for nature of exit() */
enum
{
EXIT_EXPECTED = 0x01,
EXIT_DESTRUCT_END = 0x02, /* Run END in perl_destruct */
}
/* flags for Perl_call_* routines */
enum
{
G_SCALAR = 0,
G_ARRAY = 1,
G_VOID = 128, /* skip this bit when adding flags below */
G_WANT = (128|1),
G_DISCARD = 2,
G_EVAL = 4, /* Assume eval {} around subroutine call. */
G_NOARGS = 8, /* Don't construct a @_ array. */
G_KEEPERR = 16, /* Append errors to $@, don't overwrite it */
G_NODEBUG = 32, /* Disable debugging at toplevel. */
G_METHOD = 64, /* Calling method. */
G_FAKINGEVAL = 256, /* Faking an eval context for call_sv or fold_constants */
}
extern (C)
{
/* Perl Defines */
struct interpreter {
char broiled;
}
alias interpreter PerlInterpreter;
alias void function(PerlInterpreter*) XSINIT_t;
ubyte PL_exit_flags;
/* Perl Prototypes */
int Perl_call_argv(PerlInterpreter*, char *subname, int flags, char **argv);
int perl_destruct(PerlInterpreter*);
int perl_parse(PerlInterpreter*, XSINIT_t xsinit, int argc, char** argv, char** env);
int perl_run(PerlInterpreter*);
void perl_construct(PerlInterpreter*);
void perl_free(PerlInterpreter*);
void Perl_sys_init3(int* argc, char*** argv, char*** env);
void Perl_sys_term();
PerlInterpreter* perl_alloc();
/* Unix System Environment */
extern __gshared char** environ;
}
/* Copy a char[][] to a char** */
private void buildStringA(char[][] a, char** ap)
{
foreach (s; a)
*ap++ = (s ~ '\0').ptr;
*ap = null;
}
/* Copy a char[] into a char* */
private void buildString(char[] a, char* ap)
{
foreach (c; a)
*ap++ = c;
*ap = '\0';
}
/*** The Perl Interpreter Class ***/
class perlInterpreter
{
int argc;
char** argv;
PerlInterpreter* my_perl;
int parsestatus = 1;
int exitstatus;
this(ref char[][] args)
{
auto _argv = cast (char**)malloc ((char*).sizeof * (1 + args.length));
buildStringA (args, _argv);
argv = _argv;
argc = cast (int)args.length;
}
this(int _argc, char** _argv)
{
argc = _argc;
argv = _argv;
}
~this()
{
if (argv)
free (argv);
}
void Construct()
{
if (!my_perl)
{
Perl_sys_init3 (&argc, &argv, &environ);
my_perl = perl_alloc();
perl_construct (my_perl);
}
}
void ExitFlags(uint flag)
{
PL_exit_flags |= flag;
}
void Parse()
{
if (my_perl)
parsestatus = perl_parse (my_perl, null, argc, argv, null);
}
void Run()
{
if (my_perl && !parsestatus)
exitstatus = perl_run (my_perl);
}
void CallSub(char[] subname, int flags)
{
auto sub = cast (char*)alloca ((char).sizeof * (1 + subname.length));
buildString (subname, sub);
if (my_perl && !parsestatus)
{
if (flags & G_DISCARD)
Perl_call_argv (my_perl, sub, flags, argv);
else
exitstatus = Perl_call_argv (my_perl, sub, flags, argv);
}
}
void Destruct()
{
if (my_perl)
{
perl_destruct (my_perl);
perl_free (my_perl);
Perl_sys_term();
}
}
}
And to build the library, simply use your D compiler of choice.
gdc -c -O2 -frelease Perl/Interpreter.d ar rcs libperld.a Interpreter.o ranlib libperld.a
Adding a Perl interpreter to your D program
And I’ll demonstrate how you can use the class in D.
import Perl.Interpreter;
int main(char[][] args)
{
perlInterpreter myPerl = new perlInterpreter (args);
myPerl.Construct();
myPerl.ExitFlags(EXIT_DESTRUCT_END);
myPerl.Parse();
myPerl.Run();
myPerl.Destruct();
return myPerl.exitstatus;
}
And build it with the linker option ‘-lperl’ which requires libperl-dev to be installed on your system.
gdc -c -O2 -frelease dminiperl.d gdc dminiperl.o -L. -lperld -lperl -o dminiperl
And after successful compilation, you’ll be able to use dminiperl just like Perl itself.
dminiperl myperlprog.pl
or
$ dminiperl print "Pretty Good Perl \n"; print "10890 - 9801 is ", 10890 - 9801, "\n";
or
dminiperl -e 'printf("%x\n", 3735928559)'
Calling a Perl subroutine from your D program
To call individual Perl subroutines, Perl has four call_* functions to allow interface to. For this class though, I have only included call_argv() which takes a string argument.
import Perl.Interpreter;
void main(char[][] args)
{
perlInterpreter myPerl = new perlInterpreter (args);
myPerl.Construct();
myPerl.ExitFlags(EXIT_DESTRUCT_END);
myPerl.Parse();
/* Skipping .Run() */
myPerl.CallSub("showtime", G_DISCARD | G_NOARGS);
myPerl.Destruct();
}
where showtime is a Perl subroutine that takes no arguments (that’s the G_NOARGS) and for which I’ll ignore the return value (that’s the G_DISCARD). Those flags, and others, are discussed in perlcall.
And to define the showtime subroutine in a file called showtime.pl:
print "I shan't be printed.";
sub showtime {
print time,"\n";
}
And compile and run.
$ gdc -c -O2 -frelease showtime.d $ gdc showtime.o -L. -lperld -lperl -o showtime $ showtime showtime.pl 1277201943
yielding the number of seconds that elapsed between January 1, 1970 (the beginning of the Unix epoch), and the moment I began writing this sentence. :-)
Extending this D Class
For those who know perlembed well, this is just the icing of just what you can do. Sadly however, the more luxurious features, such as evaluating a Perl statement from your D program, require a little more complete D library, and a lot more time to write than I have to give in this particular essay.
If anyone wants to access any of the code / examples in this post from a source repository, or wish to collaborate in writing a complete Perl Library Interface for D. Just shout.
Moral?
You can sometimes write faster code in D, but you can always write code faster in Perl. Because you can use each from the other (with a little bit of work), combine them as you wish. :-)
Regards