File Coverage

blib/lib/Sub/Prototype/Util.pm
Criterion Covered Total %
statement 129 133 96.9
branch 67 70 95.7
condition 10 11 90.9
subroutine 16 16 100.0
pod 2 2 100.0
total 224 232 96.5


line stmt bran cond sub pod time code
1             package Sub::Prototype::Util;
2              
3 5     5   87747 use 5.006;
  5         19  
  5         204  
4              
5 5     5   30 use strict;
  5         11  
  5         195  
6 5     5   307 use warnings;
  5         23  
  5         182  
7              
8 5     5   37 use Carp qw;
  5         10  
  5         366  
9 5     5   31 use Scalar::Util qw;
  5         7  
  5         415  
10              
11             =head1 NAME
12              
13             Sub::Prototype::Util - Prototype-related utility routines.
14              
15             =head1 VERSION
16              
17             Version 0.11
18              
19             =cut
20              
21 5     5   27 use vars qw<$VERSION>;
  5         10  
  5         8390  
22              
23             $VERSION = '0.11';
24              
25             =head1 SYNOPSIS
26              
27             use Sub::Prototype::Util qw;
28              
29             my @a = qw;
30             my @args = ( \@a, 1, { d => 2 }, undef, 3 );
31              
32             my @flat = flatten '\@$;$', @args;
33             # @flat contains now ('a', 'b', 'c', 1, { d => 2 })
34              
35             my $res = recall 'CORE::push', @args;
36             # @a contains now 'a', 'b', 'c', 1, { d => 2 }, undef, 3
37             # and $res is 7
38              
39             my $splice = wrap 'CORE::splice';
40             my @b = $splice->(\@a, 4, 2);
41             # @a contains now ('a', 'b', 'c', 1, 3)
42             # and @b is ({ d => 2 }, undef)
43              
44             =head1 DESCRIPTION
45              
46             Prototypes are evil, but sometimes you just have to bear with them, especially when messing with core functions.
47             This module provides several utilities aimed at facilitating "overloading" of prototyped functions.
48              
49             They all handle C<5.10>'s C<_> prototype.
50              
51             =head1 FUNCTIONS
52              
53             =cut
54              
55             my %sigils = qw;
56             my %reftypes = reverse %sigils;
57              
58             sub _check_ref {
59 16     16   26 my ($arg, $sigil) = @_;
60              
61 16         18 my $reftype;
62 16 100 66     98 if (not defined $arg or not defined($reftype = reftype $arg)) {
63             # not defined or plain scalar
64 2 100       8 my $that = (defined $arg) ? 'a plain scalar' : 'undef';
65 2         404 croak "Got $that where a reference was expected";
66             }
67              
68 14 100 100     687 croak "Unexpected $reftype reference" unless exists $sigils{$reftype}
69             and $sigil =~ /\Q$sigils{$reftype}\E/;
70              
71 12         32 $reftype;
72             }
73              
74             sub _clean_msg {
75 9     9   11 my ($msg) = @_;
76              
77 9         139 $msg =~ s/(?:\s+called)?\s+at\s+.*$//s;
78              
79 9         1126 $msg;
80             }
81              
82             =head2 C
83              
84             my @flattened = flatten($proto, @args);
85              
86             Flattens the array C<@args> according to the prototype C<$proto>.
87             When C<@args> is what C<@_> is after calling a subroutine with prototype C<$proto>, C returns the list of what C<@_> would have been if there were no prototype.
88             It croaks if the arguments can't possibly match the required prototype, e.g. when a reference type is wrong or when not enough elements were provided.
89              
90             =cut
91              
92             sub flatten {
93 27     27 1 18272 my $proto = shift;
94              
95 27 100       73 return @_ unless defined $proto;
96              
97 26         31 my @args;
98 26         129 while ($proto =~ /(\\?)(\[[^\]]+\]|[^\];])/g) {
99 39         75 my $sigil = $2;
100              
101 39 100       148 if ($1) {
    100          
102 16         17 my $arg = shift;
103 16         34 my $reftype = _check_ref $arg, $sigil;
104              
105 12 100       97 push @args, $reftype eq 'SCALAR'
    100          
    100          
    100          
106             ? $$arg
107             : ($reftype eq 'ARRAY'
108             ? @$arg
109             : ($reftype eq 'HASH'
110             ? %$arg
111             : ($reftype eq 'GLOB'
112             ? *$arg
113             : &$arg # _check_ref ensures this must be a code ref
114             )
115             )
116             );
117              
118             } elsif ($sigil =~ /[\@\%]/) {
119 6         12 push @args, @_;
120 6         11 last;
121             } else {
122 17 100       215 croak 'Not enough arguments to match this prototype' unless @_;
123 16         64 push @args, shift;
124             }
125             }
126              
127 21         104 return @args;
128             }
129              
130             =head2 C
131              
132             my $wrapper = wrap($name, %opts);
133             my $wrapper = wrap({ $name => $proto }, %opts);
134              
135             Generates a wrapper that calls the function C<$name> with a prototyped argument list.
136             That is, the wrapper's arguments should be what C<@_> is when you define a subroutine with the same prototype as C<$name>.
137              
138             my $a = [ 0 .. 2 ];
139             my $push = wrap 'CORE::push';
140             $push->($a, 3, 4); # returns 3 + 2 = 5 and $a now contains 0 .. 4
141              
142             You can force the use of a specific prototype.
143             In this case, C<$name> must be a hash reference that holds exactly one key / value pair, the key being the function name and the value the prototpye that should be used to call it.
144              
145             my $push = wrap { 'CORE::push' => '\@$' }; # only pushes 1 arg
146              
147             The remaining arguments C<%opts> are treated as key / value pairs that are meant to tune the code generated by L.
148             Valid keys are :
149              
150             =over 4
151              
152             =item *
153              
154             C<< ref => $func >>
155              
156             Specifies the function used in the generated code to test the reference type of scalars.
157             Defaults to C<'ref'>.
158             You may also want to use L.
159              
160             =item *
161              
162             C<< wrong_ref => $code >>
163              
164             The code executed when a reference of incorrect type is encountered.
165             The result of this snippet is also the result of the generated code, hence it defaults to C<'undef'>.
166             It's a good place to C or C too.
167              
168             =item *
169              
170             C<< sub => $bool >>
171              
172             Encloses the code into a C block.
173             Default is true.
174              
175             =item *
176              
177             C<< compile => $bool >>
178              
179             Makes L compile the code generated and return the resulting code reference.
180             Be careful that in this case C must be a fully qualified function name.
181             Defaults to true, but turned off when C is false.
182              
183             =back
184              
185             For example, this allows you to recall into C and C by using the C<\&@> prototype :
186              
187             my $grep = wrap { 'CORE::grep' => '\&@' };
188             # the prototypes are intentionally different
189             sub mygrep (&@) { $grep->(@_) }
190              
191             =cut
192              
193             sub _wrap {
194 38     38   79 my ($name, $proto, $i, $args, $coderefs, $opts) = @_;
195              
196 38         202 while ($proto =~ s/(\\?)(\[[^\]]+\]|[^\];])//) {
197 45         125 my ($ref, $sigil) = ($1, $2);
198 45 100       127 $sigil = $1 if $sigil =~ /^\[([^\]]+)\]/;
199              
200 45         86 my $cur = "\$_[$i]";
201              
202 45 100       213 if ($ref) {
    100          
    100          
    100          
203 12 100       510 if (length $sigil > 1) {
204 7         22 my $code = "my \$r = $opts->{ref}($cur); ";
205 14         66 my @branches = map {
206 7         20 my $subcall = _wrap(
207             $name, $proto, ($i + 1), $args . "$_\{$cur}, ", $coderefs, $opts
208             );
209 14         76 "if (\$r eq '$reftypes{$_}') { $subcall }";
210             } split //, $sigil;
211 7         32 $code .= join ' els', @branches, "e { $opts->{wrong_ref} }";
212 7         27 return $code;
213             } else {
214 5         18 $args .= "$sigil\{$cur}, ";
215             }
216             } elsif ($sigil =~ /[\@\%]/) {
217 8         22 $args .= '@_[' . $i . '..$#_]';
218             } elsif ($sigil =~ /\&/) {
219 9         12 my %h = do { my $c; map { $_ => $c++ } @$coderefs };
  9         13  
  9         18  
  13         46  
220 9         17 my $j;
221 9 100       23 if (exists $h{$i}) {
222 6         10 $j = int $h{$i};
223             } else {
224 3         7 push @$coderefs, $i;
225 3         5 $j = $#{$coderefs};
  3         7  
226             }
227 9         35 $args .= "sub{&{\$c[$j]}}, ";
228             } elsif ($sigil eq '_') {
229 3         11 $args .= "((\@_ > $i) ? $cur : \$_), ";
230             } else {
231 13         31 $args .= "$cur, ";
232             }
233             } continue {
234 38         166 ++$i;
235             }
236              
237 31         108 $args =~ s/,\s*$//;
238              
239 31         102 return "$name($args)";
240             }
241              
242             sub _check_name {
243 37     37   59 my ($name) = @_;
244 37 100       890 croak 'No subroutine specified' unless $name;
245              
246 33         40 my $proto;
247 33         48 my $r = ref $name;
248 33 100       97 if (!$r) {
    100          
249 20         71 $proto = prototype $name;
250             } elsif ($r eq 'HASH') {
251 7 100       370 croak 'Forced prototype hash reference must contain exactly one key/value pair' unless keys %$name == 1;
252 5         17 ($name, $proto) = %$name;
253             } else {
254 6         870 croak 'Unhandled ' . $r . ' reference as first argument';
255             }
256              
257 25         99 $name =~ s/^\s+//;
258 25         47 $name =~ s/[\s\$\@\%\*\&;].*//;
259              
260 25         73 return $name, $proto;
261             }
262              
263             sub wrap {
264 37     37 1 12886 my ($name, $proto) = _check_name shift;
265 25 100       257 croak 'Optional arguments must be passed as key => value pairs' if @_ % 2;
266 24         58 my %opts = @_;
267              
268 24   100     115 $opts{ref} ||= 'ref';
269 24 100       66 $opts{sub} = 1 unless defined $opts{sub};
270 24 100 100     126 $opts{compile} = 1 if not defined $opts{compile} and $opts{sub};
271 24 100       71 $opts{wrong_ref} = 'undef' unless defined $opts{wrong_ref};
272              
273 24         27 my @coderefs;
274             my $call;
275 24 100       53 if (defined $proto) {
276 19         52 $call = _wrap $name, $proto, 0, '', \@coderefs, \%opts;
277             } else {
278 5         15 $call = _wrap $name, '', 0, '@_';
279             }
280              
281 24 100       67 if (@coderefs) {
282 2 100       11 my $decls = @coderefs > 1 ? 'my @c = @_[' . join(', ', @coderefs) . ']; '
283             : 'my @c = ($_[' . $coderefs[0] . ']); ';
284 2         8 $call = $decls . $call;
285             }
286              
287 24         51 $call = "{ $call }";
288 24 100       72 $call = "sub $call" if $opts{sub};
289              
290 24 100       60 if ($opts{compile}) {
291 19         20 my $err;
292             {
293 19         20 local $@;
  19         22  
294 19         2360 $call = eval $call;
295 19         52 $err = $@;
296             }
297 19 100       547 croak _clean_msg $err if $err;
298             }
299              
300 22         91 return $call;
301             }
302              
303             =head2 C
304              
305             my @res = recall($name, @args);
306             my @res = recall({ $name => $proto }, @args);
307              
308             Calls the function C<$name> with the prototyped argument list C<@args>.
309             That is, C<@args> should be what C<@_> is when you call a subroutine with C<$name> as prototype.
310             You can still force the prototype by passing C<< { $name => $proto } >> as the first argument.
311              
312             my $a = [ ];
313             recall { 'CORE::push' => '\@$' }, $a, 1, 2, 3; # $a just contains 1
314              
315             It's implemented in terms of L, and hence calls C at each run.
316             If you plan to recall several times, consider using L instead.
317              
318             =cut
319              
320             sub recall;
321              
322             BEGIN {
323             my $safe_wrap = sub {
324 20         34 my $name = shift;
325              
326 20         20 my ($wrap, $err);
327             {
328 20         22 local $@;
  20         21  
329 20         27 $wrap = eval { wrap $name };
  20         38  
330 20         129 $err = $@;
331             }
332              
333 20         53 $wrap, $err;
334 5     5   27 };
335              
336 5 50       37 if ("$]" == 5.008) {
337             # goto tends to crash a lot on perl 5.8.0
338             *recall = sub {
339 0         0 my ($wrap, $err) = $safe_wrap->(shift);
340 0 0       0 croak _clean_msg $err if $err;
341 0         0 $wrap->(@_)
342             }
343 0         0 } else {
344             *recall = sub {
345 20     20   20092 my ($wrap, $err) = $safe_wrap->(shift);
346 20 100       57 croak _clean_msg $err if $err;
347 13         376 goto $wrap;
348             }
349 5         132 }
350             }
351              
352             =head1 EXPORT
353              
354             The functions L, L and L are only exported on request, either by providing their name or by the C<':funcs'> and C<':all'> tags.
355              
356             =cut
357              
358 5     5   35 use base qw;
  5         9  
  5         530  
359              
360 5     5   32 use vars qw<@EXPORT @EXPORT_OK %EXPORT_TAGS>;
  5         8  
  5         659  
361              
362             @EXPORT = ();
363             %EXPORT_TAGS = (
364             'funcs' => [ qw ]
365             );
366             @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
367             $EXPORT_TAGS{'all'} = [ @EXPORT_OK ];
368              
369             =head1 DEPENDENCIES
370              
371             L, L (core modules since perl 5), L (since 5.7.3).
372              
373             =head1 AUTHOR
374              
375             Vincent Pit, C<< >>, L.
376              
377             You can contact me by mail or on C (vincent).
378              
379             =head1 BUGS
380              
381             Please report any bugs or feature requests to C, or through the web interface at L.
382             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
383              
384             =head1 SUPPORT
385              
386             You can find documentation for this module with the perldoc command.
387              
388             perldoc Sub::Prototype::Util
389              
390             Tests code coverage report is available at L.
391              
392             =head1 COPYRIGHT & LICENSE
393              
394             Copyright 2008,2009,2010,2011,2013 Vincent Pit, all rights reserved.
395              
396             This program is free software; you can redistribute it and/or modify it
397             under the same terms as Perl itself.
398              
399             =cut
400              
401             1; # End of Sub::Prototype::Util