Section 10.5. Acme::


10.5. Acme::*

With all this crazy hackery going on, an outlet was needed on CPAN for less serious module contributions to the Perl world. The Acme namespace was set aside for wacky, explosive, or Heath-Robinsonian modules, and has very quickly become one of the more densely populated namespaces on CPAN. Most, if not all, of Acme:: is, in some way, London.pm's fault.

It all started with the Bleach module, a neat toy by Damian Conway that spawned a host of less amusing and sadly uninspired imitators. Because of the host of imitators that followed, clogging up the root of the CPAN namespace, it was decided that silliness should not be discouraged, but moved to Acme::, and Bleach became Acme::Bleach. Bleach is really clever; it takes an ordinary program, like so:

     use Bleach;     for my $i (1..10) {         print "Hello! $i\n";     }

The first time this program runs, it appears to do nothing at all. However, when you come to look at the code again, it now looks like:

     use Bleach; 

The really clever part is that it still runs, and it now prints out the message 10 times. This is astonishing until you know the trick. How does Bleach work, then? Everyone assumed this was done with source filters, but the business end of Bleach is a mere 11 lines long:

     my $tie = " \t"x8;     sub whiten { local $_ = unpack "b*", pop; tr/01/\t/; s/(.{9})/$1\n/g; $tie.$_ }     sub brighten { local $_ = pop; s/^$tie|[^ \t]//g; tr/ \t/01/; pack "b*", $_ }     sub dirty { $_[0] =~ /\S/ }     sub dress { $_[0] =~ /^$tie/ }     open 0 or print "Can't rebleach'$0'\n" and exit;     (my $shirt = join "", <0>) =~ s/.*^\s*use\s+Acme::Bleach\s*;\n//sm;     local $SIG{_ _WARN_ _} = \&dirty;     do {eval brighten $shirt; exit} unless dirty $shirt && not dress $shirt;     open 0, ">$0" or print "Cannot bleach '$0'\n" and exit;     print {0} "use Acme::Bleach;\n", whiten $shirt and exit;

It all becomes pretty obvious when you look at what the subroutines do. The whiten subroutine takes a string, turns it into its binary representation, and turns the zeros and ones into different types of whitespace. The brighten subroutine does the opposite, after first removing the signature $tie. The dirty subroutine checks to see if there's anything in a string that isn't whitespace, and dress checks to see if the signature is present.

So the module loads up your code by using a slightly nifty Perl trick, found in the documentation of open:

If EXPR is omitted, the scalar variable of the same name as the FILEHANDLE contains the filename.

That is, by opening a filehandle called 0, we take the filename from $0, the location of our program. Then we lop off everything before use Acme::Bleach, and turn off warnings by setting the warn handler to an irrelevant subroutine. Now the code we've read in could be already bleached, in which case it's not dirty and will be dressed; if this is the case, we brighten it and execute it. If not, we whiten it and write it back.

Very simple, once you've seen the trick. Other modules based on the same principle include Acme::Buffy, Acme::Pony, and many other London.pm in-jokes.

There are, of course, some Acme:: modules that do other things. They range from the amusing but simple and uninspired (Acme::Handwaveyou tell it what data you expect, and it returns it), through amusing, simple, and inspired (Acme::Don'tlike a do block, but not quite) right up to the spectacular.

Acme::Eyedrops, for instance, deserves an honorable mention. This takes an ordinary Perl programand let's use a classic:

     print "hello world\n";

and automatically obfuscates it:

     perl -MAcme::EyeDrops=sightly -e 'print sightly("helloworld.pl")'     eval eval     '"'.('['^'+').('['^')').('`'|')').('`'|'.').('['^'/').('{'^'[').'\\'.'"'.     ('`'|'(').('`'|'%').('`'|',').('`'|',').('`'|'/').('{'^'[').('['^',').('`'     |'/').('['^')').('`'|',').('`'|'$').'\\'.'\\'.('`'|'.').'\\'.'"'.';'.('!'^     '+').'"'

(We've reformatted the actual output slightly for, um, readability.)

Now you can probably see what it's done hereit's encoded the ASCII values of each character in the program: the first character is '[' ^ '+', which is p, and so on. But that's not all it can do; it can pour that mess of punctuation into pretty shapes. For instance, if you pass the shape => "simon" option, you get a rather unflattering portrait[*] of your humble author:

[*] Based on a very flattering portrait.

     eval eval '"'.                         ('['^'+').("\["^                      ')').("\`"| ')').('`'                    |'.').('['^'/' ).('{'^'[')                   .'\\'.'"'.("\`"| '(').(('`')|                 '%').('`'|(',')).( '`'|',').('`'               |'/').('{'^'[').('['^ ',').('`'|'/'              ).('['^')').('`'|',').( '`'|'$').'\\'             .'\\'.('`'|'.').'\\'.'"' .';'.('!'^'+')            .'"';$:='.'^'~';$~='@'|'(' ;$^=')'^'[';$/           ='`'|'.';$_='('^'}';$,='`'| '!';$\=')'^'}';          $:='.'^'~';$~=('@')|     '(' ;$^     =')'^'['          ;$/='`'|'.';$_='('                    ^'}';$,         ='`'|'!';$\=(')')^                      '}';$:=         '.'^'~';$~='@'|'('                       ;($^)=        ')'^'[';$/='`'|'.';                       $_='('        ^'}';$,='`'|'!';$\=                        "\)"^       '}';$:='.'^"\~";$~= (                       '@')       |'(';$^=')'^'[';$/  =                        '`'       |'.';$_='('^'}';$,  =                        ((       '`'))|'!';$\="\)"^  (                        ((       '}')));$:='.'^'~'; (   ( ( (          (   (  (       $~)))))))='@'|"\("; (        (      (        (     (       $^)))))=')'^'[';$/    ='`'|            '.'  ;      (     (   $_))='('^'}';$,    ='`'|'!'         ;($\) =     (       (  ')'))^'}';$:                             =    (         '.')^"\~";                      (          $~)          =  '@'                         |          (          (                              (          (          (                              (          (           (    (                                   (            ( ( (                   (     (  (      (                (                  (      (  (      (                (                  (      (  (                (                                  (             (   (             (    (                   '('))))))   )             )     )             )))))))))))))             )      )               ))))))))     ;             (       (                 $^))=')'^'[';             #         ;           #                 ;             #             ;     #                    ;              #                ;                       #                              ;          #  ;  #        ;               #                     ;                             #                           ;                                  #                       ;                            #    ;                         #                                ;                           #

Some modules are perhaps better left unwritten.

On the other hand, there are some Acme:: modules that are interesting ideas in other waysAcme::Your, for instance, works like our, but you get to use unqualified package variables from a different package. This is an inventive (and possibly even useful) use of source filters, which allow you to wrap a filtering layer around Perl's parser, affecting the program code it sees.

Source Filters

Source filters were invented by Paul Marquess and provide a way of intercepting Perl source code before it reaches Perl's parser. Using the functions in Filter::Util::Call to talk to the Perl internals, you can install Perl subroutines into the way of the input stream, the way Perl reads a program.

The easiest way to do this is with the Filter::Simple module, which abstracts away a lot of the business of registering filters, reading from the input stream, putting things back into the input stream, and so on. Instead of the original, clumsy source filter mechanism, you can now say:

     package Filter::Rot13;     use Filter::Simple;     FILTER { tr[a-z][n-za-m]; } 

When some code now use's Filter::Rot13, all code after the use statement will be passed through the FILTER block, here rotating the alphabet 13 places.

Many Acme modules derive their usefulness from Filter::Simple, whereas Switch, which provides an implementation of a switch-case statement for Perl, is possibly the most comprehensive use of source filters.


A final Acme curiosity, and one of my favourites, is Acme::Chef, an implementation of David Morgan-Mar's Chef programming language. In Chef, programs are expressed in the form of recipes:

     Hello World Souffle.     This recipe prints the immortal words "Hello world!", in a basically     brute force way. It also makes a lot of food for one person.     Ingredients.     72 g haricot beans     101 eggs     108 g lard     111 cups oil     32 zucchinis     119 ml water     114 g red salmon     100 g dijon mustard     33 potatoes     Method.     Put potatoes into the mixing bowl. Put dijon mustard into the mixing     bowl. Put lard into the mixing bowl. Put red salmon into the mixing     bowl. Put oil into the mixing bowl. Put water into the mixing     bowl. Put zucchinis into the mixing bowl. Put oil into the mixing     bowl. Put lard into the mixing bowl. Put lard into the mixing     bowl. Put eggs into the mixing bowl. Put haricot beans into the mixing     bowl. Liquify contents of the mixing bowl. Pour contents of the mixing     bowl into the baking dish.     Serves 1.

Acme::Chef comes with a Chef interpreter and a compiler to turn Chef programs into Perl programs. I can't help wondering how much programmer time gets spent on this sort of thing.



Advanced Perl Programming
Advanced Perl Programming
ISBN: 0596004567
EAN: 2147483647
Year: 2004
Pages: 107
Authors: Simon Cozens

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net