From: Jarkko Hietaniemi Date: Mon, 3 Sep 2001 12:17:30 +0000 (+0000) Subject: Damian-o-rama: upgrade to Attribute::Handlers 0.75, X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=55a1c97c34bea81a888ebe7db8a5607b1b7b9a39;p=p5sagit%2Fp5-mst-13.2.git Damian-o-rama: upgrade to Attribute::Handlers 0.75, Filter::Simple 0.61, NEXT 0.02, Switch 2.05, and Text::Balanced 1.86. p4raw-id: //depot/perl@11842 --- diff --git a/MANIFEST b/MANIFEST index a4f2b75..3a93a81 100644 --- a/MANIFEST +++ b/MANIFEST @@ -744,8 +744,8 @@ lib/Attribute/Handlers/demo/demo_range.pl Attribute::Handlers demo lib/Attribute/Handlers/demo/demo_rawdata.pl Attribute::Handlers demo lib/Attribute/Handlers/demo/Descriptions.pm Attribute::Handlers demo lib/Attribute/Handlers/demo/MyClass.pm Attribute::Handlers demo -lib/Attribute/Handlers/README Attribute::Handlers -lib/Attribute/Handlers/test.pl See if Attribute::Handlers works +lib/Attribute/Handlers/README Attribute::Handlers +lib/Attribute/Handlers/t/multi.t See if Attribute::Handlers works lib/attributes.pm For "sub foo : attrlist" lib/AutoLoader.pm Autoloader base class lib/AutoLoader.t See if AutoLoader works @@ -910,7 +910,9 @@ lib/FileHandle.pm Backward-compatible front end to IO extension lib/FileHandle.t See if FileHandle works lib/filetest.pm For "use filetest" lib/Filter/Simple.pm Simple frontend to Filter::Util::Call -lib/Filter/Simple/test.pl See if Filter::Simple works +lib/Filter/Simple/Changes Filter::Simple +lib/Filter/Simple/README Filter::Simple +lib/Filter/Simple/t/filter.t See if Filter::Simple works lib/find.pl A find emulator--used by find2perl lib/FindBin.pm Find name of currently executing program lib/FindBin.t See if FindBin works @@ -1053,7 +1055,9 @@ lib/Net/t/require.t libnet lib/Net/t/smtp.t libnet lib/Net/Time.pm libnet lib/newgetopt.pl A perl library supporting long option parsing -lib/NEXT.pm Pseudo-class NEXT for method redispatch +lib/NEXT.pm Pseudo-class NEXT for method redispatch +lib/NEXT/Changes NEXT +lib/NEXT/README NEXT lib/NEXT/test.pl See if NEXT works lib/open.pm Pragma to specify default I/O disciplines lib/open2.pl Open a two-ended pipe (uses IPC::Open2) @@ -1096,10 +1100,11 @@ lib/strict.t See if strictures work lib/subs.pm Declare overriding subs lib/subs.t See if subroutine pseudo-importation works lib/Switch.pm Switch for Perl -lib/Switch/Changes Switch for Perl -lib/Switch/README Switch for Perl -lib/Switch/t/given_when.t See if Perl 6 given (switch) works -lib/Switch/t/switch_case.t See if Perl 5 switch works +lib/Switch/Changes Switch +lib/Switch/README Switch +lib/Switch/t/given.t See if Perl 6 given (switch) works +lib/Switch/t/nested.t See if nested switch works +lib/Switch/t/switch.t See if Perl 5 switch works lib/Symbol.pm Symbol table manipulation routines lib/Symbol.t See if Symbol works lib/syslog.pl Perl library supporting syslogging @@ -1141,15 +1146,16 @@ lib/Test/t/todo.t See if Test works lib/Text/Abbrev.pm An abbreviation table builder lib/Text/Abbrev.t Test Text::Abbrev lib/Text/Balanced.pm Text::Balanced -lib/Text/Balanced.pod Text::Balanced -lib/Text/Balanced/t/genxt.t See if Text::Balanced works -lib/Text/Balanced/t/xbrak.t See if Text::Balanced works -lib/Text/Balanced/t/xcode.t See if Text::Balanced works -lib/Text/Balanced/t/xdeli.t See if Text::Balanced works -lib/Text/Balanced/t/xmult.t See if Text::Balanced works -lib/Text/Balanced/t/xquot.t See if Text::Balanced works -lib/Text/Balanced/t/xtagg.t See if Text::Balanced works -lib/Text/Balanced/t/xvari.t See if Text::Balanced works +lib/Text/Balanced/Changes Text::Balanced +lib/Text/Balanced/README Text::Balanced +lib/Text/Balanced/t/gentag.t See if Text::Balanced works +lib/Text/Balanced/t/extbrk.t See if Text::Balanced works +lib/Text/Balanced/t/extcbk.t See if Text::Balanced works +lib/Text/Balanced/t/extdel.t See if Text::Balanced works +lib/Text/Balanced/t/extmul.t See if Text::Balanced works +lib/Text/Balanced/t/extqlk.t See if Text::Balanced works +lib/Text/Balanced/t/exttag.t See if Text::Balanced works +lib/Text/Balanced/t/extvar.t See if Text::Balanced works lib/Text/ParseWords.pm Perl module to split words on arbitrary delimiter lib/Text/ParseWords.t See if Text::ParseWords works lib/Text/Soundex.pm Perl module to implement Soundex diff --git a/lib/Attribute/Handlers.pm b/lib/Attribute/Handlers.pm index b71a36d..dbd1bf4 100644 --- a/lib/Attribute/Handlers.pm +++ b/lib/Attribute/Handlers.pm @@ -2,8 +2,8 @@ package Attribute::Handlers; use 5.006; use Carp; use warnings; -$VERSION = '0.70'; -$DB::single=1; +$VERSION = '0.75'; +# $DB::single=1; my %symcache; sub findsym { @@ -36,19 +36,23 @@ sub _usage_AH_ { croak "Usage: use $_[0] autotie => {AttrName => TieClassName,...}"; } +my $qual_id = qr/^[_a-z]\w*(::[_a-z]\w*)*$/i; + sub import { my $class = shift @_; return unless $class eq "Attribute::Handlers"; while (@_) { my $cmd = shift; - if ($cmd eq 'autotie') { + if ($cmd =~ /^autotie((?:ref)?)$/) { + my $tiedata = '($was_arrayref ? $data : @$data)'; + $tiedata = ($1 ? '$ref, ' : '') . $tiedata; my $mapping = shift; _usage_AH_ $class unless ref($mapping) eq 'HASH'; while (my($attr, $tieclass) = each %$mapping) { - $tieclass =~ s/^([_a-z]\w*(::[_a-z]\w*))(.*)/$1/is; + $tieclass =~ s/^([_a-z]\w*(::[_a-z]\w*)*)(.*)/$1/is; my $args = $3||'()'; - usage $class unless $attr =~ m/^[_a-z]\w*(::[_a-z]\w*)*$/i - && $tieclass =~ m/^[_a-z]\w*(::[_a-z]\w*)/i + _usage_AH_ $class unless $attr =~ $qual_id + && $tieclass =~ $qual_id && eval "use base $tieclass; 1"; if ($tieclass->isa('Exporter')) { local $Exporter::ExportLevel = 2; @@ -59,14 +63,12 @@ sub import { eval qq{ sub $attr : ATTR(VAR) { my (\$ref, \$data) = \@_[2,4]; - \$data = [ \$data ] unless ref \$data eq 'ARRAY'; - # print \$ref, ": "; - # use Data::Dumper 'Dumper'; - # print Dumper [ [\$ref, \$data] ]; + my \$was_arrayref = ref \$data eq 'ARRAY'; + \$data = [ \$data ] unless \$was_arrayref; my \$type = ref(\$ref)||"value (".(\$ref||"").")"; - (\$type eq 'SCALAR')? tie \$\$ref,'$tieclass',\@\$data - :(\$type eq 'ARRAY') ? tie \@\$ref,'$tieclass',\@\$data - :(\$type eq 'HASH') ? tie \%\$ref,'$tieclass',\@\$data + (\$type eq 'SCALAR')? tie \$\$ref,'$tieclass',$tiedata + :(\$type eq 'ARRAY') ? tie \@\$ref,'$tieclass',$tiedata + :(\$type eq 'HASH') ? tie \%\$ref,'$tieclass',$tiedata : die "Can't autotie a \$type\n" } 1 } or die "Internal error: $@"; @@ -91,10 +93,10 @@ sub _resolve_lastattr { } sub AUTOLOAD { - my ($class) = @_; - $AUTOLOAD =~ /_ATTR_(.*?)_(.*)/ or + my ($class) = $AUTOLOAD =~ m/(.*)::/g; + $AUTOLOAD =~ m/_ATTR_(.*?)_(.*)/ or croak "Can't locate class method '$AUTOLOAD' via package '$class'"; - croak "Attribute handler '$2' doesn't handle $1 attributes"; + croak "Attribute handler '$3' doesn't handle $2 attributes"; } sub DESTROY {} @@ -106,7 +108,7 @@ sub _gen_handler_AH_() { _resolve_lastattr; my ($pkg, $ref, @attrs) = @_; foreach (@attrs) { - my ($attr, $data) = /^([a-z_]\w*)(?:[(](.*)[)])?$/i or next; + my ($attr, $data) = /^([a-z_]\w*)(?:[(](.*)[)])?$/is or next; if ($attr eq 'ATTR') { $data ||= "ANY"; $raw{$ref} = $data =~ s/\s*,?\s*RAWDATA\s*,?\s*//; @@ -185,8 +187,8 @@ Attribute::Handlers - Simpler definition of attribute handlers =head1 VERSION -This document describes version 0.70 of Attribute::Handlers, -released June 3, 2001. +This document describes version 0.75 of Attribute::Handlers, +released September 3, 2001. =head1 SYNOPSIS @@ -546,11 +548,36 @@ C<__CALLER__>, which may be specified as the qualifier of an attribute: package Tie::Me::Kangaroo:Down::Sport; - use Attribute::Handler autotie => { __CALLER__::Roo => __PACKAGE__ }; + use Attribute::Handlers autotie => { __CALLER__::Roo => __PACKAGE__ }; This causes Attribute::Handlers to define the C attribute in the package that imports the Tie::Me::Kangaroo:Down::Sport module. +=head3 Passing the tied object to C + +Occasionally it is important to pass a reference to the object being tied +to the TIESCALAR, TIEHASH, etc. that ties it. + +The C mechanism supports this too. The following code: + + use Attribute::Handlers autotieref => { Selfish => Tie::Selfish }; + my $var : Selfish(@args); + +has the same effect as: + + tie my $var, 'Tie::Selfish', @args; + +But when C<"autotieref"> is used instead of C<"autotie">: + + use Attribute::Handlers autotieref => { Selfish => Tie::Selfish }; + my $var : Selfish(@args); + +the effect is to pass the C call an extra reference to the variable +being tied: + + tie my $var, 'Tie::Selfish', \$var, @args; + + =head1 EXAMPLES @@ -752,5 +779,4 @@ Bug reports and other feedback are most welcome. Copyright (c) 2001, 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) + and/or modified under the same terms as Perl itself. diff --git a/lib/Attribute/Handlers/Changes b/lib/Attribute/Handlers/Changes index 9aa870e..4df4edb 100644 --- a/lib/Attribute/Handlers/Changes +++ b/lib/Attribute/Handlers/Changes @@ -26,7 +26,7 @@ Revision history for Perl extension Attribute::Handlers - Critical doc patch -0.65 Sun Jun 3 07:40:03 2001 +0.70 Sun Jun 3 07:40:03 2001 - Added __CALLER__ pseudo class for 'autotie' @@ -44,3 +44,20 @@ Revision history for Perl extension Attribute::Handlers - Cleaned up interactions with other class hierarchies (due to being base class of UNIVERSAL) + + +0.75 Mon Sep 3 09:07:08 2001 + + - Cleaned up AUTOLOAD + + - Numerous bug fixes (thanks Pete) + + - Fixed handling of attribute data that includes a newline (thanks Pete) + + - Added "autotieref" option (thanks Pete) + + - Switched off $DB::single + + - Changed licence for inclusion in core distribution + + - Fixed 'autotie' for tied classes with multi-level names (thanks Jeff) diff --git a/lib/Attribute/Handlers/README b/lib/Attribute/Handlers/README index 2de8de9..e8f0783 100644 --- a/lib/Attribute/Handlers/README +++ b/lib/Attribute/Handlers/README @@ -1,5 +1,5 @@ ============================================================================== - Release of version 0.70 of Attribute::Handlers + Release of version 0.75 of Attribute::Handlers ============================================================================== @@ -46,15 +46,28 @@ AUTHOR COPYRIGHT Copyright (c) 2001, 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) + and/or modified under the same terms as Perl itself. ============================================================================== -CHANGES IN VERSION 0.70 +CHANGES IN VERSION 0.75 + + + - Cleaned up AUTOLOAD + + - Numerous bug fixes (thanks Pete) + + - Fixed handling of attribute data that includes a newline (thanks Pete) + + - Added "autotieref" option (thanks Pete) + + - Switched off $DB::single + + - Changed licence for inclusion in core distribution + + - Fixed 'autotie' for tied classes with multi-level names (thanks Jeff) -(No changes have been documented for this version) ============================================================================== diff --git a/lib/Attribute/Handlers/demo/demo.pl b/lib/Attribute/Handlers/demo/demo.pl index 02fa64a..7a269e8 100755 --- a/lib/Attribute/Handlers/demo/demo.pl +++ b/lib/Attribute/Handlers/demo/demo.pl @@ -4,7 +4,7 @@ use v5.6.0; use base Demo; my $y : Demo :This($this) = sub : Demo(1,2,3) {}; -sub x : Demo(4,5,6) :Multi {} +sub x : Demo(4, 5, 6) :Multi {} my %z : Demo(hash) :Multi(method,maybe); # my %a : NDemo(hash); diff --git a/lib/Attribute/Handlers/demo/demo_cycle.pl b/lib/Attribute/Handlers/demo/demo_cycle.pl index 954316f..771de94 100755 --- a/lib/Attribute/Handlers/demo/demo_cycle.pl +++ b/lib/Attribute/Handlers/demo/demo_cycle.pl @@ -1,9 +1,15 @@ -use Attribute::Handlers autotie => { Cycle => Tie::Cycle }; +package Selfish; -my $next : Cycle(['A'..'Z']); +sub TIESCALAR { + use Data::Dumper 'Dumper'; + print Dumper [ \@_ ]; + bless {}, $_[0]; +} -print tied $next, "\n"; +package main; -while (<>) { - print $next, "\n"; -} +use Attribute::Handlers autotieref => { Selfish => Selfish }; + +my $next : Selfish("me"); + +print "$next\n"; diff --git a/lib/Attribute/Handlers/t/multi.t b/lib/Attribute/Handlers/t/multi.t new file mode 100644 index 0000000..5f4e59e --- /dev/null +++ b/lib/Attribute/Handlers/t/multi.t @@ -0,0 +1,131 @@ +END {print "not ok 1\n" unless $loaded;} +use v5.6.0; +use Attribute::Handlers; +$loaded = 1; + +CHECK { $main::phase++ } + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +sub ok { $::count++; push @::results, [$_[1], $_[0]?"":"not "]; } + +END { print "1..$::count\n"; + print map "$_->[1]ok $_->[0]\n", + sort {$a->[0]<=>$b->[0]} + grep $_->[0], @::results } + +package Test; +use warnings; +no warnings 'redefine'; + +sub UNIVERSAL::Lastly :ATTR(INIT) { ::ok $_[4][0] && $main::phase, $_[4][1] } + +sub UNIVERSAL::Okay :ATTR(BEGIN) { ::ok $_[4][0] && !$main::phase, $_[4][1] } + +sub Dokay :ATTR(SCALAR) { ::ok @{$_[4]} } +sub Dokay :ATTR(HASH) { ::ok @{$_[4]} } +sub Dokay :ATTR(ARRAY) { ::ok @{$_[4]} } +sub Dokay :ATTR(CODE) { ::ok @{$_[4]} } + +sub Vokay :ATTR(VAR) { ::ok @{$_[4]} } + +sub Aokay :ATTR(ANY) { ::ok @{$_[4]} } + +package main; +use warnings; + +my $x1 :Lastly(1,41); +my @x1 :Lastly(1=>42); +my %x1 :Lastly(1,43); +sub x1 :Lastly(1,44) {} + +my Test $x2 :Dokay(1,5); + +package Test; +my $x3 :Dokay(1,6); +my Test $x4 :Dokay(1,7); +sub x3 :Dokay(1,8) {} + +my $y1 :Okay(1,9); +my @y1 :Okay(1,10); +my %y1 :Okay(1,11); +sub y1 :Okay(1,12) {} + +my $y2 :Vokay(1,13); +my @y2 :Vokay(1,14); +my %y2 :Vokay(1,15); +# BEGIN {eval 'sub y2 :Vokay(0,16) {}; 1' or +::ok(1,16); +# } + +my $z :Aokay(1,17); +my @z :Aokay(1,18); +my %z :Aokay(1,19); +sub z :Aokay(1,20) {}; + +package DerTest; +use base 'Test'; +use warnings; + +my $x5 :Dokay(1,21); +my Test $x6 :Dokay(1,22); +sub x5 :Dokay(1,23); + +my $y3 :Okay(1,24); +my @y3 :Okay(1,25); +my %y3 :Okay(1,26); +sub y3 :Okay(1,27) {} + +package Unrelated; + +my $x11 :Okay(1,1); +my @x11 :Okay(1=>2); +my %x11 :Okay(1,3); +sub x11 :Okay(1,4) {} + +BEGIN { eval 'my $x7 :Dokay(0,28)' or ::ok(1,28); } +my Test $x8 :Dokay(1,29); +eval 'sub x7 :Dokay(0,30) {}' or ::ok(1,30); + + +package Tie::Loud; + +sub TIESCALAR { ::ok(1,31); bless {}, $_[0] } +sub FETCH { ::ok(1,32); return 1 } +sub STORE { ::ok(1,33); return 1 } + +package Tie::Noisy; + +sub TIEARRAY { ::ok(1,$_[1]); bless {}, $_[0] } +sub FETCH { ::ok(1,35); return 1 } +sub STORE { ::ok(1,36); return 1 } +sub FETCHSIZE { 100 } + +package Tie::Row::dy; + +sub TIEHASH { ::ok(1,$_[1]); bless {}, $_[0] } +sub FETCH { ::ok(1,38); return 1 } +sub STORE { ::ok(1,39); return 1 } + +package main; + +eval 'sub x7 :ATTR(SCALAR) :ATTR(CODE) {}' and ::ok(0,40) or ::ok(1,40); + +use Attribute::Handlers autotie => { Other::Loud => Tie::Loud, + Noisy => Tie::Noisy, + UNIVERSAL::Rowdy => Tie::Row::dy, + }; + +my Other $loud : Loud; +$loud++; + +my @noisy : Noisy(34); +$noisy[0]++; + +my %rowdy : Rowdy(37); +$rowdy{key}++; + diff --git a/lib/Filter/Simple.pm b/lib/Filter/Simple.pm index 9d88bf1..a92615d 100644 --- a/lib/Filter/Simple.pm +++ b/lib/Filter/Simple.pm @@ -2,7 +2,7 @@ package Filter::Simple; use vars qw{ $VERSION }; -$VERSION = '0.60'; +$VERSION = '0.61'; use Filter::Util::Call; use Carp; @@ -15,8 +15,6 @@ sub import { sub FILTER (&;$) { my $caller = caller; my ($filter, $terminator) = @_; - croak "Usage: use Filter::Simple sub {...}, $terminator_opt;" - unless ref $filter eq CODE; *{"${caller}::import"} = gen_filter_import($caller,$filter,$terminator); *{"${caller}::unimport"} = \*filter_unimport; } @@ -110,7 +108,7 @@ To use the module it is necessary to do the following: =item 1. Download, build, and install the Filter::Util::Call module. -(If you have Perl 5.7.1 or later you already have Filter::Util::Call.) +(If you have Perl 5.7.1 or later, this is already done for you.) =item 2. @@ -203,10 +201,15 @@ a source code filter is reduced to: =item 1. +Download and install the Filter::Simple module. +(If you have Perl 5.7.1 or later, this is already done for you.) + +=item 2. + Set up a module that does a C and then calls C. -=item 2. +=item 3. Within the anonymous subroutine or block that is passed to C, process the contents of $_ to change the source code in @@ -298,6 +301,35 @@ This is exactly the same as: except that the C subroutine is not exported by Filter::Simple. +=head2 Using Filter::Simple and Exporter together + +You can't directly use Exporter when Filter::Simple. + +Filter::Simple generates an C subroutine for your module +(which hides the one inherited from Exporter). + +The C code you specify will, however, receive the C's argument +list, so you can use that filter block as your C subroutine. + +You'll need to call C from your C code +to make it work correctly. + +For example: + + use Filter::Simple; + + use base Exporter; + @EXPORT = qw(foo); + @EXPORT_OK = qw(bar); + + sub foo { print "foo\n" } + sub bar { print "bar\n" } + + FILTER { + # Your filtering code here + __PACKAGE__->export_to_level(2,undef,@_); + } + =head2 How it works @@ -338,7 +370,6 @@ 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) + Copyright (c) 2000-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. diff --git a/lib/Filter/Simple/Changes b/lib/Filter/Simple/Changes new file mode 100644 index 0000000..e15c37b --- /dev/null +++ b/lib/Filter/Simple/Changes @@ -0,0 +1,32 @@ +Revision history for Perl extension Filter::Simple + +0.01 Tue Sep 19 20:18:44 2000 + - original version; created by h2xs 1.18 + +0.01 Tue Sep 26 09:30:14 2000 + + - Changed module name to Filter::Simple + + +0.60 Wed May 2 07:38:18 2001 + + - Fixed POD nit (thanks Dean) + + - Added optional second argument to import to allow + terminator to be changed (thanks Brad) + + - Fixed bug when empty filtered text was appended to (thanks Brad) + + - Added FILTER as the normal mechanism for specifying filters + + +0.61 Mon Sep 3 08:25:21 2001 + + - Added a real test suite (thanks Jarkko) + + - Changed licence to facilitate inclusion in + core distribution + + - Added documentation for using F::S and Exporter together + + diff --git a/lib/Filter/Simple/README b/lib/Filter/Simple/README new file mode 100644 index 0000000..03e4599 --- /dev/null +++ b/lib/Filter/Simple/README @@ -0,0 +1,62 @@ +============================================================================== + Release of version 0.61 of Filter::Simple +============================================================================== + + +NAME + Filter::Simple - Simplified source filtering + +SYNOPSIS + # in MyFilter.pm: + + package MyFilter; + + use Filter::Simple; + + FILTER { ... }; + + # or just: + # + # use Filter::Simple sub { ... }; + + + # in user's code: + + use MyFilter; + + # this is filtered + + no MyFilter; + + # this is not + + +DESCRIPTION + The Filter::Simple module provides a simplified interface to + Filter::Util::Call; one that is sufficient for most common cases. + +AUTHOR + Damian Conway (damian@conway.org) + +COPYRIGHT + Copyright (c) 2000-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. + + +============================================================================== + +CHANGES IN VERSION 0.61 + +(No changes have been documented for this version) + +============================================================================== + +AVAILABILITY + +Filter::Simple has been uploaded to the CPAN +and is also available from: + + http://www.csse.monash.edu.au/~damian/CPAN/Filter-Simple.tar.gz + +============================================================================== diff --git a/lib/Filter/Simple/test.pl b/lib/Filter/Simple/t/filter.t similarity index 65% rename from lib/Filter/Simple/test.pl rename to lib/Filter/Simple/t/filter.t index 3fb3270..47ee7d7 100644 --- a/lib/Filter/Simple/test.pl +++ b/lib/Filter/Simple/t/filter.t @@ -1,14 +1,6 @@ -#!./perl - -BEGIN { - chdir('t') if -d 't'; - @INC = 'lib'; -} - +use FilterTest qr/not ok/ => "ok", fail => "ok"; print "1..6\n"; -use MyFilter qr/not ok/ => "ok", fail => "ok"; - sub fail { print "fail ", $_[0], "\n" } print "not ok 1\n"; @@ -20,7 +12,7 @@ fail(3); print "not " unless "whatnot okapi" eq "whatokapi"; print "ok 5\n"; -no MyFilter; +no FilterTest; print "not " unless "not ok" =~ /^not /; print "ok 6\n"; diff --git a/lib/NEXT.pm b/lib/NEXT.pm index 144b145..e41065c 100644 --- a/lib/NEXT.pm +++ b/lib/NEXT.pm @@ -35,14 +35,23 @@ sub AUTOLOAD } no strict 'refs'; @{$NEXT::NEXT{$self,$wanted_method}} = - map { *{"${_}::$caller_method"}{CODE}||() } @forebears; + map { *{"${_}::$caller_method"}{CODE}||() } @forebears + unless $wanted_method eq 'AUTOLOAD'; @{$NEXT::NEXT{$self,$wanted_method}} = - map { *{"${_}::AUTOLOAD"}{CODE}||() } @forebears - unless @{$NEXT::NEXT{$self,$wanted_method}}; + map { (*{"${_}::AUTOLOAD"}{CODE}) ? + "${_}::AUTOLOAD" : () } @forebears + unless @{$NEXT::NEXT{$self,$wanted_method}||[]}; + } + my $call_method = shift @{$NEXT::NEXT{$self,$wanted_method}}; + return unless defined $call_method; + if (ref $call_method eq 'CODE') { + return shift()->$call_method(@_) + } + else { # AN AUTOLOAD + no strict 'refs'; + ${$call_method} = $caller_method eq 'AUTOLOAD' && ${"${caller_class}::AUTOLOAD"} || $wanted; + return $call_method->(@_); } - $wanted_method = shift @{$NEXT::NEXT{$self,$wanted_method}}; - return shift()->$wanted_method(@_) if $wanted_method; - return; } 1; @@ -95,8 +104,14 @@ that uses it. If a method C calls C<$self->NEXT::m()>, the call to C is redispatched as if the calling method had not originally been found. In other words, a call to C<$self->NEXT::m()> resumes the depth-first, -left-to-right search of parent classes that resulted in the original -call to C. +left-to-right search of C<$self>'s class hierarchy that resulted in the +original call to C. + +Note that this is not the same thing as C<$self->SUPER::m()>, which +begins a new dispatch that is restricted to searching the ancestors +of the current class. C<$self->NEXT::m()> can backtrack +past the current class -- to look for a suitable method in other +ancestors of C<$self> -- whereas C<$self->SUPER::m()> cannot. A typical use would be in the destructors of a class hierarchy, as illustrated in the synopsis above. Each class in the hierarchy @@ -134,7 +149,6 @@ Comment, suggestions, and patches welcome. =head1 COPYRIGHT - Copyright (c) 2000, Damian Conway. All Rights Reserved. + Copyright (c) 2000-2001, 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) + and/or modified under the same terms as Perl itself. diff --git a/lib/NEXT/Changes b/lib/NEXT/Changes new file mode 100644 index 0000000..bb5e27a --- /dev/null +++ b/lib/NEXT/Changes @@ -0,0 +1,22 @@ +Revision history for Perl extension NEXT.pm. + +0.01 Tue Apr 10 18:27:00 EST 2001 + + - original version + + +0.01 Thu Apr 12 17:06:49 2001 + + - Documented the difference between NEXT and SUPER (thanks Ken) + + + +0.01 Thu Apr 12 17:15:42 2001 + + + +0.02 Mon Sep 3 07:52:27 2001 + + - Fixed setting of $AUTOLOAD in NEXT'd AUTOLOADS (thanks Leonid) + + - Changed licence for inclusion in core distribution diff --git a/lib/NEXT/README b/lib/NEXT/README new file mode 100644 index 0000000..471b2bb --- /dev/null +++ b/lib/NEXT/README @@ -0,0 +1,71 @@ +============================================================================== + Release of version 0.02 of NEXT +============================================================================== + + +NAME + + NEXT - Pseudo class for method redispatch + + +DESCRIPTION + + NEXT.pm adds a pseudoclass named C to any program that + uses it. If a method C calls C<$self->NEXT::m()>, the call to + C is redispatched as if the calling method had not originally + been found. + + In other words, a call to C<$self->NEXT::m()> resumes the + depth-first, left-to-right search of parent classes that + resulted in the original call to C. + + Note that this is not the same thing as C<$self->SUPER::m()>, which + begins a new dispatch that is restricted to searching the ancestors + of the current class. C<$self->NEXT::m()> can backtrack past + the current class -- to look for a suitable method in other + ancestors of C<$self> -- whereas C<$self->SUPER::m()> cannot. + + An particularly interesting use of redispatch is in + C'ed methods. If such a method determines that it is + not able to handle a particular call, it may choose to + redispatch that call, in the hope that some other C + (above it, or to its left) might do better. + + Note that it is a fatal error for any method (including C) + to attempt to redispatch any method except itself. For example: + + sub D::oops { $_[0]->NEXT::other_method() } # BANG! + + +AUTHOR + + Damian Conway (damian@conway.org) + + +COPYRIGHT + + Copyright (c) 2000-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. + + +============================================================================== + +CHANGES IN VERSION 0.02 + + + - Fixed setting of $AUTOLOAD in NEXT'd AUTOLOADS (thanks Leonid) + + - Changed licence for inclusion in core distribution + + +============================================================================== + +AVAILABILITY + +NEXT has been uploaded to the CPAN +and is also available from: + + http://www.csse.monash.edu.au/~damian/CPAN/NEXT.tar.gz + +============================================================================== diff --git a/lib/NEXT/test.pl b/lib/NEXT/test.pl index 6328fd1..0ba0b66 100644 --- a/lib/NEXT/test.pl +++ b/lib/NEXT/test.pl @@ -1,12 +1,6 @@ #! /usr/local/bin/perl -w - -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - -BEGIN { print "1..20\n"; } +BEGIN { print "1..25\n"; } use NEXT; @@ -18,29 +12,32 @@ sub A::DESTROY { $_[0]->NEXT::DESTROY() } package B; use base qw( A ); -sub B::AUTOLOAD { return ( 9, $_[0]->NEXT::AUTOLOAD() ) } +sub B::AUTOLOAD { return ( 9, $_[0]->NEXT::AUTOLOAD() ) + if $AUTOLOAD =~ /.*(missing_method|secondary)/ } sub B::DESTROY { $_[0]->NEXT::DESTROY() } package C; -sub C::DESTROY { print "ok 18\n"; $_[0]->NEXT::DESTROY() } +sub C::DESTROY { print "ok 23\n"; $_[0]->NEXT::DESTROY() } package D; @D::ISA = qw( B C E ); sub D::method { return ( 2, $_[0]->NEXT::method() ) } sub D::AUTOLOAD { return ( 8, $_[0]->NEXT::AUTOLOAD() ) } -sub D::DESTROY { print "ok 17\n"; $_[0]->NEXT::DESTROY() } +sub D::DESTROY { print "ok 22\n"; $_[0]->NEXT::DESTROY() } sub D::oops { $_[0]->NEXT::method() } +sub D::secondary { return ( 17, 18, map { $_+10 } $_[0]->NEXT::secondary() ) } package E; @E::ISA = qw( F G ); sub E::method { return ( 4, $_[0]->NEXT::method(), $_[0]->NEXT::method() ) } -sub E::AUTOLOAD { return ( 10, $_[0]->NEXT::AUTOLOAD() ) } -sub E::DESTROY { print "ok 19\n"; $_[0]->NEXT::DESTROY() } +sub E::AUTOLOAD { return ( 10, $_[0]->NEXT::AUTOLOAD() ) + if $AUTOLOAD =~ /.*(missing_method|secondary)/ } +sub E::DESTROY { print "ok 24\n"; $_[0]->NEXT::DESTROY() } package F; sub F::method { return ( 5 ) } -sub F::AUTOLOAD { return ( 11 ) } -sub F::DESTROY { print "ok 20\n" } +sub F::AUTOLOAD { return ( 11 ) if $AUTOLOAD =~ /.*(missing_method|secondary)/ } +sub F::DESTROY { print "ok 25\n" } package G; sub G::method { return ( 6 ) } @@ -71,19 +68,20 @@ eval { $obj->oops() } && print "not "; print "ok 12\n"; # AUTOLOAD'ED METHOD CAN'T REDISPATCH TO NAMED METHOD (ok 13) -eval q{ - package C; - sub AUTOLOAD { $_[0]->NEXT::method() }; + +eval { + local *C::AUTOLOAD = sub { $_[0]->NEXT::method() }; + *C::AUTOLOAD = *C::AUTOLOAD; + eval { $obj->missing_method(); } && print "not "; }; -eval { $obj->missing_method(); } && print "not "; print "ok 13\n"; # NAMED METHOD CAN'T REDISPATCH TO AUTOLOAD'ED METHOD (ok 14) -eval q{ - package C; - sub method { $_[0]->NEXT::AUTOLOAD() }; +eval { + *C::method = sub{ $_[0]->NEXT::AUTOLOAD() }; + *C::method = *C::method; + eval { $obj->method(); } && print "not "; }; -eval { $obj->method(); } && print "not "; print "ok 14\n"; # BASE CLASS METHODS ONLY REDISPATCHED WITHIN HIERARCHY (ok 15..16) @@ -96,4 +94,8 @@ print "ok 15\n"; print "not " unless @val==1 && $val[0]==9; print "ok 16\n"; -# CAN REDISPATCH DESTRUCTORS (ok 17..20) +# TEST SECONDARY AUTOLOAD REDISPATCH (ok 17..21) +@vals = $obj->secondary(); +print map "ok $_\n", @vals; + +# CAN REDISPATCH DESTRUCTORS (ok 22..25) diff --git a/lib/Switch.pm b/lib/Switch.pm index 405d201..7e6e577 100644 --- a/lib/Switch.pm +++ b/lib/Switch.pm @@ -4,7 +4,7 @@ use strict; use vars qw($VERSION); use Carp; -$VERSION = '2.04'; +$VERSION = '2.05'; # LOAD FILTERING MODULE... @@ -473,8 +473,8 @@ Switch - A switch statement for Perl =head1 VERSION -This document describes version 2.04 of Switch, -released July 30, 2001. +This document describes version 2.05 of Switch, +released September 3, 2001. =head1 SYNOPSIS @@ -827,6 +827,6 @@ Bug reports and other feedback are most welcome. =head1 COPYRIGHT -Copyright (c) 1997-2000, Damian Conway. All Rights Reserved. -This module is free software; you can redistribute it and/or -modify it under the same terms as Perl itself. + Copyright (c) 1997-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. diff --git a/lib/Switch/Changes b/lib/Switch/Changes index 2f74241..d741a69 100755 --- a/lib/Switch/Changes +++ b/lib/Switch/Changes @@ -43,8 +43,8 @@ Revision history for Perl extension Switch. - Added support for Perl 6 given..when syntax -2.04 Mon Jul 30 13:17:35 2001 +2.05 Mon Sep 3 08:13:25 2001 - - Suppressed 'undef value' warning under -w (thanks Michael) + - Changed licence for inclusion in core distribution - - Added support for Perl 6 given..when syntax + - Added new test file for non-fallthrough and nested switches diff --git a/lib/Switch/README b/lib/Switch/README index d5a7d28..312571b 100644 --- a/lib/Switch/README +++ b/lib/Switch/README @@ -1,5 +1,5 @@ ============================================================================== - Release of version 2.04 of Switch + Release of version 2.05 of Switch ============================================================================== @@ -19,20 +19,19 @@ AUTHOR Damian Conway (damian@conway.org) COPYRIGHT - Copyright (c) 1997-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) + Copyright (c) 1997-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. ============================================================================== -CHANGES IN VERSION 2.04 +CHANGES IN VERSION 2.05 - - Suppressed 'undef value' warning under -w (thanks Michael) + - Changed licence for inclusion in core distribution - - Added support for Perl 6 given..when syntax + - Added new test file for non-fallthrough and nested switches ============================================================================== diff --git a/lib/Switch/t/given_when.t b/lib/Switch/t/given.t old mode 100644 new mode 100755 similarity index 100% rename from lib/Switch/t/given_when.t rename to lib/Switch/t/given.t diff --git a/lib/Switch/t/nested.t b/lib/Switch/t/nested.t new file mode 100755 index 0000000..032d3a6 --- /dev/null +++ b/lib/Switch/t/nested.t @@ -0,0 +1,18 @@ +use Switch; + +print "1..4\n"; + +my $count = 1; +for my $count (1..3, 'four') +{ + switch ([$count]) + { + case qr/\d/ { + switch ($count) { + case 1 { print "ok 1\n" } + case [2,3] { print "ok $count\n" } + } + } + case 'four' { print "ok 4\n" } + } +} diff --git a/lib/Switch/t/switch_case.t b/lib/Switch/t/switch.t old mode 100644 new mode 100755 similarity index 100% rename from lib/Switch/t/switch_case.t rename to lib/Switch/t/switch.t diff --git a/lib/Text/Balanced.pm b/lib/Text/Balanced.pm index 73d6332..f50e2f5 100644 --- a/lib/Text/Balanced.pm +++ b/lib/Text/Balanced.pm @@ -10,7 +10,7 @@ use Exporter; use SelfLoader; use vars qw { $VERSION @ISA %EXPORT_TAGS }; -$VERSION = '1.85'; +$VERSION = '1.86'; @ISA = qw ( Exporter ); %EXPORT_TAGS = ( ALL => [ qw( @@ -566,9 +566,9 @@ sub _match_codeblock($$$$$$$) # NEED TO COVER MANY MORE CASES HERE!!! if ($$textref =~ m#\G\s*( [-+*x/%^&|.]=? + | [!=]~ | =(?!>) | (\*\*|&&|\|\||<<|>>)=? - | [!=][~=] | split|grep|map|return )#gcx) { @@ -995,3 +995,1211 @@ package Text::Balanced::ErrorMsg; use overload '""' => sub { "$_[0]->{error}, detected at offset $_[0]->{pos}" }; 1; + +__END__ + +=head1 NAME + +Text::Balanced - Extract delimited text sequences from strings. + + +=head1 SYNOPSIS + + use Text::Balanced qw ( + extract_delimited + extract_bracketed + extract_quotelike + extract_codeblock + extract_variable + extract_tagged + extract_multiple + + gen_delimited_pat + gen_extract_tagged + ); + + # Extract the initial substring of $text that is delimited by + # two (unescaped) instances of the first character in $delim. + + ($extracted, $remainder) = extract_delimited($text,$delim); + + + # Extract the initial substring of $text that is bracketed + # with a delimiter(s) specified by $delim (where the string + # in $delim contains one or more of '(){}[]<>'). + + ($extracted, $remainder) = extract_bracketed($text,$delim); + + + # Extract the initial substring of $text that is bounded by + # an HTML/XML tag. + + ($extracted, $remainder) = extract_tagged($text); + + + # Extract the initial substring of $text that is bounded by + # a C...C pair. Don't allow nested C tags + + ($extracted, $remainder) = + extract_tagged($text,"BEGIN","END",undef,{bad=>["BEGIN"]}); + + + # Extract the initial substring of $text that represents a + # Perl "quote or quote-like operation" + + ($extracted, $remainder) = extract_quotelike($text); + + + # Extract the initial substring of $text that represents a block + # of Perl code, bracketed by any of character(s) specified by $delim + # (where the string $delim contains one or more of '(){}[]<>'). + + ($extracted, $remainder) = extract_codeblock($text,$delim); + + + # Extract the initial substrings of $text that would be extracted by + # one or more sequential applications of the specified functions + # or regular expressions + + @extracted = extract_multiple($text, + [ \&extract_bracketed, + \&extract_quotelike, + \&some_other_extractor_sub, + qr/[xyz]*/, + 'literal', + ]); + +# Create a string representing an optimized pattern (a la Friedl) +# that matches a substring delimited by any of the specified characters +# (in this case: any type of quote or a slash) + + $patstring = gen_delimited_pat(q{'"`/}); + + +# Generate a reference to an anonymous sub that is just like extract_tagged +# but pre-compiled and optimized for a specific pair of tags, and consequently +# much faster (i.e. 3 times faster). It uses qr// for better performance on +# repeated calls, so it only works under Perl 5.005 or later. + + $extract_head = gen_extract_tagged('',''); + + ($extracted, $remainder) = $extract_head->($text); + + +=head1 DESCRIPTION + +The various C subroutines may be used to extract a +delimited string (possibly after skipping a specified prefix string). +The search for the string always begins at the current C +location of the string's variable (or at index zero, if no C +position is defined). + +=head2 General behaviour in list contexts + +In a list context, all the subroutines return a list, the first three +elements of which are always: + +=over 4 + +=item [0] + +The extracted string, including the specified delimiters. +If the extraction fails an empty string is returned. + +=item [1] + +The remainder of the input string (i.e. the characters after the +extracted string). On failure, the entire string is returned. + +=item [2] + +The skipped prefix (i.e. the characters before the extracted string). +On failure, the empty string is returned. + +=back + +Note that in a list context, the contents of the original input text (the first +argument) are not modified in any way. + +However, if the input text was passed in a variable, that variable's +C value is updated to point at the first character after the +extracted text. That means that in a list context the various +subroutines can be used much like regular expressions. For example: + + while ( $next = (extract_quotelike($text))[0] ) + { + # process next quote-like (in $next) + } + + +=head2 General behaviour in scalar and void contexts + +In a scalar context, the extracted string is returned, having first been +removed from the input text. Thus, the following code also processes +each quote-like operation, but actually removes them from $text: + + while ( $next = extract_quotelike($text) ) + { + # process next quote-like (in $next) + } + +Note that if the input text is a read-only string (i.e. a literal), +no attempt is made to remove the extracted text. + +In a void context the behaviour of the extraction subroutines is +exactly the same as in a scalar context, except (of course) that the +extracted substring is not returned. + +=head2 A note about prefixes + +Prefix patterns are matched without any trailing modifiers (C etc.) +This can bite you if you're expecting a prefix specification like +'.*?(?=

)' to skip everything up to the first

tag. Such a prefix +pattern will only succeed if the

tag is on the current line, since +. normally doesn't match newlines. + +To overcome this limitation, you need to turn on /s matching within +the prefix pattern, using the C<(?s)> directive: '(?s).*?(?=

)' + + +=head2 C + +The C function formalizes the common idiom +of extracting a single-character-delimited substring from the start of +a string. For example, to extract a single-quote delimited string, the +following code is typically used: + + ($remainder = $text) =~ s/\A('(\\.|[^'])*')//s; + $extracted = $1; + +but with C it can be simplified to: + + ($extracted,$remainder) = extract_delimited($text, "'"); + +C takes up to four scalars (the input text, the +delimiters, a prefix pattern to be skipped, and any escape characters) +and extracts the initial substring of the text that +is appropriately delimited. If the delimiter string has multiple +characters, the first one encountered in the text is taken to delimit +the substring. +The third argument specifies a prefix pattern that is to be skipped +(but must be present!) before the substring is extracted. +The final argument specifies the escape character to be used for each +delimiter. + +All arguments are optional. If the escape characters are not specified, +every delimiter is escaped with a backslash (C<\>). +If the prefix is not specified, the +pattern C<'\s*'> - optional whitespace - is used. If the delimiter set +is also not specified, the set C is used. If the text to be processed +is not specified either, C<$_> is used. + +In list context, C returns a array of three +elements, the extracted substring (I), the remainder of the text, and the skipped prefix (if +any). If a suitable delimited substring is not found, the first +element of the array is the empty string, the second is the complete +original text, and the prefix returned in the third element is an +empty string. + +In a scalar context, just the extracted substring is returned. In +a void context, the extracted substring (and any prefix) are simply +removed from the beginning of the first argument. + +Examples: + + # Remove a single-quoted substring from the very beginning of $text: + + $substring = extract_delimited($text, "'", ''); + + # Remove a single-quoted Pascalish substring (i.e. one in which + # doubling the quote character escapes it) from the very + # beginning of $text: + + $substring = extract_delimited($text, "'", '', "'"); + + # Extract a single- or double- quoted substring from the + # beginning of $text, optionally after some whitespace + # (note the list context to protect $text from modification): + + ($substring) = extract_delimited $text, q{"'}; + + + # Delete the substring delimited by the first '/' in $text: + + $text = join '', (extract_delimited($text,'/','[^/]*')[2,1]; + +Note that this last example is I the same as deleting the first +quote-like pattern. For instance, if C<$text> contained the string: + + "if ('./cmd' =~ m/$UNIXCMD/s) { $cmd = $1; }" + +then after the deletion it would contain: + + "if ('.$UNIXCMD/s) { $cmd = $1; }" + +not: + + "if ('./cmd' =~ ms) { $cmd = $1; }" + + +See L<"extract_quotelike"> for a (partial) solution to this problem. + + +=head2 C + +Like C<"extract_delimited">, the C function takes +up to three optional scalar arguments: a string to extract from, a delimiter +specifier, and a prefix pattern. As before, a missing prefix defaults to +optional whitespace and a missing text defaults to C<$_>. However, a missing +delimiter specifier defaults to C<'{}()[]EE'> (see below). + +C extracts a balanced-bracket-delimited +substring (using any one (or more) of the user-specified delimiter +brackets: '(..)', '{..}', '[..]', or '<..>'). Optionally it will also +respect quoted unbalanced brackets (see below). + +A "delimiter bracket" is a bracket in list of delimiters passed as +C's second argument. Delimiter brackets are +specified by giving either the left or right (or both!) versions +of the required bracket(s). Note that the order in which +two or more delimiter brackets are specified is not significant. + +A "balanced-bracket-delimited substring" is a substring bounded by +matched brackets, such that any other (left or right) delimiter +bracket I the substring is also matched by an opposite +(right or left) delimiter bracket I. Any +type of bracket not in the delimiter list is treated as an ordinary +character. + +In other words, each type of bracket specified as a delimiter must be +balanced and correctly nested within the substring, and any other kind of +("non-delimiter") bracket in the substring is ignored. + +For example, given the string: + + $text = "{ an '[irregularly :-(] {} parenthesized >:-)' string }"; + +then a call to C in a list context: + + @result = extract_bracketed( $text, '{}' ); + +would return: + + ( "{ an '[irregularly :-(] {} parenthesized >:-)' string }" , "" , "" ) + +since both sets of C<'{..}'> brackets are properly nested and evenly balanced. +(In a scalar context just the first element of the array would be returned. In +a void context, C<$text> would be replaced by an empty string.) + +Likewise the call in: + + @result = extract_bracketed( $text, '{[' ); + +would return the same result, since all sets of both types of specified +delimiter brackets are correctly nested and balanced. + +However, the call in: + + @result = extract_bracketed( $text, '{([<' ); + +would fail, returning: + + ( undef , "{ an '[irregularly :-(] {} parenthesized >:-)' string }" ); + +because the embedded pairs of C<'(..)'>s and C<'[..]'>s are "cross-nested" and +the embedded C<'E'> is unbalanced. (In a scalar context, this call would +return an empty string. In a void context, C<$text> would be unchanged.) + +Note that the embedded single-quotes in the string don't help in this +case, since they have not been specified as acceptable delimiters and are +therefore treated as non-delimiter characters (and ignored). + +However, if a particular species of quote character is included in the +delimiter specification, then that type of quote will be correctly handled. +for example, if C<$text> is: + + $text = 'link'; + +then + + @result = extract_bracketed( $text, '<">' ); + +returns: + + ( '', 'link', "" ) + +as expected. Without the specification of C<"> as an embedded quoter: + + @result = extract_bracketed( $text, '<>' ); + +the result would be: + + ( 'link', "" ) + +In addition to the quote delimiters C<'>, C<">, and C<`>, full Perl quote-like +quoting (i.e. q{string}, qq{string}, etc) can be specified by including the +letter 'q' as a delimiter. Hence: + + @result = extract_bracketed( $text, '' ); + +would correctly match something like this: + + $text = ''; + +See also: C<"extract_quotelike"> and C<"extract_codeblock">. + + +=head2 C + +C extracts and segments text between (balanced) +specified tags. + +The subroutine takes up to five optional arguments: + +=over 4 + +=item 1. + +A string to be processed (C<$_> if the string is omitted or C) + +=item 2. + +A string specifying a pattern to be matched as the opening tag. +If the pattern string is omitted (or C) then a pattern +that matches any standard HTML/XML tag is used. + +=item 3. + +A string specifying a pattern to be matched at the closing tag. +If the pattern string is omitted (or C) then the closing +tag is constructed by inserting a C after any leading bracket +characters in the actual opening tag that was matched (I the pattern +that matched the tag). For example, if the opening tag pattern +is specified as C<'{{\w+}}'> and actually matched the opening tag +C<"{{DATA}}">, then the constructed closing tag would be C<"{{/DATA}}">. + +=item 4. + +A string specifying a pattern to be matched as a prefix (which is to be +skipped). If omitted, optional whitespace is skipped. + +=item 5. + +A hash reference containing various parsing options (see below) + +=back + +The various options that can be specified are: + +=over 4 + +=item C $listref> + +The list reference contains one or more strings specifying patterns +that must I appear within the tagged text. + +For example, to extract +an HTML link (which should not contain nested links) use: + + extract_tagged($text, '', '', undef, {reject => ['']} ); + +=item C $listref> + +The list reference contains one or more strings specifying patterns +that are I be be treated as nested tags within the tagged text +(even if they would match the start tag pattern). + +For example, to extract an arbitrary XML tag, but ignore "empty" elements: + + extract_tagged($text, undef, undef, undef, {ignore => ['<[^>]*/>']} ); + +(also see L<"gen_delimited_pat"> below). + + +=item C $str> + +The C option indicates the action to be taken if a matching end +tag is not encountered (i.e. before the end of the string or some +C pattern matches). By default, a failure to match a closing +tag causes C to immediately fail. + +However, if the string value associated with is "MAX", then +C returns the complete text up to the point of failure. +If the string is "PARA", C returns only the first paragraph +after the tag (up to the first line that is either empty or contains +only whitespace characters). +If the string is "", the the default behaviour (i.e. failure) is reinstated. + +For example, suppose the start tag "/para" introduces a paragraph, which then +continues until the next "/endpara" tag or until another "/para" tag is +encountered: + + $text = "/para line 1\n\nline 3\n/para line 4"; + + extract_tagged($text, '/para', '/endpara', undef, + {reject => '/para', fail => MAX ); + + # EXTRACTED: "/para line 1\n\nline 3\n" + +Suppose instead, that if no matching "/endpara" tag is found, the "/para" +tag refers only to the immediately following paragraph: + + $text = "/para line 1\n\nline 3\n/para line 4"; + + extract_tagged($text, '/para', '/endpara', undef, + {reject => '/para', fail => MAX ); + + # EXTRACTED: "/para line 1\n" + +Note that the specified C behaviour applies to nested tags as well. + +=back + +On success in a list context, an array of 6 elements is returned. The elements are: + +=over 4 + +=item [0] + +the extracted tagged substring (including the outermost tags), + +=item [1] + +the remainder of the input text, + +=item [2] + +the prefix substring (if any), + +=item [3] + +the opening tag + +=item [4] + +the text between the opening and closing tags + +=item [5] + +the closing tag (or "" if no closing tag was found) + +=back + +On failure, all of these values (except the remaining text) are C. + +In a scalar context, C returns just the complete +substring that matched a tagged text (including the start and end +tags). C is returned on failure. In addition, the original input +text has the returned substring (and any prefix) removed from it. + +In a void context, the input text just has the matched substring (and +any specified prefix) removed. + + +=head2 C + +(Note: This subroutine is only available under Perl5.005) + +C generates a new anonymous subroutine which +extracts text between (balanced) specified tags. In other words, +it generates a function identical in function to C. + +The difference between C and the anonymous +subroutines generated by +C, is that those generated subroutines: + +=over 4 + +=item * + +do not have to reparse tag specification or parsing options every time +they are called (whereas C has to effectively rebuild +its tag parser on every call); + +=item * + +make use of the new qr// construct to pre-compile the regexes they use +(whereas C uses standard string variable interpolation +to create tag-matching patterns). + +=back + +The subroutine takes up to four optional arguments (the same set as +C except for the string to be processed). It returns +a reference to a subroutine which in turn takes a single argument (the text to +be extracted from). + +In other words, the implementation of C is exactly +equivalent to: + + sub extract_tagged + { + my $text = shift; + $extractor = gen_extract_tagged(@_); + return $extractor->($text); + } + +(although C is not currently implemented that way, in order +to preserve pre-5.005 compatibility). + +Using C to create extraction functions for specific tags +is a good idea if those functions are going to be called more than once, since +their performance is typically twice as good as the more general-purpose +C. + + +=head2 C + +C attempts to recognize, extract, and segment any +one of the various Perl quotes and quotelike operators (see +L) Nested backslashed delimiters, embedded balanced bracket +delimiters (for the quotelike operators), and trailing modifiers are +all caught. For example, in: + + extract_quotelike 'q # an octothorpe: \# (not the end of the q!) #' + + extract_quotelike ' "You said, \"Use sed\"." ' + + extract_quotelike ' s{([A-Z]{1,8}\.[A-Z]{3})} /\L$1\E/; ' + + extract_quotelike ' tr/\\\/\\\\/\\\//ds; ' + +the full Perl quotelike operations are all extracted correctly. + +Note too that, when using the /x modifier on a regex, any comment +containing the current pattern delimiter will cause the regex to be +immediately terminated. In other words: + + 'm / + (?i) # CASE INSENSITIVE + [a-z_] # LEADING ALPHABETIC/UNDERSCORE + [a-z0-9]* # FOLLOWED BY ANY NUMBER OF ALPHANUMERICS + /x' + +will be extracted as if it were: + + 'm / + (?i) # CASE INSENSITIVE + [a-z_] # LEADING ALPHABETIC/' + +This behaviour is identical to that of the actual compiler. + +C takes two arguments: the text to be processed and +a prefix to be matched at the very beginning of the text. If no prefix +is specified, optional whitespace is the default. If no text is given, +C<$_> is used. + +In a list context, an array of 11 elements is returned. The elements are: + +=over 4 + +=item [0] + +the extracted quotelike substring (including trailing modifiers), + +=item [1] + +the remainder of the input text, + +=item [2] + +the prefix substring (if any), + +=item [3] + +the name of the quotelike operator (if any), + +=item [4] + +the left delimiter of the first block of the operation, + +=item [5] + +the text of the first block of the operation +(that is, the contents of +a quote, the regex of a match or substitution or the target list of a +translation), + +=item [6] + +the right delimiter of the first block of the operation, + +=item [7] + +the left delimiter of the second block of the operation +(that is, if it is a C, C, or C), + +=item [8] + +the text of the second block of the operation +(that is, the replacement of a substitution or the translation list +of a translation), + +=item [9] + +the right delimiter of the second block of the operation (if any), + +=item [10] + +the trailing modifiers on the operation (if any). + +=back + +For each of the fields marked "(if any)" the default value on success is +an empty string. +On failure, all of these values (except the remaining text) are C. + + +In a scalar context, C returns just the complete substring +that matched a quotelike operation (or C on failure). In a scalar or +void context, the input text has the same substring (and any specified +prefix) removed. + +Examples: + + # Remove the first quotelike literal that appears in text + + $quotelike = extract_quotelike($text,'.*?'); + + # Replace one or more leading whitespace-separated quotelike + # literals in $_ with "" + + do { $_ = join '', (extract_quotelike)[2,1] } until $@; + + + # Isolate the search pattern in a quotelike operation from $text + + ($op,$pat) = (extract_quotelike $text)[3,5]; + if ($op =~ /[ms]/) + { + print "search pattern: $pat\n"; + } + else + { + print "$op is not a pattern matching operation\n"; + } + + +=head2 C and "here documents" + +C can successfully extract "here documents" from an input +string, but with an important caveat in list contexts. + +Unlike other types of quote-like literals, a here document is rarely +a contiguous substring. For example, a typical piece of code using +here document might look like this: + + <<'EOMSG' || die; + This is the message. + EOMSG + exit; + +Given this as an input string in a scalar context, C +would correctly return the string "<<'EOMSG'\nThis is the message.\nEOMSG", +leaving the string " || die;\nexit;" in the original variable. In other words, +the two separate pieces of the here document are successfully extracted and +concatenated. + +In a list context, C would return the list + +=over 4 + +=item [0] + +"<<'EOMSG'\nThis is the message.\nEOMSG\n" (i.e. the full extracted here document, +including fore and aft delimiters), + +=item [1] + +" || die;\nexit;" (i.e. the remainder of the input text, concatenated), + +=item [2] + +"" (i.e. the prefix substring -- trivial in this case), + +=item [3] + +"<<" (i.e. the "name" of the quotelike operator) + +=item [4] + +"'EOMSG'" (i.e. the left delimiter of the here document, including any quotes), + +=item [5] + +"This is the message.\n" (i.e. the text of the here document), + +=item [6] + +"EOMSG" (i.e. the right delimiter of the here document), + +=item [7..10] + +"" (a here document has no second left delimiter, second text, second right +delimiter, or trailing modifiers). + +=back + +However, the matching position of the input variable would be set to +"exit;" (i.e. I the closing delimiter of the here document), +which would cause the earlier " || die;\nexit;" to be skipped in any +sequence of code fragment extractions. + +To avoid this problem, when it encounters a here document whilst +extracting from a modifiable string, C silently +rearranges the string to an equivalent piece of Perl: + + <<'EOMSG' + This is the message. + EOMSG + || die; + exit; + +in which the here document I contiguous. It still leaves the +matching position after the here document, but now the rest of the line +on which the here document starts is not skipped. + +To prevent from mucking about with the input in this way +(this is the only case where a list-context C does so), +you can pass the input variable as an interpolated literal: + + $quotelike = extract_quotelike("$var"); + + +=head2 C + +C attempts to recognize and extract a balanced +bracket delimited substring that may contain unbalanced brackets +inside Perl quotes or quotelike operations. That is, C +is like a combination of C<"extract_bracketed"> and +C<"extract_quotelike">. + +C takes the same initial three parameters as C: +a text to process, a set of delimiter brackets to look for, and a prefix to +match first. It also takes an optional fourth parameter, which allows the +outermost delimiter brackets to be specified separately (see below). + +Omitting the first argument (input text) means process C<$_> instead. +Omitting the second argument (delimiter brackets) indicates that only C<'{'> is to be used. +Omitting the third argument (prefix argument) implies optional whitespace at the start. +Omitting the fourth argument (outermost delimiter brackets) indicates that the +value of the second argument is to be used for the outermost delimiters. + +Once the prefix an dthe outermost opening delimiter bracket have been +recognized, code blocks are extracted by stepping through the input text and +trying the following alternatives in sequence: + +=over 4 + +=item 1. + +Try and match a closing delimiter bracket. If the bracket was the same +species as the last opening bracket, return the substring to that +point. If the bracket was mismatched, return an error. + +=item 2. + +Try to match a quote or quotelike operator. If found, call +C to eat it. If C fails, return +the error it returned. Otherwise go back to step 1. + +=item 3. + +Try to match an opening delimiter bracket. If found, call +C recursively to eat the embedded block. If the +recursive call fails, return an error. Otherwise, go back to step 1. + +=item 4. + +Unconditionally match a bareword or any other single character, and +then go back to step 1. + +=back + + +Examples: + + # Find a while loop in the text + + if ($text =~ s/.*?while\s*\{/{/) + { + $loop = "while " . extract_codeblock($text); + } + + # Remove the first round-bracketed list (which may include + # round- or curly-bracketed code blocks or quotelike operators) + + extract_codeblock $text, "(){}", '[^(]*'; + + +The ability to specify a different outermost delimiter bracket is useful +in some circumstances. For example, in the Parse::RecDescent module, +parser actions which are to be performed only on a successful parse +are specified using a Cdefer:...E> directive. For example: + + sentence: subject verb object + + +Parse::RecDescent uses CE')> to extract the code +within the Cdefer:...E> directive, but there's a problem. + +A deferred action like this: + + 10) {$count--}} > + +will be incorrectly parsed as: + + + +because the "less than" operator is interpreted as a closing delimiter. + +But, by extracting the directive using +SE')>> +the '>' character is only treated as a delimited at the outermost +level of the code block, so the directive is parsed correctly. + +=head2 C + +The C subroutine takes a string to be processed and a +list of extractors (subroutines or regular expressions) to apply to that string. + +In an array context C returns an array of substrings +of the original string, as extracted by the specified extractors. +In a scalar context, C returns the first +substring successfully extracted from the original string. In both +scalar and void contexts the original string has the first successfully +extracted substring removed from it. In all contexts +C starts at the current C of the string, and +sets that C appropriately after it matches. + +Hence, the aim of of a call to C in a list context +is to split the processed string into as many non-overlapping fields as +possible, by repeatedly applying each of the specified extractors +to the remainder of the string. Thus C is +a generalized form of Perl's C subroutine. + +The subroutine takes up to four optional arguments: + +=over 4 + +=item 1. + +A string to be processed (C<$_> if the string is omitted or C) + +=item 2. + +A reference to a list of subroutine references and/or qr// objects and/or +literal strings and/or hash references, specifying the extractors +to be used to split the string. If this argument is omitted (or +C) the list: + + [ + sub { extract_variable($_[0], '') }, + sub { extract_quotelike($_[0],'') }, + sub { extract_codeblock($_[0],'{}','') }, + ] + +is used. + + +=item 3. + +An number specifying the maximum number of fields to return. If this +argument is omitted (or C), split continues as long as possible. + +If the third argument is I, then extraction continues until I fields +have been successfully extracted, or until the string has been completely +processed. + +Note that in scalar and void contexts the value of this argument is +automatically reset to 1 (under C<-w>, a warning is issued if the argument +has to be reset). + +=item 4. + +A value indicating whether unmatched substrings (see below) within the +text should be skipped or returned as fields. If the value is true, +such substrings are skipped. Otherwise, they are returned. + +=back + +The extraction process works by applying each extractor in +sequence to the text string. If the extractor is a subroutine it +is called in a list +context and is expected to return a list of a single element, namely +the extracted text. +Note that the value returned by an extractor subroutine need not bear any +relationship to the corresponding substring of the original text (see +examples below). + +If the extractor is a precompiled regular expression or a string, +it is matched against the text in a scalar context with a leading +'\G' and the gc modifiers enabled. The extracted value is either +$1 if that variable is defined after the match, or else the +complete match (i.e. $&). + +If the extractor is a hash reference, it must contain exactly one element. +The value of that element is one of the +above extractor types (subroutine reference, regular expression, or string). +The key of that element is the name of a class into which the successful +return value of the extractor will be blessed. + +If an extractor returns a defined value, that value is immediately +treated as the next extracted field and pushed onto the list of fields. +If the extractor was specified in a hash reference, the field is also +blessed into the appropriate class, + +If the extractor fails to match (in the case of a regex extractor), or returns an empty list or an undefined value (in the case of a subroutine extractor), it is +assumed to have failed to extract. +If none of the extractor subroutines succeeds, then one +character is extracted from the start of the text and the extraction +subroutines reapplied. Characters which are thus removed are accumulated and +eventually become the next field (unless the fourth argument is true, in which +case they are disgarded). + +For example, the following extracts substrings that are valid Perl variables: + + @fields = extract_multiple($text, + [ sub { extract_variable($_[0]) } ], + undef, 1); + +This example separates a text into fields which are quote delimited, +curly bracketed, and anything else. The delimited and bracketed +parts are also blessed to identify them (the "anything else" is unblessed): + + @fields = extract_multiple($text, + [ + { Delim => sub { extract_delimited($_[0],q{'"}) } }, + { Brack => sub { extract_bracketed($_[0],'{}') } }, + ]); + +This call extracts the next single substring that is a valid Perl quotelike +operator (and removes it from $text): + + $quotelike = extract_multiple($text, + [ + sub { extract_quotelike($_[0]) }, + ], undef, 1); + +Finally, here is yet another way to do comma-separated value parsing: + + @fields = extract_multiple($csv_text, + [ + sub { extract_delimited($_[0],q{'"}) }, + qr/([^,]+)(.*)/, + ], + undef,1); + +The list in the second argument means: +I<"Try and extract a ' or " delimited string, otherwise extract anything up to a comma...">. +The undef third argument means: +I<"...as many times as possible...">, +and the true value in the fourth argument means +I<"...discarding anything else that appears (i.e. the commas)">. + +If you wanted the commas preserved as separate fields (i.e. like split +does if your split pattern has capturing parentheses), you would +just make the last parameter undefined (or remove it). + + +=head2 C + +The C subroutine takes a single (string) argument and + > builds a Friedl-style optimized regex that matches a string delimited +by any one of the characters in the single argument. For example: + + gen_delimited_pat(q{'"}) + +returns the regex: + + (?:\"(?:\\\"|(?!\").)*\"|\'(?:\\\'|(?!\').)*\') + +Note that the specified delimiters are automatically quotemeta'd. + +A typical use of C would be to build special purpose tags +for C. For example, to properly ignore "empty" XML elements +(which might contain quoted strings): + + my $empty_tag = '<(' . gen_delimited_pat(q{'"}) . '|.)+/>'; + + extract_tagged($text, undef, undef, undef, {ignore => [$empty_tag]} ); + + +C may also be called with an optional second argument, +which specifies the "escape" character(s) to be used for each delimiter. +For example to match a Pascal-style string (where ' is the delimiter +and '' is a literal ' within the string): + + gen_delimited_pat(q{'},q{'}); + +Different escape characters can be specified for different delimiters. +For example, to specify that '/' is the escape for single quotes +and '%' is the escape for double quotes: + + gen_delimited_pat(q{'"},q{/%}); + +If more delimiters than escape chars are specified, the last escape char +is used for the remaining delimiters. +If no escape char is specified for a given specified delimiter, '\' is used. + +Note that +C was previously called +C. That name may still be used, but is now deprecated. + + +=head1 DIAGNOSTICS + +In a list context, all the functions return C<(undef,$original_text)> +on failure. In a scalar context, failure is indicated by returning C +(in this case the input text is not modified in any way). + +In addition, on failure in I context, the C<$@> variable is set. +Accessing C<$@-E{error}> returns one of the error diagnostics listed +below. +Accessing C<$@-E{pos}> returns the offset into the original string at +which the error was detected (although not necessarily where it occurred!) +Printing C<$@> directly produces the error message, with the offset appended. +On success, the C<$@> variable is guaranteed to be C. + +The available diagnostics are: + +=over 4 + +=item C + +The delimiter provided to C was not one of +C<'()[]EE{}'>. + +=item C + +A non-optional prefix was specified but wasn't found at the start of the text. + +=item C + +C or C was expecting a +particular kind of bracket at the start of the text, and didn't find it. + +=item C + +C didn't find one of the quotelike operators C, +C, C, C, C, C or C at the start of the substring +it was extracting. + +=item C + +C, C or C encountered +a closing bracket where none was expected. + +=item C + +C, C or C ran +out of characters in the text before closing one or more levels of nested +brackets. + +=item C + +C attempted to match an embedded quoted substring, but +failed to find a closing quote to match it. + +=item C + +C was unable to find a closing delimiter to match the +one that opened the quote-like operation. + +=item C + +C, C or C found +a valid bracket delimiter, but it was the wrong species. This usually +indicates a nesting error, but may indicate incorrect quoting or escaping. + +=item C + +C or C found one of the +quotelike operators C, C, C, C, C, C or C +without a suitable block after it. + +=item C + +C was expecting one of '$', '@', or '%' at the start of +a variable, but didn't find any of them. + +=item C + +C found a '$', '@', or '%' indicating a variable, but that +character was not followed by a legal Perl identifier. + +=item C + +C failed to find any of the outermost opening brackets +that were specified. + +=item C + +A nested code block was found that started with a delimiter that was specified +as being only to be used as an outermost bracket. + +=item C + +C or C found one of the +quotelike operators C, C or C followed by only one block. + +=item C + +C failed to find a closing bracket to match the outermost +opening bracket. + +=item C + +C did not find a suitable opening tag (after any specified +prefix was removed). + +=item C + +C matched the specified opening tag and tried to +modify the matched text to produce a matching closing tag (because +none was specified). It failed to generate the closing tag, almost +certainly because the opening tag did not start with a +bracket of some kind. + +=item C + +C found a nested tag that appeared in the "reject" list +(and the failure mode was not "MAX" or "PARA"). + +=item C + +C found a nested opening tag that was not matched by a +corresponding nested closing tag (and the failure mode was not "MAX" or "PARA"). + +=item C + +C reached the end of the text without finding a closing tag +to match the original opening tag (and the failure mode was not +"MAX" or "PARA"). + + + + +=back + + +=head1 AUTHOR + +Damian Conway (damian@conway.org) + + +=head1 BUGS AND IRRITATIONS + +There are undoubtedly serious bugs lurking somewhere in this code, if +only because parts of it give the impression of understanding a great deal +more about Perl than they really do. + +Bug reports and other feedback are most welcome. + + +=head1 COPYRIGHT + + Copyright (c) 1997-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. diff --git a/lib/Text/Balanced.pod b/lib/Text/Balanced.pod deleted file mode 100644 index 75d67aa..0000000 --- a/lib/Text/Balanced.pod +++ /dev/null @@ -1,1205 +0,0 @@ -=head1 NAME - -Text::Balanced - Extract delimited text sequences from strings. - - -=head1 SYNOPSIS - - use Text::Balanced qw ( - extract_delimited - extract_bracketed - extract_quotelike - extract_codeblock - extract_variable - extract_tagged - extract_multiple - - gen_delimited_pat - gen_extract_tagged - ); - - # Extract the initial substring of $text that is delimited by - # two (unescaped) instances of the first character in $delim. - - ($extracted, $remainder) = extract_delimited($text,$delim); - - - # Extract the initial substring of $text that is bracketed - # with a delimiter(s) specified by $delim (where the string - # in $delim contains one or more of '(){}[]<>'). - - ($extracted, $remainder) = extract_bracketed($text,$delim); - - - # Extract the initial substring of $text that is bounded by - # an HTML/XML tag. - - ($extracted, $remainder) = extract_tagged($text); - - - # Extract the initial substring of $text that is bounded by - # a C...C pair. Don't allow nested C tags - - ($extracted, $remainder) = - extract_tagged($text,"BEGIN","END",undef,{bad=>["BEGIN"]}); - - - # Extract the initial substring of $text that represents a - # Perl "quote or quote-like operation" - - ($extracted, $remainder) = extract_quotelike($text); - - - # Extract the initial substring of $text that represents a block - # of Perl code, bracketed by any of character(s) specified by $delim - # (where the string $delim contains one or more of '(){}[]<>'). - - ($extracted, $remainder) = extract_codeblock($text,$delim); - - - # Extract the initial substrings of $text that would be extracted by - # one or more sequential applications of the specified functions - # or regular expressions - - @extracted = extract_multiple($text, - [ \&extract_bracketed, - \&extract_quotelike, - \&some_other_extractor_sub, - qr/[xyz]*/, - 'literal', - ]); - -# Create a string representing an optimized pattern (a la Friedl) -# that matches a substring delimited by any of the specified characters -# (in this case: any type of quote or a slash) - - $patstring = gen_delimited_pat(q{'"`/}); - - -# Generate a reference to an anonymous sub that is just like extract_tagged -# but pre-compiled and optimized for a specific pair of tags, and consequently -# much faster (i.e. 3 times faster). It uses qr// for better performance on -# repeated calls, so it only works under Perl 5.005 or later. - - $extract_head = gen_extract_tagged('',''); - - ($extracted, $remainder) = $extract_head->($text); - - -=head1 DESCRIPTION - -The various C subroutines may be used to extract a -delimited string (possibly after skipping a specified prefix string). -The search for the string always begins at the current C -location of the string's variable (or at index zero, if no C -position is defined). - -=head2 General behaviour in list contexts - -In a list context, all the subroutines return a list, the first three -elements of which are always: - -=over 4 - -=item [0] - -The extracted string, including the specified delimiters. -If the extraction fails an empty string is returned. - -=item [1] - -The remainder of the input string (i.e. the characters after the -extracted string). On failure, the entire string is returned. - -=item [2] - -The skipped prefix (i.e. the characters before the extracted string). -On failure, the empty string is returned. - -=back - -Note that in a list context, the contents of the original input text (the first -argument) are not modified in any way. - -However, if the input text was passed in a variable, that variable's -C value is updated to point at the first character after the -extracted text. That means that in a list context the various -subroutines can be used much like regular expressions. For example: - - while ( $next = (extract_quotelike($text))[0] ) - { - # process next quote-like (in $next) - } - - -=head2 General behaviour in scalar and void contexts - -In a scalar context, the extracted string is returned, having first been -removed from the input text. Thus, the following code also processes -each quote-like operation, but actually removes them from $text: - - while ( $next = extract_quotelike($text) ) - { - # process next quote-like (in $next) - } - -Note that if the input text is a read-only string (i.e. a literal), -no attempt is made to remove the extracted text. - -In a void context the behaviour of the extraction subroutines is -exactly the same as in a scalar context, except (of course) that the -extracted substring is not returned. - -=head2 A note about prefixes - -Prefix patterns are matched without any trailing modifiers (C etc.) -This can bite you if you're expecting a prefix specification like -'.*?(?=

)' to skip everything up to the first

tag. Such a prefix -pattern will only succeed if the

tag is on the current line, since -. normally doesn't match newlines. - -To overcome this limitation, you need to turn on /s matching within -the prefix pattern, using the C<(?s)> directive: '(?s).*?(?=

)' - - -=head2 C - -The C function formalizes the common idiom -of extracting a single-character-delimited substring from the start of -a string. For example, to extract a single-quote delimited string, the -following code is typically used: - - ($remainder = $text) =~ s/\A('(\\.|[^'])*')//s; - $extracted = $1; - -but with C it can be simplified to: - - ($extracted,$remainder) = extract_delimited($text, "'"); - -C takes up to four scalars (the input text, the -delimiters, a prefix pattern to be skipped, and any escape characters) -and extracts the initial substring of the text that -is appropriately delimited. If the delimiter string has multiple -characters, the first one encountered in the text is taken to delimit -the substring. -The third argument specifies a prefix pattern that is to be skipped -(but must be present!) before the substring is extracted. -The final argument specifies the escape character to be used for each -delimiter. - -All arguments are optional. If the escape characters are not specified, -every delimiter is escaped with a backslash (C<\>). -If the prefix is not specified, the -pattern C<'\s*'> - optional whitespace - is used. If the delimiter set -is also not specified, the set C is used. If the text to be processed -is not specified either, C<$_> is used. - -In list context, C returns a array of three -elements, the extracted substring (I), the remainder of the text, and the skipped prefix (if -any). If a suitable delimited substring is not found, the first -element of the array is the empty string, the second is the complete -original text, and the prefix returned in the third element is an -empty string. - -In a scalar context, just the extracted substring is returned. In -a void context, the extracted substring (and any prefix) are simply -removed from the beginning of the first argument. - -Examples: - - # Remove a single-quoted substring from the very beginning of $text: - - $substring = extract_delimited($text, "'", ''); - - # Remove a single-quoted Pascalish substring (i.e. one in which - # doubling the quote character escapes it) from the very - # beginning of $text: - - $substring = extract_delimited($text, "'", '', "'"); - - # Extract a single- or double- quoted substring from the - # beginning of $text, optionally after some whitespace - # (note the list context to protect $text from modification): - - ($substring) = extract_delimited $text, q{"'}; - - - # Delete the substring delimited by the first '/' in $text: - - $text = join '', (extract_delimited($text,'/','[^/]*')[2,1]; - -Note that this last example is I the same as deleting the first -quote-like pattern. For instance, if C<$text> contained the string: - - "if ('./cmd' =~ m/$UNIXCMD/s) { $cmd = $1; }" - -then after the deletion it would contain: - - "if ('.$UNIXCMD/s) { $cmd = $1; }" - -not: - - "if ('./cmd' =~ ms) { $cmd = $1; }" - - -See L<"extract_quotelike"> for a (partial) solution to this problem. - - -=head2 C - -Like C<"extract_delimited">, the C function takes -up to three optional scalar arguments: a string to extract from, a delimiter -specifier, and a prefix pattern. As before, a missing prefix defaults to -optional whitespace and a missing text defaults to C<$_>. However, a missing -delimiter specifier defaults to C<'{}()[]EE'> (see below). - -C extracts a balanced-bracket-delimited -substring (using any one (or more) of the user-specified delimiter -brackets: '(..)', '{..}', '[..]', or '<..>'). Optionally it will also -respect quoted unbalanced brackets (see below). - -A "delimiter bracket" is a bracket in list of delimiters passed as -C's second argument. Delimiter brackets are -specified by giving either the left or right (or both!) versions -of the required bracket(s). Note that the order in which -two or more delimiter brackets are specified is not significant. - -A "balanced-bracket-delimited substring" is a substring bounded by -matched brackets, such that any other (left or right) delimiter -bracket I the substring is also matched by an opposite -(right or left) delimiter bracket I. Any -type of bracket not in the delimiter list is treated as an ordinary -character. - -In other words, each type of bracket specified as a delimiter must be -balanced and correctly nested within the substring, and any other kind of -("non-delimiter") bracket in the substring is ignored. - -For example, given the string: - - $text = "{ an '[irregularly :-(] {} parenthesized >:-)' string }"; - -then a call to C in a list context: - - @result = extract_bracketed( $text, '{}' ); - -would return: - - ( "{ an '[irregularly :-(] {} parenthesized >:-)' string }" , "" , "" ) - -since both sets of C<'{..}'> brackets are properly nested and evenly balanced. -(In a scalar context just the first element of the array would be returned. In -a void context, C<$text> would be replaced by an empty string.) - -Likewise the call in: - - @result = extract_bracketed( $text, '{[' ); - -would return the same result, since all sets of both types of specified -delimiter brackets are correctly nested and balanced. - -However, the call in: - - @result = extract_bracketed( $text, '{([<' ); - -would fail, returning: - - ( undef , "{ an '[irregularly :-(] {} parenthesized >:-)' string }" ); - -because the embedded pairs of C<'(..)'>s and C<'[..]'>s are "cross-nested" and -the embedded C<'E'> is unbalanced. (In a scalar context, this call would -return an empty string. In a void context, C<$text> would be unchanged.) - -Note that the embedded single-quotes in the string don't help in this -case, since they have not been specified as acceptable delimiters and are -therefore treated as non-delimiter characters (and ignored). - -However, if a particular species of quote character is included in the -delimiter specification, then that type of quote will be correctly handled. -for example, if C<$text> is: - - $text = 'link'; - -then - - @result = extract_bracketed( $text, '<">' ); - -returns: - - ( '', 'link', "" ) - -as expected. Without the specification of C<"> as an embedded quoter: - - @result = extract_bracketed( $text, '<>' ); - -the result would be: - - ( 'link', "" ) - -In addition to the quote delimiters C<'>, C<">, and C<`>, full Perl quote-like -quoting (i.e. q{string}, qq{string}, etc) can be specified by including the -letter 'q' as a delimiter. Hence: - - @result = extract_bracketed( $text, '' ); - -would correctly match something like this: - - $text = ''; - -See also: C<"extract_quotelike"> and C<"extract_codeblock">. - - -=head2 C - -C extracts and segments text between (balanced) -specified tags. - -The subroutine takes up to five optional arguments: - -=over 4 - -=item 1. - -A string to be processed (C<$_> if the string is omitted or C) - -=item 2. - -A string specifying a pattern to be matched as the opening tag. -If the pattern string is omitted (or C) then a pattern -that matches any standard HTML/XML tag is used. - -=item 3. - -A string specifying a pattern to be matched at the closing tag. -If the pattern string is omitted (or C) then the closing -tag is constructed by inserting a C after any leading bracket -characters in the actual opening tag that was matched (I the pattern -that matched the tag). For example, if the opening tag pattern -is specified as C<'{{\w+}}'> and actually matched the opening tag -C<"{{DATA}}">, then the constructed closing tag would be C<"{{/DATA}}">. - -=item 4. - -A string specifying a pattern to be matched as a prefix (which is to be -skipped). If omitted, optional whitespace is skipped. - -=item 5. - -A hash reference containing various parsing options (see below) - -=back - -The various options that can be specified are: - -=over 4 - -=item C $listref> - -The list reference contains one or more strings specifying patterns -that must I appear within the tagged text. - -For example, to extract -an HTML link (which should not contain nested links) use: - - extract_tagged($text, '', '', undef, {reject => ['']} ); - -=item C $listref> - -The list reference contains one or more strings specifying patterns -that are I be be treated as nested tags within the tagged text -(even if they would match the start tag pattern). - -For example, to extract an arbitrary XML tag, but ignore "empty" elements: - - extract_tagged($text, undef, undef, undef, {ignore => ['<[^>]*/>']} ); - -(also see L<"gen_delimited_pat"> below). - - -=item C $str> - -The C option indicates the action to be taken if a matching end -tag is not encountered (i.e. before the end of the string or some -C pattern matches). By default, a failure to match a closing -tag causes C to immediately fail. - -However, if the string value associated with is "MAX", then -C returns the complete text up to the point of failure. -If the string is "PARA", C returns only the first paragraph -after the tag (up to the first line that is either empty or contains -only whitespace characters). -If the string is "", the the default behaviour (i.e. failure) is reinstated. - -For example, suppose the start tag "/para" introduces a paragraph, which then -continues until the next "/endpara" tag or until another "/para" tag is -encountered: - - $text = "/para line 1\n\nline 3\n/para line 4"; - - extract_tagged($text, '/para', '/endpara', undef, - {reject => '/para', fail => MAX ); - - # EXTRACTED: "/para line 1\n\nline 3\n" - -Suppose instead, that if no matching "/endpara" tag is found, the "/para" -tag refers only to the immediately following paragraph: - - $text = "/para line 1\n\nline 3\n/para line 4"; - - extract_tagged($text, '/para', '/endpara', undef, - {reject => '/para', fail => MAX ); - - # EXTRACTED: "/para line 1\n" - -Note that the specified C behaviour applies to nested tags as well. - -=back - -On success in a list context, an array of 6 elements is returned. The elements are: - -=over 4 - -=item [0] - -the extracted tagged substring (including the outermost tags), - -=item [1] - -the remainder of the input text, - -=item [2] - -the prefix substring (if any), - -=item [3] - -the opening tag - -=item [4] - -the text between the opening and closing tags - -=item [5] - -the closing tag (or "" if no closing tag was found) - -=back - -On failure, all of these values (except the remaining text) are C. - -In a scalar context, C returns just the complete -substring that matched a tagged text (including the start and end -tags). C is returned on failure. In addition, the original input -text has the returned substring (and any prefix) removed from it. - -In a void context, the input text just has the matched substring (and -any specified prefix) removed. - - -=head2 C - -(Note: This subroutine is only available under Perl5.005) - -C generates a new anonymous subroutine which -extracts text between (balanced) specified tags. In other words, -it generates a function identical in function to C. - -The difference between C and the anonymous -subroutines generated by -C, is that those generated subroutines: - -=over 4 - -=item * - -do not have to reparse tag specification or parsing options every time -they are called (whereas C has to effectively rebuild -its tag parser on every call); - -=item * - -make use of the new qr// construct to pre-compile the regexes they use -(whereas C uses standard string variable interpolation -to create tag-matching patterns). - -=back - -The subroutine takes up to four optional arguments (the same set as -C except for the string to be processed). It returns -a reference to a subroutine which in turn takes a single argument (the text to -be extracted from). - -In other words, the implementation of C is exactly -equivalent to: - - sub extract_tagged - { - my $text = shift; - $extractor = gen_extract_tagged(@_); - return $extractor->($text); - } - -(although C is not currently implemented that way, in order -to preserve pre-5.005 compatibility). - -Using C to create extraction functions for specific tags -is a good idea if those functions are going to be called more than once, since -their performance is typically twice as good as the more general-purpose -C. - - -=head2 C - -C attempts to recognize, extract, and segment any -one of the various Perl quotes and quotelike operators (see -L) Nested backslashed delimiters, embedded balanced bracket -delimiters (for the quotelike operators), and trailing modifiers are -all caught. For example, in: - - extract_quotelike 'q # an octothorpe: \# (not the end of the q!) #' - - extract_quotelike ' "You said, \"Use sed\"." ' - - extract_quotelike ' s{([A-Z]{1,8}\.[A-Z]{3})} /\L$1\E/; ' - - extract_quotelike ' tr/\\\/\\\\/\\\//ds; ' - -the full Perl quotelike operations are all extracted correctly. - -Note too that, when using the /x modifier on a regex, any comment -containing the current pattern delimiter will cause the regex to be -immediately terminated. In other words: - - 'm / - (?i) # CASE INSENSITIVE - [a-z_] # LEADING ALPHABETIC/UNDERSCORE - [a-z0-9]* # FOLLOWED BY ANY NUMBER OF ALPHANUMERICS - /x' - -will be extracted as if it were: - - 'm / - (?i) # CASE INSENSITIVE - [a-z_] # LEADING ALPHABETIC/' - -This behaviour is identical to that of the actual compiler. - -C takes two arguments: the text to be processed and -a prefix to be matched at the very beginning of the text. If no prefix -is specified, optional whitespace is the default. If no text is given, -C<$_> is used. - -In a list context, an array of 11 elements is returned. The elements are: - -=over 4 - -=item [0] - -the extracted quotelike substring (including trailing modifiers), - -=item [1] - -the remainder of the input text, - -=item [2] - -the prefix substring (if any), - -=item [3] - -the name of the quotelike operator (if any), - -=item [4] - -the left delimiter of the first block of the operation, - -=item [5] - -the text of the first block of the operation -(that is, the contents of -a quote, the regex of a match or substitution or the target list of a -translation), - -=item [6] - -the right delimiter of the first block of the operation, - -=item [7] - -the left delimiter of the second block of the operation -(that is, if it is a C, C, or C), - -=item [8] - -the text of the second block of the operation -(that is, the replacement of a substitution or the translation list -of a translation), - -=item [9] - -the right delimiter of the second block of the operation (if any), - -=item [10] - -the trailing modifiers on the operation (if any). - -=back - -For each of the fields marked "(if any)" the default value on success is -an empty string. -On failure, all of these values (except the remaining text) are C. - - -In a scalar context, C returns just the complete substring -that matched a quotelike operation (or C on failure). In a scalar or -void context, the input text has the same substring (and any specified -prefix) removed. - -Examples: - - # Remove the first quotelike literal that appears in text - - $quotelike = extract_quotelike($text,'.*?'); - - # Replace one or more leading whitespace-separated quotelike - # literals in $_ with "" - - do { $_ = join '', (extract_quotelike)[2,1] } until $@; - - - # Isolate the search pattern in a quotelike operation from $text - - ($op,$pat) = (extract_quotelike $text)[3,5]; - if ($op =~ /[ms]/) - { - print "search pattern: $pat\n"; - } - else - { - print "$op is not a pattern matching operation\n"; - } - - -=head2 C and "here documents" - -C can successfully extract "here documents" from an input -string, but with an important caveat in list contexts. - -Unlike other types of quote-like literals, a here document is rarely -a contiguous substring. For example, a typical piece of code using -here document might look like this: - - <<'EOMSG' || die; - This is the message. - EOMSG - exit; - -Given this as an input string in a scalar context, C -would correctly return the string "<<'EOMSG'\nThis is the message.\nEOMSG", -leaving the string " || die;\nexit;" in the original variable. In other words, -the two separate pieces of the here document are successfully extracted and -concatenated. - -In a list context, C would return the list - -=over 4 - -=item [0] - -"<<'EOMSG'\nThis is the message.\nEOMSG\n" (i.e. the full extracted here document, -including fore and aft delimiters), - -=item [1] - -" || die;\nexit;" (i.e. the remainder of the input text, concatenated), - -=item [2] - -"" (i.e. the prefix substring -- trivial in this case), - -=item [3] - -"<<" (i.e. the "name" of the quotelike operator) - -=item [4] - -"'EOMSG'" (i.e. the left delimiter of the here document, including any quotes), - -=item [5] - -"This is the message.\n" (i.e. the text of the here document), - -=item [6] - -"EOMSG" (i.e. the right delimiter of the here document), - -=item [7..10] - -"" (a here document has no second left delimiter, second text, second right -delimiter, or trailing modifiers). - -=back - -However, the matching position of the input variable would be set to -"exit;" (i.e. I the closing delimiter of the here document), -which would cause the earlier " || die;\nexit;" to be skipped in any -sequence of code fragment extractions. - -To avoid this problem, when it encounters a here document whilst -extracting from a modifiable string, C silently -rearranges the string to an equivalent piece of Perl: - - <<'EOMSG' - This is the message. - EOMSG - || die; - exit; - -in which the here document I contiguous. It still leaves the -matching position after the here document, but now the rest of the line -on which the here document starts is not skipped. - -To prevent from mucking about with the input in this way -(this is the only case where a list-context C does so), -you can pass the input variable as an interpolated literal: - - $quotelike = extract_quotelike("$var"); - - -=head2 C - -C attempts to recognize and extract a balanced -bracket delimited substring that may contain unbalanced brackets -inside Perl quotes or quotelike operations. That is, C -is like a combination of C<"extract_bracketed"> and -C<"extract_quotelike">. - -C takes the same initial three parameters as C: -a text to process, a set of delimiter brackets to look for, and a prefix to -match first. It also takes an optional fourth parameter, which allows the -outermost delimiter brackets to be specified separately (see below). - -Omitting the first argument (input text) means process C<$_> instead. -Omitting the second argument (delimiter brackets) indicates that only C<'{'> is to be used. -Omitting the third argument (prefix argument) implies optional whitespace at the start. -Omitting the fourth argument (outermost delimiter brackets) indicates that the -value of the second argument is to be used for the outermost delimiters. - -Once the prefix an dthe outermost opening delimiter bracket have been -recognized, code blocks are extracted by stepping through the input text and -trying the following alternatives in sequence: - -=over 4 - -=item 1. - -Try and match a closing delimiter bracket. If the bracket was the same -species as the last opening bracket, return the substring to that -point. If the bracket was mismatched, return an error. - -=item 2. - -Try to match a quote or quotelike operator. If found, call -C to eat it. If C fails, return -the error it returned. Otherwise go back to step 1. - -=item 3. - -Try to match an opening delimiter bracket. If found, call -C recursively to eat the embedded block. If the -recursive call fails, return an error. Otherwise, go back to step 1. - -=item 4. - -Unconditionally match a bareword or any other single character, and -then go back to step 1. - -=back - - -Examples: - - # Find a while loop in the text - - if ($text =~ s/.*?while\s*\{/{/) - { - $loop = "while " . extract_codeblock($text); - } - - # Remove the first round-bracketed list (which may include - # round- or curly-bracketed code blocks or quotelike operators) - - extract_codeblock $text, "(){}", '[^(]*'; - - -The ability to specify a different outermost delimiter bracket is useful -in some circumstances. For example, in the Parse::RecDescent module, -parser actions which are to be performed only on a successful parse -are specified using a Cdefer:...E> directive. For example: - - sentence: subject verb object - - -Parse::RecDescent uses CE')> to extract the code -within the Cdefer:...E> directive, but there's a problem. - -A deferred action like this: - - 10) {$count--}} > - -will be incorrectly parsed as: - - - -because the "less than" operator is interpreted as a closing delimiter. - -But, by extracting the directive using -SE')>> -the '>' character is only treated as a delimited at the outermost -level of the code block, so the directive is parsed correctly. - -=head2 C - -The C subroutine takes a string to be processed and a -list of extractors (subroutines or regular expressions) to apply to that string. - -In an array context C returns an array of substrings -of the original string, as extracted by the specified extractors. -In a scalar context, C returns the first -substring successfully extracted from the original string. In both -scalar and void contexts the original string has the first successfully -extracted substring removed from it. In all contexts -C starts at the current C of the string, and -sets that C appropriately after it matches. - -Hence, the aim of of a call to C in a list context -is to split the processed string into as many non-overlapping fields as -possible, by repeatedly applying each of the specified extractors -to the remainder of the string. Thus C is -a generalized form of Perl's C subroutine. - -The subroutine takes up to four optional arguments: - -=over 4 - -=item 1. - -A string to be processed (C<$_> if the string is omitted or C) - -=item 2. - -A reference to a list of subroutine references and/or qr// objects and/or -literal strings and/or hash references, specifying the extractors -to be used to split the string. If this argument is omitted (or -C) the list: - - [ - sub { extract_variable($_[0], '') }, - sub { extract_quotelike($_[0],'') }, - sub { extract_codeblock($_[0],'{}','') }, - ] - -is used. - - -=item 3. - -An number specifying the maximum number of fields to return. If this -argument is omitted (or C), split continues as long as possible. - -If the third argument is I, then extraction continues until I fields -have been successfully extracted, or until the string has been completely -processed. - -Note that in scalar and void contexts the value of this argument is -automatically reset to 1 (under C<-w>, a warning is issued if the argument -has to be reset). - -=item 4. - -A value indicating whether unmatched substrings (see below) within the -text should be skipped or returned as fields. If the value is true, -such substrings are skipped. Otherwise, they are returned. - -=back - -The extraction process works by applying each extractor in -sequence to the text string. If the extractor is a subroutine it -is called in a list -context and is expected to return a list of a single element, namely -the extracted text. -Note that the value returned by an extractor subroutine need not bear any -relationship to the corresponding substring of the original text (see -examples below). - -If the extractor is a precompiled regular expression or a string, -it is matched against the text in a scalar context with a leading -'\G' and the gc modifiers enabled. The extracted value is either -$1 if that variable is defined after the match, or else the -complete match (i.e. $&). - -If the extractor is a hash reference, it must contain exactly one element. -The value of that element is one of the -above extractor types (subroutine reference, regular expression, or string). -The key of that element is the name of a class into which the successful -return value of the extractor will be blessed. - -If an extractor returns a defined value, that value is immediately -treated as the next extracted field and pushed onto the list of fields. -If the extractor was specified in a hash reference, the field is also -blessed into the appropriate class, - -If the extractor fails to match (in the case of a regex extractor), or returns an empty list or an undefined value (in the case of a subroutine extractor), it is -assumed to have failed to extract. -If none of the extractor subroutines succeeds, then one -character is extracted from the start of the text and the extraction -subroutines reapplied. Characters which are thus removed are accumulated and -eventually become the next field (unless the fourth argument is true, in which -case they are disgarded). - -For example, the following extracts substrings that are valid Perl variables: - - @fields = extract_multiple($text, - [ sub { extract_variable($_[0]) } ], - undef, 1); - -This example separates a text into fields which are quote delimited, -curly bracketed, and anything else. The delimited and bracketed -parts are also blessed to identify them (the "anything else" is unblessed): - - @fields = extract_multiple($text, - [ - { Delim => sub { extract_delimited($_[0],q{'"}) } }, - { Brack => sub { extract_bracketed($_[0],'{}') } }, - ]); - -This call extracts the next single substring that is a valid Perl quotelike -operator (and removes it from $text): - - $quotelike = extract_multiple($text, - [ - sub { extract_quotelike($_[0]) }, - ], undef, 1); - -Finally, here is yet another way to do comma-separated value parsing: - - @fields = extract_multiple($csv_text, - [ - sub { extract_delimited($_[0],q{'"}) }, - qr/([^,]+)(.*)/, - ], - undef,1); - -The list in the second argument means: -I<"Try and extract a ' or " delimited string, otherwise extract anything up to a comma...">. -The undef third argument means: -I<"...as many times as possible...">, -and the true value in the fourth argument means -I<"...discarding anything else that appears (i.e. the commas)">. - -If you wanted the commas preserved as separate fields (i.e. like split -does if your split pattern has capturing parentheses), you would -just make the last parameter undefined (or remove it). - - -=head2 C - -The C subroutine takes a single (string) argument and -builds a Friedl-style optimized regex that matches a string delimited -by any one of the characters in the single argument. For example: - - gen_delimited_pat(q{'"}) - -returns the regex: - - (?:\"(?:\\\"|(?!\").)*\"|\'(?:\\\'|(?!\').)*\') - -Note that the specified delimiters are automatically quotemeta'd. - -A typical use of C would be to build special purpose tags -for C. For example, to properly ignore "empty" XML elements -(which might contain quoted strings): - - my $empty_tag = '<(' . gen_delimited_pat(q{'"}) . '|.)+/>'; - - extract_tagged($text, undef, undef, undef, {ignore => [$empty_tag]} ); - - -C may also be called with an optional second argument, -which specifies the "escape" character(s) to be used for each delimiter. -For example to match a Pascal-style string (where ' is the delimiter -and '' is a literal ' within the string): - - gen_delimited_pat(q{'},q{'}); - -Different escape characters can be specified for different delimiters. -For example, to specify that '/' is the escape for single quotes -and '%' is the escape for double quotes: - - gen_delimited_pat(q{'"},q{/%}); - -If more delimiters than escape chars are specified, the last escape char -is used for the remaining delimiters. -If no escape char is specified for a given specified delimiter, '\' is used. - -Note that -C was previously called -C. That name may still be used, but is now deprecated. - - -=head1 DIAGNOSTICS - -In a list context, all the functions return C<(undef,$original_text)> -on failure. In a scalar context, failure is indicated by returning C -(in this case the input text is not modified in any way). - -In addition, on failure in I context, the C<$@> variable is set. -Accessing C<$@-E{error}> returns one of the error diagnostics listed -below. -Accessing C<$@-E{pos}> returns the offset into the original string at -which the error was detected (although not necessarily where it occurred!) -Printing C<$@> directly produces the error message, with the offset appended. -On success, the C<$@> variable is guaranteed to be C. - -The available diagnostics are: - -=over 4 - -=item C - -The delimiter provided to C was not one of -C<'()[]EE{}'>. - -=item C - -A non-optional prefix was specified but wasn't found at the start of the text. - -=item C - -C or C was expecting a -particular kind of bracket at the start of the text, and didn't find it. - -=item C - -C didn't find one of the quotelike operators C, -C, C, C, C, C or C at the start of the substring -it was extracting. - -=item C - -C, C or C encountered -a closing bracket where none was expected. - -=item C - -C, C or C ran -out of characters in the text before closing one or more levels of nested -brackets. - -=item C - -C attempted to match an embedded quoted substring, but -failed to find a closing quote to match it. - -=item C - -C was unable to find a closing delimiter to match the -one that opened the quote-like operation. - -=item C - -C, C or C found -a valid bracket delimiter, but it was the wrong species. This usually -indicates a nesting error, but may indicate incorrect quoting or escaping. - -=item C - -C or C found one of the -quotelike operators C, C, C, C, C, C or C -without a suitable block after it. - -=item C - -C was expecting one of '$', '@', or '%' at the start of -a variable, but didn't find any of them. - -=item C - -C found a '$', '@', or '%' indicating a variable, but that -character was not followed by a legal Perl identifier. - -=item C - -C failed to find any of the outermost opening brackets -that were specified. - -=item C - -A nested code block was found that started with a delimiter that was specified -as being only to be used as an outermost bracket. - -=item C - -C or C found one of the -quotelike operators C, C or C followed by only one block. - -=item C - -C failed to find a closing bracket to match the outermost -opening bracket. - -=item C - -C did not find a suitable opening tag (after any specified -prefix was removed). - -=item C - -C matched the specified opening tag and tried to -modify the matched text to produce a matching closing tag (because -none was specified). It failed to generate the closing tag, almost -certainly because the opening tag did not start with a -bracket of some kind. - -=item C - -C found a nested tag that appeared in the "reject" list -(and the failure mode was not "MAX" or "PARA"). - -=item C - -C found a nested opening tag that was not matched by a -corresponding nested closing tag (and the failure mode was not "MAX" or "PARA"). - -=item C - -C reached the end of the text without finding a closing tag -to match the original opening tag (and the failure mode was not -"MAX" or "PARA"). - - - - -=back - - -=head1 AUTHOR - -Damian Conway (damian@conway.org) - - -=head1 BUGS AND IRRITATIONS - -There are undoubtedly serious bugs lurking somewhere in this code, if -only because parts of it give the impression of understanding a great deal -more about Perl than they really do. - -Bug reports and other feedback are most welcome. - - -=head1 COPYRIGHT - - Copyright (c) 1997-2000, Damian Conway. All Rights Reserved. -This module is free software; you can redistribute it and/or -modify it under the same terms as Perl itself. diff --git a/lib/Text/Balanced/Changes b/lib/Text/Balanced/Changes new file mode 100644 index 0000000..5b34b73 --- /dev/null +++ b/lib/Text/Balanced/Changes @@ -0,0 +1,248 @@ +Revision history for Perl extension Text::Balanced. + +1.00 Mon Aug 11 12:42:56 1997 + + - original version + + +1.01 Mon Sep 8 18:09:18 EST 1997 + + - changed "quotemeta" to "quotemeta $_" to work + around bug in Perl 5.002 and 5.003 + + +1.10 Tue Sep 30 17:23:23 EST 1997 + + - reworked extract_quotelike to correct handling of some obscure cases + + +1.21 Sat Oct 4 17:21:54 EST 1997 + + - synchronised with Parse::RecDescent distribution (version number + will now reflect that package) + +1.23 Fri Oct 17 10:26:38 EST 1997 + + - changed behaviour in scalar and void contexts. Scalar contexts + now return only the extracted string. Void contexts now remove + the extracted string from the first argument (or $_). + +1.24 + + - changed behaviour in scalar contexts. Scalar contexts + now return the extracted string _and_ remove it from the + first argument (or $_). + + - changed return values on failure (all contexts return undef + for invalid return fields) + + - fixed some lurking bugs with trailing modifier handling + + - added :ALL tag to simplify wholesale importing of functions + + - fixed serious bug with embedded division operators ("/") + This now also allows the ?...? form of pattern matching! + +1.35 Wed Jun 24 09:53:31 1998 + + - fixed handling of :: quantifiers in extract_variable() + + - numerous trivial lexical changes to make xemacs happy + + +1.36 Tue Jul 14 12:26:04 1998 + + - Reinstated POD file missing from previous distribution + + - Added undocumented fourth parameter to extract_codeblock + so as to correctly handle (?) and (s?) modifiers in + RecDescent grammars. + + +1.40 Tue Aug 4 13:54:52 1998 + + - Added (optional) handling of embedded quoted text to + extract_delimited (see revised entry in Balanced.pod) + + - Added extract_tagged which extracts text between arbitrary, + optionally nested start and end tags (see new entry in + Balanced.pod). + + - Added delimited_pat which builds a pattern which matches a + string delimited by any of the delimiters specified (see new + entry in Balanced.pod). + + - Added test.pl + + +1.41 Mon Aug 10 14:51:50 1998 + + - Reinstated change to extract_codeblock from 1.36 which were + mysteriously lost in 1.40 + + +1.50 Thu Aug 27 09:20:19 1998 + + - Improved the structure of the regex generated by + delimited_pat (and used in extract_delimited). It's + considerably more complex, but also more robust and + much faster in the worst case. + + - Altered extract_variable to accept whitespace in variables, + e.g. '$ a -> {'b'} -> [2]' + + + +1.51 Sat Feb 13 10:31:55 1999 + + - Fixed bugs in prefix matching in extract_variable: + * incorrectly used default if '' specified + * now handles $#array correctly + + - Fixed bugs in extract_codeblock: + * Now handles !~ properly + * Now handles embedded comments better. + * Now handles "raw" pattern matches better. + + - Added support for single strings or qr's as + 'reject' and 'ignore' args to extract_tagged() + + - Added gen_extract_tagged() to "precompile" + a specific tag extractor for repeated use + (approximately 3 times faster!) + + +1.52 Thu Mar 4 12:43:38 1999 + + - Added CSV parsing example to documentation of extract_multiple. + + - Fixed a bug with extract_codeblock in "RecDescent" mode + (it would accept "subrule(s?)" and "subrule(?)", but + not "subrule(s)"). Thanks, Jan. + + +1.66 Fri Jul 2 13:29:22 1999 + + - Added ability to use quotelike operators in extract_bracketed + + - Fixed bug under 5.003 ('foreach my $func' not understood) + + - Added escape specification as fourth arg to &extract_delimited + + - Fixed handling of &delimited_pat and &extract_delimited + when delimiter is same as escape + + - Fixed handling of ->, =>, and >> in &extract_code + when delimiters are "<>" + + +1.76 Fri Nov 19 06:51:54 1999 + + - IMPORTANT: Now requires 5.005 or better. + + - IMPORTANT: Made extract methods sensitive to the pos() + value of the text they are parsing. In other words, + all extract subroutines now act like patterns of the form + /\G.../gc. See documentation for details. + + - IMPORTANT: Changed semantics of extract_multiple, in line + with the above change, and to simplify the semantics to + something vaguely predictable. See documentation for details. + + - Added ability to use qr/../'s and raw strings as extractors + in extract_multiple. See documentation. + + - Added fourth argument to extract_codeblock to allow + outermost brackets to be separately specified. See + documentation for details. + + - Reimplemented internals of all extraction subroutines + for significant speed-ups (between 100% and 2000% + improvement). + + - Fixed nasty bug in extract_variable and extract_codeblock + (they were returning prefix as well in scalar context) + + - Allowed read-only strings to be used as arguments in + scalar contexts. + + - Renamed delimited_pat to gen-delimited pat (in line with + gen_extract_tagged). Old name still works, but is now deprecated. + + - Tweaked all extraction subs so they correctly handle + zero-length prefix matches after another zero-length match. + + +1.77 Mon Nov 22 06:08:23 1999 + + - Fixed major bug in extract_codeblock (would not + terminate if there was trailing whitespace) + + - Improved /.../ pattern parsing within codeblocks + + +1.81 Wed Sep 13 11:58:49 2000 + + - Fixed test count in extract_codeblock.t + + - Fixed improbable bug with trailing ->'s in extract_variable + + - Fixed (HT|X)ML tag extraction in extract_tagged (thanks, Tim) + + - Added explanatory note about prefix matching (thanks again, Tim) + + - Added handling of globs and sub refs to extract_variable + + - Pod tweak (thanks Abigail) + + - Allowed right tags to be run-time evaluated, so + extract_tagged($text, '/([a-z]+)', '/end$1') works + as expected. + + - Added optional blessing of matches via extract_multiple + + - Fixed bug in autogeneration of closing tags in extract_tagged + (Thanks, Coke) + + - Fixed bug in interaction between extract_multiple and + gen_extract_tagged (Thanks Anthony) + + +1.82 Sun Jan 14 16:56:04 2001 + + - Fixed nit in extract_variable.t + (tested more cases than it promised to) + + - Fixed bug extracting prefix in extract_quotelike (Thanks Michael) + + - Added handling of Perl 4 package qualifier: $Package'var, etc. + + - Added handling of here docs (see documentation for limitations) + + - Added reporting of failure position via $@->{pos} (see documentation) + + +1.83 Mon Jan 15 12:43:12 2001 + + - Fixed numerous bugs in here doc extraction (many thanks Tim) + + +1.84 Thu Apr 26 11:58:13 2001 + + - Fixed bug in certain extractions not matching strings + with embedded newlines (thanks Robin) + + +1.85 Sun Jun 3 07:47:18 2001 + + - Fixed bug in extract_variable recognizing method calls that + start with an underscore (thanks Jeff) + + +1.86 Mon Sep 3 06:57:08 2001 + + - Revised licence for inclusion in core distribution + + - Consolidated POD in .pm file + + - renamed tests to let DOS cope with them diff --git a/lib/Text/Balanced/README b/lib/Text/Balanced/README new file mode 100755 index 0000000..feba188 --- /dev/null +++ b/lib/Text/Balanced/README @@ -0,0 +1,88 @@ +============================================================================== + Release of version 1.86 of Text::Balanced +============================================================================== + + +NAME + + Text::Balanced - Extract delimited text sequences from strings. + + +SUMMARY (see Balanced.pod for full details) + + Text::Balanced::extract_delimited + + `extract_delimited' extracts the initial substring of a string + which is delimited by a user-specified set of single-character + delimiters, whilst ignoring any backslash-escaped delimiter + characters. + + Text::Balanced::extract_bracketed + + `extract_bracketed' extracts a balanced-bracket-delimited substring + (using any one (or more) of the user-specified delimiter brackets: + '(..)', '{..}', '[..]', or '<..>'). + + Text::Balanced::extract_quotelike + + `extract_quotelike' attempts to recognize and extract any one of the + various Perl quote and quotelike operators (see "perlop(3)"). Embedded + backslashed delimiters, nested bracket delimiters (for the + quotelike operators), and trailing modifiers are all correctly handled. + + Text::Balanced::extract_codeblock + + `extract_codeblock' attempts to recognize and extract a + balanced bracket-delimited substring which may also contain + unbalanced brackets inside Perl quotes or quotelike + operations. That is, `extract_codeblock' is like a combination + of `extract_bracketed' and `extract_quotelike'. + + Text::Balanced::extract_tagged + + `extract_tagged' attempts to recognize and extract a + substring between two arbitrary "tag" patterns (a start tag + and an end tag). + + +INSTALLATION + + It's all pure Perl, so just put the .pm file in its appropriate + local Perl subdirectory. + + +AUTHOR + + Damian Conway (damian@cs.monash.edu.au) + + +COPYRIGHT + + Copyright (c) 1997-2001, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the same terms as Perl itself. + + + +============================================================================== + +CHANGES IN VERSION 1.86 + + + - Revised licence for inclusion in core distribution + + - Consolidated POD in .pm file + + - renamed tests to let DOS cope with them + + +============================================================================== + +AVAILABILITY + +Text::Balanced has been uploaded to the CPAN +and is also available from: + + http://www.csse.monash.edu.au/~damian/CPAN/Text-Balanced.tar.gz + +============================================================================== diff --git a/lib/Text/Balanced/t/xbrak.t b/lib/Text/Balanced/t/extbrk.t similarity index 97% rename from lib/Text/Balanced/t/xbrak.t rename to lib/Text/Balanced/t/extbrk.t index 5a8e524..a36025d 100644 --- a/lib/Text/Balanced/t/xbrak.t +++ b/lib/Text/Balanced/t/extbrk.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/xcode.t b/lib/Text/Balanced/t/extcbk.t similarity index 93% rename from lib/Text/Balanced/t/xcode.t rename to lib/Text/Balanced/t/extcbk.t index 00be51e..10f9741 100644 --- a/lib/Text/Balanced/t/xcode.t +++ b/lib/Text/Balanced/t/extcbk.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' @@ -11,7 +6,7 @@ BEGIN { # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) -BEGIN { $| = 1; print "1..37\n"; } +BEGIN { $| = 1; print "1..41\n"; } END {print "not ok 1\n" unless $loaded;} use Text::Balanced qw ( extract_codeblock ); $loaded = 1; @@ -57,6 +52,12 @@ while (defined($str = )) __DATA__ +# USING: extract_codeblock($str); +{ $data[4] =~ /['"]/; }; + +# USING: extract_codeblock($str,'(){}',undef,'()'); +(Foo(')')); + # USING: extract_codeblock($str,'<>'); < %x = ( try => "this") >; < %x = () >; diff --git a/lib/Text/Balanced/t/xdeli.t b/lib/Text/Balanced/t/extdel.t similarity index 97% rename from lib/Text/Balanced/t/xdeli.t rename to lib/Text/Balanced/t/extdel.t index 7e5b06b..c5ca88e 100644 --- a/lib/Text/Balanced/t/xdeli.t +++ b/lib/Text/Balanced/t/extdel.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/xmult.t b/lib/Text/Balanced/t/extmul.t similarity index 99% rename from lib/Text/Balanced/t/xmult.t rename to lib/Text/Balanced/t/extmul.t index 31dd7d4..46addcc 100644 --- a/lib/Text/Balanced/t/xmult.t +++ b/lib/Text/Balanced/t/extmul.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/xquot.t b/lib/Text/Balanced/t/extqlk.t similarity index 97% rename from lib/Text/Balanced/t/xquot.t rename to lib/Text/Balanced/t/extqlk.t index 567e0a5..217d7d1 100644 --- a/lib/Text/Balanced/t/xquot.t +++ b/lib/Text/Balanced/t/extqlk.t @@ -1,10 +1,4 @@ -#!./perl -ws - -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - +#! /usr/local/bin/perl -ws # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/xtagg.t b/lib/Text/Balanced/t/exttag.t similarity index 98% rename from lib/Text/Balanced/t/xtagg.t rename to lib/Text/Balanced/t/exttag.t index c883181..764e790 100644 --- a/lib/Text/Balanced/t/xtagg.t +++ b/lib/Text/Balanced/t/exttag.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/xvari.t b/lib/Text/Balanced/t/extvar.t similarity index 97% rename from lib/Text/Balanced/t/xvari.t rename to lib/Text/Balanced/t/extvar.t index dd35b9c..93bd22b 100644 --- a/lib/Text/Balanced/t/xvari.t +++ b/lib/Text/Balanced/t/extvar.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' diff --git a/lib/Text/Balanced/t/genxt.t b/lib/Text/Balanced/t/gentag.t similarity index 97% rename from lib/Text/Balanced/t/genxt.t rename to lib/Text/Balanced/t/gentag.t index 6889653..4e68b41 100644 --- a/lib/Text/Balanced/t/genxt.t +++ b/lib/Text/Balanced/t/gentag.t @@ -1,8 +1,3 @@ -BEGIN { - chdir 't' if -d 't'; - @INC = '../lib'; -} - # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl'