Hack 94. Add Your Own Perl Syntax


Shape the language as you see fit.

Perl is a great language, but it's certainly not perfect. Sometimes bits and pieces of the implementation poke through. Sometimes the natural solution to a problem doesn't fit the existing language very well at all. Some problems are easier if you can just define them away.

Sometimes the simplest solution is just to change the syntax of Perl.

For example, it's frustrating that you can't specify a simple parameter list for a subroutine (without some gyrations, as in "Autodeclare Method Arguments" [Hack #47]):

my @NUMERAL_FOR    = (0..9,'A'..'Z'); sub convert_to_base($base, $number) {     my $converted  = "";     while ($number > 0)     {         $converted = $NUMERAL_FOR[$number % $base] . $converted;         $number    = int( $number / $base);     }     return $converted; }

Instead, you have to do it yourself:

sub convert_to_base {     my ($base, $number) = @_;   # <-- DIY parameter list     my $converted       = ''     while ($number > 0)     {         $converted      = $NUMERAL_FOR[$number % $base] . $converted;         $number         = int( $number / $base);     }     return $converted; }

This is why far too many people just write:

sub convert_to_base {     my $converted  = '';     while ($_[1] > 0)     {         $converted = $NUMERAL_FOR[$_[1] % $_[0]] . $converted;         $_[1]      = int( $_[1] / $_[0]);     }     return $converted; }

buying themselves a world of future maintenance pain in the process.

The Hack

Although Perl may not be perfect, it is perfectable. For example, recent versions of Perl provide a way to grab your program's source code before it even reaches the compiler, change it in some useful manner, and then send it on to be compiled and executed. The easiest way to do that is to write a module that uses the standard (in version 5.8.0 and later) Filter::Simple module:

package My::Filter; use Filter::Simple; FILTER_ONLY code => sub {     # The code from any program that uses this module     # is passed into this subroutine in $_.     # Whatever is in $_ at the end of this subroutine     # becomes the source code that the compiler eventually sees. }; 1;

Because the Perl compiler only sees the end result of these source filters, only that end result has to be valid Perl code. The original source code that the filter intercepts can be anything you like, as long as the filter can transform that anything into valid Perl.

For example, you could augment the Perl subroutine declaration syntax by creating a source filter that looks for subs with parameter lists and converts them to normal subs:

package Sub::With::Params; use Filter::Simple; # Regex that matches a valid Perl identifier (e.g. a sub name)... my $IDENT = qr/[^\\W\\d]\\w*/; # Apply this filter to the code of any program # that uses Sub::With::Params... FILTER_ONLY code => sub {     s{ ( sub \\s* $IDENT \\s* )   # Match any named sub declaration        (   \\( .*? \\)        )   # ...followed by a parameter list        (   \\s* \\{           )   # ...followed by a sub body     }     {$1$3 my $2 = \\@_;}gxs;     # Then move the param list inside the                                 # sub, converting it to a list of                                 # lexical variables initialized from @_ }; 1;

By setting up this filter module so that it expects subs with parameter lists and transforms them into regular subs that unpack @_ into lexicals, now you can write:

use Sub::With::Params; sub convert_to_base($base, $number) {     my $converted  = '';     while ($number > 0)     {         $converted = $NUMERAL_FOR[$number % $base] . $converted;         $number    = int( $number / $base);     }     return $converted; }

and have it work as you expect. Sub::With::Params will now intercept your source code on its way to the compiler and convert it to:

sub convert_to_base { my ($base, $number) = @_;     my $converted  = '';     while ($number > 0)     {         $converted = $NUMERAL_FOR[$number % $base] . $converted;         $number    = int( $number / $base);     }     return $converted; }



Perl Hacks
Perl Hacks: Tips & Tools for Programming, Debugging, and Surviving
ISBN: 0596526741
EAN: 2147483647
Year: 2004
Pages: 141

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