From: Jarkko Hietaniemi Date: Wed, 22 Nov 2000 22:46:39 +0000 (+0000) Subject: Add the simple frontend to Filter::Util:Call, Filter::Simple 0.01 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=b38acab94ba96981e7b8c028105420d79fa3580f;p=p5sagit%2Fp5-mst-13.2.git Add the simple frontend to Filter::Util:Call, Filter::Simple 0.01 by Damian Conway, as suggested by Paul Marquess. p4raw-id: //depot/perl@7821 --- diff --git a/lib/Filter/Simple.pm b/lib/Filter/Simple.pm new file mode 100644 index 0000000..fa883e6 --- /dev/null +++ b/lib/Filter/Simple.pm @@ -0,0 +1,248 @@ +package Filter::Simple; + +use vars qw{ $VERSION }; + +$VERSION = '0.01'; + +use Filter::Util::Call; +use Carp; + +sub import { + my $caller = caller; + my ($class, $filter) = @_; + croak "Usage: use Filter::Simple sub {...}" unless ref $filter eq CODE; + *{"${caller}::import"} = gen_filter_import($caller, $filter); + *{"${caller}::unimport"} = \*filter_unimport; +} + +sub gen_filter_import { + my ($class, $filter) = @_; + return sub { + my ($imported_class, @args) = @_; + filter_add( + sub { + my ($status, $off); + my $data = ""; + while ($status = filter_read()) { + if (m/^\s*no\s+$class\s*;\s*$/) { + $off=1; + last; + } + $data .= $_; + $_ = ""; + } + $_ = $data; + $filter->(@args) unless $status < 0; + $_ .= "no $class;\n" if $off; + return length; + } + ); + } +} + +sub filter_unimport { + filter_del(); +} + +1; + +__END__ + +=head1 NAME + +Filter::Simple - Simplified source filtering + + +=head1 SYNOPSIS + + # in MyFilter.pm: + + package MyFilter; + + use Filter::Simple sub { ... }; + + + # in user's code: + + use MyFilter; + + # this code is filtered + + no MyFilter; + + # this code is not + + +=head1 DESCRIPTION + +=head2 The Problem + +Source filtering is an immensely powerful feature of recent versions of Perl. +It allows one to extend the language itself (e.g. the Switch module), to +simplify the language (e.g. Language::Pythonesque), or to completely recast the +language (e.g. Lingua::Romana::Perligata). Effectively, it allows one to use +the full power of Perl as its own, recursively applied, macro language. + +The excellent Filter::Util::Call module (by Paul Marquess) provides a +usable Perl interface to source filtering, but it is often too powerful +and not nearly as simple as it could be. + +To use the module it is necessary to do the following: + +=over 4 + +=item 1. + +Download, build, and install the Filter::Util::Call module. + +=item 2. + +Set up a module that does a C. + +=item 3. + +Within that module, create an C subroutine. + +=item 4. + +Within the C subroutine do a call to C, passing +it either a subroutine reference. + +=item 5. + +Within the subroutine reference, call C or C +to "prime" $_ with source code data from the source file that will +C your module. Check the status value returned to see if any +source code was actually read in. + +=item 6. + +Process the contents of $_ to change the source code in the desired manner. + +=item 7. + +Return the status value. + +=item 8. + +If the act of unimporting your module (via a C) should cause source +code filtering to cease, create an C subroutine, and have it call +C. Make sure that the call to C or +C in step 5 will not accidentally read past the +C. Effectively this limits source code filters to line-by-line +operation, unless the C subroutine does some fancy +pre-pre-parsing of the source code it's filtering. + +=back + +For example, here is a minimal source code filter in a module named +BANG.pm. It simply converts every occurrence of the sequence C +to the sequence C in any piece of code following a +C statement (until the next C statement, if any): + + package BANG; + + use Filter::Util::Call ; + + sub import { + filter_add( sub { + my $caller = caller; + my ($status, $no_seen, $data); + while ($status = filter_read()) { + if (/^\s*no\s+$caller\s*;\s*$/) { + $no_seen=1; + last; + } + $data .= $_; + $_ = ""; + } + $_ = $data; + s/BANG\s+BANG/die 'BANG' if \$BANG/g + unless $status < 0; + $_ .= "no $class;\n" if $no_seen; + return 1; + }) + } + + sub unimport { + filter_del(); + } + + 1 ; + +Given this level of complexity, it's perhaps not surprising that source +code filtering is still a mystery to most users. + + +=head2 A Solution + +The Filter::Simple module provides a vastly simplified interface to +Filter::Util::Call; one that is sufficient for most common cases. + +Instead of the above process, with Filter::Simple the task of setting up +a source code filter is reduced to: + +=over 4 + +=item 1. + +Set up a module that does a C. + +=item 2. + +Within the anonymous subroutine passed to C, process the +contents of $_ to change the source code in the desired manner. + +=back + +In other words, the previous example, would become: + + package BANG; + + use Filter::Simple sub { + s/BANG\s+BANG/die 'BANG' if \$BANG/g; + }; + + 1 ; + + +=head2 How it works + +The Filter::Simple module exports into the package that Cs it (e.g. +package "BANG" in the above example) two automagically constructed +subroutines -- C and C -- which take care of all the +nasty details. + +In addition, the generated C subroutine passes its own argument +list to the filtering subroutine, so the BANG.pm filter could easily +be made parametric: + + package BANG; + + use Filter::Simple sub { + my ($die_msg, $var_name) = @_; + s/BANG\s+BANG/die '$die_msg' if \${$var_name}/g; + }; + + # and in some user code: + + use BANG "BOOM", "BAM; # "BANG BANG" becomes: die 'BOOM' if $BAM + + +The specified filtering subroutine is called every time a C +is encountered, and passed all the source code following that call, +up to either the next C call or the end of the source file +(whichever occurs first). Currently, any C call must appear +by itself on a separate line, or it is ignored. + + +=head1 AUTHOR + +Damian Conway (damian@conway.org) + +=head1 COPYRIGHT + + Copyright (c) 2000, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed +and/or modified under the terms of the Perl Artistic License + (see http://www.perl.com/perl/misc/Artistic.html)