pathologically polluting perl (with c, java and other rubbish using inline.pm) brian ingerson...
TRANSCRIPT
Pathologically Polluting Perl(with C, Java and Other Rubbish using Inline.pm)
Brian IngersonActiveState
Thursday June 14th, 19101
Leacock 132
Yet Another Perl Conference
Inline.pm
Pathologically Polluting Perl(with C, Python and Other Rubbish using Inline.pm)
Brian IngersonActiveState
Wednesday July 25th 3:45pm
Grande Ballroom C
The Perl Conference 5
Inline.pm
Part I• Introducing Inline.pm
• C Perl Run
• Whence Inline?
• One-Liners
• The Inline Syntax
• Inline::C
• Supported Platforms
Inline.pm
Introducing Inline.pm• Inline lets you write Perl subroutines ...
• ... in other programming languages!
• Functions, methods and classes too.
• Put some other code directly into your script,
• and simply run it like normal. It just works.
• Inline takes care of all the details!
Inline.pm
hello_world.pluse Inline C => <<'END_C';
#include <stdio.h>
void greet() { printf("Hello, world\n");}
END_C
greet;
Inline.pm
hello_world.pluse Inline C => <<'END_C';
#include <stdio.h>
void greet() { printf("Hello, world\n");}
END_C
greet;
Inline.pm
hello_world.pluse Inline C => <<'END_C';
#include <stdio.h>
void greet() { printf("Hello, world\n");}
END_C
greet;
Inline.pm
hello_foo.pluse Inline C;
greet('foo');
__END____C__
void greet(char* who) { printf("Hello, %s\n", who);}
Inline.pm
hello_foo.pluse Inline C;
greet('foo');
__END____C__
void greet(char* who) { printf("Hello, %s\n", who);}
Inline.pm
hello_foo.pluse Inline C;
greet('foo');
__END____C__
void greet(char* who) { printf("Hello, %s\n", who);}
Inline.pm
length.pluse Inline C;
open DICT, '/usr/dict/words' or die $!;undef $/;print "The length of /usr/dict/words is ", l(<DICT>), " characters\n";
__END____C__
int l(char* str) { return strlen(str);}
Inline.pm
length.pluse Inline C;
open DICT, '/usr/dict/words' or die $!;undef $/;print "The length of /usr/dict/words is ", len(<DICT>), " characters\n";
__END____C__
int len(char* str) { return strlen(str);}
Inline.pm
Whence Inline?• Inline came forth from Altered States:
Inline.pm
• TPC
• DDC
• PRD
• MDV
• The Perl Conf 4
• Dr. Damian Conway
• Parse::RecDescent
• Digest MD5
• Happy Birthday!
One-liners• Print all primes• perl -le 'while(($_||=1)++){print if(1x$_)!~/^(11+)\1+$/}'
• Crash your Pentium• perl -e 'require
DynaLoader;DynaLoader::dl_install_xsub("main::hangme",unpack("I", pack("P4", "\xF0\x0F\xC7\xC8")));hangme()'
• A scary JAPH• perl -e 'BEGIN{my$x="Knuth heals rare project\n";$^H
{integer}=sub{my$y=shift;$_=substr$x=>$y&0x1F,1;$y>32?uc:lc};$^H=hex join""=>2,1,1,0,0}print 52,2,10,23,16,8,1,19,3,6,15, 12,5,49,21,14,9,11,36,13,22,32,7,18,24;'
Inline.pm
Inline One-liners• JAPH• perl -e 'use Inline C=>q{void J(){printf("Just Another Perl
Hacker\n");}};J'
• JAxH• perl -le 'use Inline C=>q{SV*JAxH(char*x){return
newSVpvf("Just Another %s Hacker",x);}};print JAxH+Perl'
• The Muenzerbrot Setuse Inline C=>'void C(){int m,u,e=0;float l,_,I;for(;1840-e;putchar((+
+e>907 &&942>e?61-m:u)["\n)moc.isc@rezneumb(rezneuM drahnreB"]))for(u=_=l=0;79-(m =e%80)&&I*l+_*_<6&&26-++u;_=2*l*_+e/80*.09-1,l=I)I=l*l-_*_-2+m/27.;}';&C
Inline.pm
The Inline Syntax• Basic Syntax
use Inline C => "source code";
• "String"use Inline C => <<'END_C';
• 'DATA'use Inline C => 'DATA';
• 'FILE'use Inline::Files;
use Inline C => 'FILE';
Inline.pm
The Inline Syntax• Basic Configuration
use Inline C => "source code",
LIBS => '-lfoo',
PREFIX => 'foo_';
• General Config Options– NAME, DIRECTORY
• Language ('C') Config Options– LIBS, INC, AUTO_INCLUDE
Inline.pm
The Inline Syntax• Shorthand
use Inline C => 'DATA';
use Inline C => 'FILE';
use Inline C;
Inline.pm
The Inline Syntax
Inline.pm
• Inline has two functions: bind() and init()• Both functions are rarely used• bind() is like eval() for C code• Takes same args as 'use Inline ...'Inline->bind(C => $code);
• init() is the runtime equiv of INIT()Inline->init;
Inline::C• Basic Perl5 Internals
• Hacking Internals
• Wrapping External Libraries
• The Perl Stack
Inline.pm
Perl5 API (perlapi.pod)
Inline.pm
SV* newSVpvf(const char* pat, ...)void sv_catpvf(SV* sv, const char* pat, ...)void SvGROW(SV* sv, STRLEN len)SV* newSViv(IV i)SV* sv_2mortal(SV* sv)char* SvPV(SV* sv, STRLEN len)char* SvPV_nolen(SV* sv)U32 SvREFCNT(SV* sv)bool SvROK(SV* sv)bool SvIOK(SV* sv)STRLEN SvLEN(SV* sv)SV* get_sv(const char* name, I32 create)I32 call_pv(const char* sub_name, I32 flags)SV* eval_pv(const char* p, I32 croak_on_err)void croak(const char* pat, ...)
Hacking Internals
Inline.pm
• Inline makes it easy to mess around with Perl internals• Caveat Scriptor!• READONLY• Lost and Found
writable.pl
Inline.pm
use Inline C;$str = \ "Ingy hates Inline\n";writable($$str);$$str =~ s/hate/love/g;print $$str;
__END____C__void writable(SV* x) {SvREADONLY_off(x);}
writable.pl
Inline.pm
use Inline C;$str = \ "Ingy hates Inline\n";writable($$str);$$str =~ s/hate/love/g;print $$str;
__END____C__void writable(SV* x) {SvREADONLY_off(x);}
lostfound.pl
Inline.pm
use Inline C;
$x = q{"Everybody Loves My Ingy"};$x = 42;find_string($x);print "My favorite number is $x\n";
__END____C__void find_string(SV* x) { SvPOK_on(x); }
lostfound.pl
Inline.pm
use Inline C;
$x = q{"Everybody Loves My Ingy"};$x = 42;find_string($x);print "My favorite number is $x\n";
__END____C__void find_string(SV* x) { SvPOK_on(x); }
WinJAPH.pluse Inline C => DATA => LIBS => '-luser32', PREFIX => 'my_';
MessageBoxA('Inline Message Box', 'Just Another Perl Hacker');
__END____C__
#include <windows.h>int my_MessageBoxA(char* Caption, char* Text) { return MessageBoxA(0, Text, Caption, 0);}
Inline.pm
WinJAPH.pluse Inline C => DATA => LIBS => '-luser32', PREFIX => 'my_';
MessageBoxA('Inline Message Box', 'Just Another Perl Hacker');
__END____C__
#include <windows.h>int my_MessageBoxA(char* Caption, char* Text) { return MessageBoxA(0, Text, Caption, 0);}
Inline.pm
localtime.plprint map {"$_\n"} get_localtime(time);
use Inline C => <<'END_OF_C_CODE';#include <time.h>void get_localtime(int utc) { struct tm *ltime = localtime(&utc); Inline_Stack_Vars; Inline_Stack_Reset; Inline_Stack_Push(newSViv(ltime->tm_year)); Inline_Stack_Push(newSViv(ltime->tm_mon)); Inline_Stack_Push(newSViv(ltime->tm_mday)); Inline_Stack_Push(newSViv(ltime->tm_hour)); Inline_Stack_Push(newSViv(ltime->tm_min)); Inline_Stack_Push(newSViv(ltime->tm_sec)); Inline_Stack_Push(newSViv(ltime->tm_isdst)); Inline_Stack_Done;}END_OF_C_CODE
Inline.pm
localtime.plprint map {"$_\n"} get_localtime(time);
use Inline C => <<'END_OF_C_CODE';#include <time.h>void get_localtime(int utc) { struct tm *ltime = localtime(&utc); Inline_Stack_Vars; Inline_Stack_Reset; Inline_Stack_Push(newSViv(ltime->tm_year)); Inline_Stack_Push(newSViv(ltime->tm_mon)); Inline_Stack_Push(newSViv(ltime->tm_mday)); Inline_Stack_Push(newSViv(ltime->tm_hour)); Inline_Stack_Push(newSViv(ltime->tm_min)); Inline_Stack_Push(newSViv(ltime->tm_sec)); Inline_Stack_Push(newSViv(ltime->tm_isdst)); Inline_Stack_Done;}END_OF_C_CODE
Inline.pm
Supported Platforms for C• All Modern Unix Variants
• All Modern Windows & Cygwin
• Requires Perl's Build Environment
• Binary Distribution Support
• Works with Mingw32/gcc on Win32
• Working on VMS et al.
• Supports Perl 5.005_03 and up.
Inline.pm
Part II• Object Oriented C
• Inline C++
• How Inline Works
• Inline::Python
• Inline::Java
• Inline::ASM
Inline.pm
Object Oriented C• Perl can use C based objects
• The OO behaviour is handled by Perl
• The data is on accessible from C
Inline.pm
oopc.plmy $obj1 = Soldier->new('Benjamin', 'Private', 11111);my $obj2 = Soldier->new('Sanders', 'Colonel', 22222);my $obj3 = Soldier->new('Matt', 'Sergeant', 33333);
for my $obj ($obj1, $obj2, $obj3) { print ($obj->get_serial, ") ", $obj->get_name, " is a ", $obj->get_rank, "\n");}
Inline.pm
oopc.plpackage Soldier;use Inline C => <<'END';typedef struct { char* name; char* rank; long serial;} Soldier;
Inline.pm
oopc.pl
Inline.pm
SV* new(char* class, char* name, char* rank, long serial) { Soldier* soldier = malloc(sizeof(Soldier)); SV* obj_ref = newSViv(0); SV* obj = newSVrv(obj_ref, class); soldier->name = strdup(name); soldier->rank = strdup(rank); soldier->serial = serial;
sv_setiv(obj, (IV)soldier); SvREADONLY_on(obj); return obj_ref;}
oopc.pl
char* get_name(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->name;}
char* get_rank(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->rank;}
long get_serial(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->serial;}
Inline.pm
oopc.plvoid DESTROY(SV* obj) { Soldier* soldier = (Soldier*)SvIV(SvRV(obj)); free(soldier->name); free(soldier->rank); free(soldier);}
Inline.pm
Some Ware Beyond the C• The primary goal of Inline is:
– "Make it easy to use other programming languages with Perl"
• This is not limited to C.
• Drumroll please…
Inline.pm
Inline::CPP
Inline.pm
use Inline 'C++';my $obj1 = Soldier->new('Benjamin', 'Private', 11111);__END____C++__class Soldier { public: Soldier(char *name, char *rank, int serial); char *get_name(); char *get_rank(); int get_serial(); private: char *name; char *rank; int serial;};
How Inline Works• Inline's implementation is simple.
• It makes use of existing Perl tools.
• It just does a *lot* of little things.
• It's magic.
Inline.pm
The Friends of Inline• Parse::RecDescent
• Digest::MD5
• Config.pm
• XS
• ExtUtils::MakeMaker
• DynaLoader
• perl !
Inline.pm
The C Parse::RecDescent Grammar
Inline.pm
c_code: part(s) {1}part: comment | function_definition { my $function = $item[1]->[0]; push @{$thisparser->{data}->{functions}}, $function; -- lines deleted -- } | anything_elsecomment: m{\s* // [^\n]* \n }x | m{\s* /\* (?:[^*]+|\*(?!/))* \*/ ([ \t]*)? }xfunction_definition: -- lines deleted --anything_else: /.*/
How Inline C Works1. Name, NAME, AUTONAME2. Dust for Fingerprints (.inl)3. Directory Assistance4. Build
1. Parse2. Glue3. Compile4. Cache
5. Load (and Bind)
Inline.pm
How Inline Foo Works1. Name, NAME, AUTONAME2. Dust for Fingerprints (.inl)3. Directory Assistance4. Build
1. Parse2. Glue3. Compile4. Cache
5. Load (and Bind)
Inline.pm
The 'config' file
Inline.pm
version : 0.40languages : % C : C C++ : CPP CPP : CPPtypes : % C : compiled CPP : compiledmodules : % C : Inline::C CPP : Inline::CPPsuffixes : % C : so CPP : so
The '.inl' file
Inline.pm
md5 : 2cdb20c55cc56102fd68db27620832aename : japhlanguage : Clanguage_id : Cdate_compiled : Fri Jun 1 01:15:55 2001inline_version : 0.40ILSM : % module : Inline::C suffix : so type : compiledConfig : % apiversion : 5.6.1 archname : i686-linux-thread-multi cc : gcc osname : linux osvers : 2.2.17 so : so version : 5.6.1
snake.pluse Inline Python;my $language = shift;print $language, (match($language, 'Perl') ? ' rules' : ' sucks'), "!\n";__END____Python__import sysimport redef match(str, regex): f = re.compile(regex); if f.match(str): return 1 return 0
Inline.pm
squarer.java
public class squarer { public squarer() {} public double square(double x) { return x * x ; }}
Inline.pm
squarer.pluse strict;use Inline Java => 'DATA', CLASSPATH => '/usr/local/j????';
my $s = new my_squarer();my $n = 6;
print "$n squared is ", $s->square($n), "\n";
__END____Java__class my_squarer extends squarer { public my_squarer() { super(); }}
Inline.pm
yasquarer.pluse strict;
use Inline Java => 'STUDY', CLASSPATH =>'/usr/local/j????', STUDY => ['squarer'];
my $s = new squarer();my $n = 7;
print "$n squared is ", $s->square($n), "\n" ;
Inline.pm
asm_japh.pluse Inline ASM => DATA => PROTOTYPES => {JAxH => 'void(char*)'};print JAxH('Perl');__END____C__ BITS 32 GLOBAL JAxH EXTERN printf SECTION .textJAxH push ebp mov ebp,esp mov eax,[ebp+8] push dword eax push dword jaxhstr call printf mov esp,ebp pop ebp ret SECTION .datajaxhstr db "Just Another %s Hacker", 10, 0
Inline.pm
Part III• The Inline API
• Inline::Foo
• Inline::PERL
• Inline::Perl
• Inline::MakeMaker
• Inline::CPR
• Future Plans
Inline.pm
The Inline API• Rolling Your Own
• Almost any language can be Inlined
• 5 degrees of separation
• Actually 5 methods
• Inline-API.pod
Inline.pm
The Inline API• ILSMs are subclasses of Inline
• Named Inline::Foo
• register()
• validate()
• build()
• load()
• info()
Inline.pm
t/01usages.t
use Test; plan( tests => 1);
# test 5# Make sure language name aliases work ('foo'
instead of 'Foo')
ok(test5('test5'));
use Inline foo => <<'END_OF_FOO';foo-sub test5 { foo-return $_[0] foo-eq 'test5';}END_OF_FOO
Inline.pm
Inline/Foo.pm
package Inline::Foo;$VERSION = '0.01';require Inline;@ISA = qw(Inline);use strict;use Carp;
Inline.pm
Inline/Foo.pm
sub register { return { language => 'Foo', aliases => ['foo'], type => 'interpreted', suffix => 'foo', };}
Inline.pm
Inline/Foo.pm sub validate { my $o = shift; $o->{ILSM}{PATTERN} ||= 'foo-'; $o->{ILSM}{BAR} ||= 0; while (@_) { my ($key, $value) = splice @_, 0, 2; if ($key eq 'PATTERN') { $o->{ILSM}{PATTERN} = $value; next; } if ($key eq 'BAR') { croak usage_config_bar unless $value =~ /^[01]$/; $o->{ILSM}{BAR} = $value; next; } croak usage_config($key); }}
Inline.pm
Inline/Foo.pm sub build { my $o = shift; my $code = $o->{API}{code}; my $pattern = $o->{ILSM}{PATTERN}; $code =~ s/$pattern//g; $code =~ s/bar-//g if $o->{ILSM}{BAR}; sleep 1; # imitate compile delay { package Foo::Tester; eval $code; } croak "Foo build failed:\n$@" if $@; my $path = "$o->{API}{install_lib}/auto/". "$o->{API}{modpname}"; my $obj = $o->{API}{location}; $o->mkpath($path) unless -d $path; open FOO_OBJ, "> $obj" or croak "Can't open $obj for output\n$!"; print FOO_OBJ $code; close \*FOO_OBJ;}
Inline.pm
Inline/Foo.pm sub load { my $o = shift; my $obj = $o->{API}{location}; open FOO_OBJ, "< $obj" or croak "Can't open $obj for output\n$!"; my $code = join '', <FOO_OBJ>; close \*FOO_OBJ; eval "package $o->{API}{pkg};\n$code"; croak "Unable to load Foo module $obj:\n$@" if $@;}
Inline.pm
Inline::PERL
Inline.pm
•Bring the power of PERL programming,
•to your Perl programs!
•John McNamara
•Posted on perlmonks.net
•Available as Acme-Inline-Perl
•It's on its way to CPAN...
Inline/PERL.pm sub register { return { language => 'PERL', aliases => ['PEARL', 'CGI'], type => 'interpreted', suffix => 'PL', };}sub build { my $o = shift; my $code = $o->{API}{code}; if ($code =~ /strict/) { carp <<END;Your code contains "use strict;". Please be aware thatthis may provide you with too much of a clue.';END
Inline.pm
Inline::Perl
Inline.pm
•Bind another Perl to Perl
•Legacy Perls
•Like Perl 4
•or Perl 5.8
•or Perl 5+i
Inline CPAN Modules
Inline.pm
•Write an Inline Module
•Add a NAME and VERSION
•use Inline::MakeMaker;
•make dist
•Equivalent to XS
PerlAPI.pmpackage PerlAPI;
use strict;require Exporter;@PerlAPI::ISA = qw(Exporter);@PerlAPI::EXPORT = qw(SvREADONLY_off);$PerlAPI::VERSION = '0.42';use Inline C => DATA => PREFIX => 'my_', NAME => 'PerlAPI', VERSION => '0.42';
1;__DATA____C__void my_SvREADONLY_off(SV* x)
{SvREADONLY_off(x);}
Inline.pm
Makefile.PL use Inline::MakeMaker;
WriteInlineMakefile( NAME => 'PerlAPI', VERSION_FROM => 'PerlAPI.pm',);
Inline.pm
test.pluse Test;use PerlAPI;plan(tests => 1);
$str = \ "Ingy hates Inline\n";SvREADONLY_off($$str);$$str =~ s/hate/love/g;
ok($$str =~ /loves/);
Inline.pm
Soooo….• Inline is a great way to extend Perl w/ C
• But...
• How can we easily embed Perl in C?
Inline.pm
What if….• We could pass plain C to Perl,
• And Perl could pass it to Inline,
• And Inline could bind to main(),
• And just invoke main() from Perl.
Inline.pm
Introducing CPR• "C Perl Run"
• The C interpreter
• A brand new programming language
• That combines all of C
• With the guts of Perl
• #!usr/bin/cpr
Inline.pm
Hello_Perl.cpr
Inline.pm
#!/usr/local/bin/cprint main(void) { printf("Hello World, I'm running under Perl
version %s\n", CPR_eval("use Config; $Config{version}") ); return 0;}
Hello_Cruel_World.cpr
Inline.pm
#!/usr/bin/cprint main(void) { CPR_eval("use Inline (C => q{ char* greet() { return \"Hello world\"; } })"); printf("%s, I'm running under Perl version %s\n", CPR_eval("&greet"), CPR_eval("use Config; $Config{version}")); return 0;}
Future Plans• A new frontier for Perl
• More ILSMs
• Inline 0.50
• Development Tools
• You!
Inline.pm
Inline 0.50 Plans
Inline.pm
• XS like modules• Viral Inline stub• No core requirement• Support all versions equally• Encourage hacking and collaboration
Development Tools
Inline.pm
• The Inline Debugger• Module authoring automation
• Binary distributions