File Coverage

blib/lib/with.pm
Criterion Covered Total %
statement 135 150 90.0
branch 49 64 76.5
condition 18 24 75.0
subroutine 22 22 100.0
pod 0 4 0.0
total 224 264 84.8


line stmt bran cond sub pod time code
1             package with;
2              
3 6     6   463074 use 5.009_004;
  6         24  
4              
5 6     6   35 use strict;
  6         15  
  6         150  
6 6     6   32 use warnings;
  6         15  
  6         192  
7              
8 6     6   32 use Carp qw;
  6         11  
  6         419  
9 6     6   3694 use Filter::Util::Call;
  6         5906  
  6         413  
10 6     6   4008 use Text::Balanced qw;
  6         129871  
  6         809  
11 6     6   69 use Scalar::Util qw;
  6         13  
  6         413  
12              
13 6     6   3807 use Sub::Prototype::Util qw;
  6         15094  
  6         3981  
14              
15             =head1 NAME
16              
17             with - Lexically call methods with a default object.
18              
19             =head1 VERSION
20              
21             Version 0.03
22              
23             =cut
24              
25             our $VERSION = '0.03';
26              
27             =head1 WARNING
28              
29             This module was an early experiment which turned out to be completely unpractical.
30             Therefore its use is officially B.
31             Please don't use it, and don't hesitate to contact me if you want to reuse the namespace.
32              
33             =head1 SYNOPSIS
34              
35             package Deuce;
36              
37             sub new { my $class = shift; bless { id = > shift }, $class }
38              
39             sub hlagh { my $self = shift; print "Deuce::hlagh $self->{id}\n" }
40              
41              
42             package Pants;
43              
44             sub hlagh { print "Pants::hlagh\n" }
45              
46             our @ISA;
47             push @ISA, 'Deuce';
48             my $deuce = new Deuce 1;
49              
50             hlagh; # Pants::hlagh
51              
52             {
53             use with \$deuce;
54             hlagh; # Deuce::hlagh 1
55             Pants::hlagh; # Pants::hlagh
56              
57             {
58             use with \Deuce->new(2);
59             hlagh; # Deuce::hlagh 2
60             }
61              
62             hlagh; # Deuce::hlagh 1
63              
64             no with;
65             hlagh; # Pants::hlagh
66             }
67              
68             hlagh; # Pants::hlagh
69              
70             =head1 DESCRIPTION
71              
72             This pragma lets you define a default object against with methods will be called in the current scope when possible.
73             It is enabled by the C idiom (note that you must pass a reference to the object).
74             If you C several times in the current scope, the default object will be the last specified one.
75              
76             =cut
77              
78             my $EOP = qr/\n+|\Z/;
79             my $CUT = qr/\n=cut.*$EOP/;
80             my $pod_or_DATA = qr/
81             ^=(?:head[1-4]|item) .*? $CUT
82             | ^=pod .*? $CUT
83             | ^=for .*? $EOP
84             | ^=begin \s* (\S+) .*? \n=end \s* \1 .*? $EOP
85             | ^__(DATA|END)__\r?\n.*
86             /smx;
87              
88             my $extractor = [
89             { 'with::COMMENT' => qr/(?
90             { 'with::PODDATA' => $pod_or_DATA },
91             { 'with::QUOTELIKE' => sub {
92             extract_quotelike $_[0], qr/(?=(?:['"]|\bq.|<<\w+))/
93             } },
94             { 'with::VARIABLE' => sub {
95             extract_variable $_[0], qr/(?=\\?[\$\@\%\&\*])/
96             } },
97             { 'with::HASHKEY' => qr/\w+\s*=>/ },
98             { 'with::QUALIFIED' => qr/\w+(?:::\w+)+(?:::)?/ },
99             { 'with::SUB' => qr/sub\s+\w+(?:::\w+)*/ },
100             { 'with::FILEHANDLE' => qr/<[\$\*]?[^\W>]*>/ },
101             { 'with::USE' => qr/(?:use|no)\s+\S+/ },
102             ];
103              
104             my %skip;
105             $skip{$_} = 1 for qw
106             if else elsif unless given when or and
107             while until for foreach next redo last continue
108             eq ne lt gt le ge cmp
109             map grep system exec sort print say
110             new
111             STDIN STDOUT STDERR>;
112              
113             my @core = qw
114             chomp chop chown chr chroot close closedir connect cos crypt
115             dbmclose dbmopen defined delete die do dump each endgrent
116             endhostent endnetent endprotoent endpwent endservent eof eval
117             exec exists exit exp fcntl fileno flock fork format formline
118             getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname
119             gethostent getlogin getnetbyaddr getnetbyname getnetent
120             getpeername getpgrp getppid getpriority getprotobyname
121             getprotobynumber getprotoent getpwent getpwnam getpwuid
122             getservbyname getservbyport getservent getsockname getsockopt
123             glob gmtime goto grep hex index int ioctl join keys kill last lc
124             lcfirst length link listen local localtime lock log lstat map
125             mkdir msgctl msgget msgrcv msgsnd my next no oct open opendir
126             ord our pack package pipe pop pos print printf prototype push
127             quotemeta rand read readdir readline readlink recv redo ref
128             rename require reset return reverse rewinddir rindex rmdir
129             scalar seek seekdir select semctl semget semop send setgrent
130             sethostent setnetent setpgrp setpriority setprotoent setpwent
131             setservent setsockopt shift shmctl shmget shmread shmwrite
132             shutdown sin sleep socket socketpair sort splice split sprintf
133             sqrt srand stat study sub substr symlink syscall sysopen sysread
134             sysseek system syswrite tell telldir tie tied time times
135             truncate uc ucfirst umask undef unlink unpack unshift untie use
136             utime values vec wait waitpid wantarray warn write>;
137              
138             my %core;
139             $core{$_} = prototype "CORE::$_" for @core;
140             undef @core;
141             # Fake prototypes
142             $core{'not'} = '$';
143             $core{'defined'} = '_';
144             $core{'undef'} = ';\[$@%&*]';
145              
146             my %hints;
147              
148             sub code {
149 6     6   61 no strict 'refs';
  6         18  
  6         2925  
150 66 50   66 0 191 my $name = @_ > 1 ? join '::', @_
151             : $_[0];
152 66         112 return *{$name}{CODE};
  66         493  
153             }
154              
155             sub corewrap {
156 14     14 0 48 my ($name, $par) = @_;
157 14 50       38 return '' unless $name;
158 14         32 my $wrap = 'with::core::' . $name;
159 14 50       42 if (not code $wrap) {
160 14         37 my $proto = $core{$name};
161 14         67 my $func = wrap { 'CORE::' . $name => $proto }, compile => 1;
162             my $code = set_prototype sub {
163 13     13   6915 my ($caller, $H) = (caller 0)[0, 10];
164 13   50     83 my $id = ($H || {})->{with};
165 13         30 my $obj;
166             # Try method call.
167 13 50 33     118 if ($id and $obj = $hints{$id}) {
168 13 100       140 if (my $meth = $$obj->can($name)) {
169 1 50       7 @_ = flatten $proto, @_ if defined $proto;
170 1         33 unshift @_, $$obj;
171 1         7 goto &$meth;
172             }
173             }
174             # Try function call in caller namescape.
175 12         52 my $qname = $caller . '::' . $name;
176 12 100       39 if (code $qname) {
177 1 50       7 @_ = flatten $proto, @_ if defined $proto;
178 1         59 goto &$qname;
179             }
180             # Try core function call.
181 11         35 my @ret = eval { $func->(@_) };
  11         394  
182 11 50       226 if ($@) {
183             # Produce a correct error in regard of the caller.
184 0         0 my $msg = $@;
185 0         0 $msg =~ s/(called)\s+at.*/$1/s;
186 0         0 croak $msg;
187             }
188 11 100       77 return wantarray ? @ret : $ret[0];
189 14         2912 }, $proto;
190             {
191 6     6   118 no strict 'refs';
  6         27  
  6         1757  
  14         29  
192 14         46 *$wrap = $code;
193             }
194             }
195 14         96 return $wrap . ' ' . $par;
196             }
197              
198             sub subwrap {
199 47     47 0 178 my ($name, $par, $proto) = @_;
200 47 50       158 return '' unless $name;
201 47 100       286 return "with::defer $par'$name'," unless defined $proto;
202 14         46 my $wrap = 'with::sub::' . $name;
203 14 100       29 if (not code $wrap) {
204             my $code = set_prototype sub {
205 14     14   1840 my ($caller, $H) = (caller 0)[0, 10];
206 14   100     99 my $id = ($H || {})->{with};
207 14         28 my $obj;
208             # Try method call.
209 14 100 66     91 if ($id and $obj = $hints{$id}) {
210 13 50       82 if (my $meth = $$obj->can($name)) {
211 0         0 @_ = flatten $proto, @_;
212 0         0 unshift @_, $$obj;
213 0         0 goto &$meth;
214             }
215             }
216             # Try function call in caller namescape.
217 14         47 my $qname = $caller . '::' . $name;
218 14 50       43 goto &$qname if code $qname;
219             # This call won't succeed, but it'll throw an exception we should propagate.
220 6     6   51 eval { no strict 'refs'; $qname->(@_) };
  6         16  
  6         859  
  0         0  
  0         0  
221 0 0       0 if ($@) {
222             # Produce a correct 'Undefined subroutine' error in regard of the caller.
223 0         0 my $msg = $@;
224 0         0 $msg =~ s/(called)\s+at.*/$1/s;
225 0         0 croak $msg;
226             }
227 0         0 croak "$qname didn't exist and yet the call succeeded\n";
228 4         51 }, $proto;
229             {
230 6     6   44 no strict 'refs';
  6         15  
  6         1265  
  4         12  
231 4         19 *$wrap = $code;
232             }
233             }
234 14         76 return $wrap . ' '. $par;
235             }
236              
237             sub defer {
238 30     30 0 26303 my $name = shift;
239 30         254 my ($caller, $H) = (caller 0)[0, 10];
240 30   100     193 my $id = ($H || {})->{with};
241 30         77 my $obj;
242             # Try method call.
243 30 100 66     190 if ($id and $obj = $hints{$id}) {
244 27 100       172 if (my $meth = $$obj->can($name)) {
245 18         54 unshift @_, $$obj;
246 18         93 goto &$meth;
247             }
248             }
249             # Try function call in caller namescape.
250 12         42 $name = $caller . '::' . $name;
251 12 100       43 goto &$name if code $name;
252             # This call won't succeed, but it'll throw an exception we should propagate.
253 6     6   42 eval { no strict 'refs'; $name->(@_) };
  6         14  
  6         5379  
  2         8  
  2         44  
254 2 50       13 if ($@) {
255             # Produce a correct 'Undefined subroutine' error in regard of the caller.
256 2         7 my $msg = $@;
257 2         23 $msg =~ s/(called)\s+at.*/$1/s;
258 2         362 croak $msg;
259             }
260 0         0 croak "$name didn't exist and yet the call succeeded\n";
261             }
262              
263             sub import {
264 7 100 66 7   223 return unless defined $_[1] and ref $_[1];
265 6         77 my $caller = (caller 0)[0];
266 6         50 my $id = refaddr $_[1];
267 6         66 $hints{$^H{with} = $id} = $_[1];
268             filter_add sub {
269 24     24   268 my ($status, $lastline);
270 24         69 my ($data, $count) = ('', 0);
271 24         162 while ($status = filter_read) {
272 143 50       336 return $status if $status < 0;
273 143 100 100     640 return $status unless defined $^H{with} && $^H{with} == $id;
274 131 100 100     649 if (/^__(?:DATA)__\r?$/ || /\b(?:use|no)\s+with\b/) {
275 4         11 $lastline = $_;
276 4         8 last;
277             }
278 127         299 $data .= $_;
279 127         202 ++$count;
280 127         500 $_ = '';
281             }
282 12 100       6421 return $count if not $count;
283 8         20 my $instr;
284             my @components;
285 8         52 for (extract_multiple($data, $extractor)) {
286 340 100       2749 if (ref) { push @components, $_; $instr = 0 }
  166 50       315  
  166         272  
287 0         0 elsif ($instr) { $components[-1] .= $_ }
288 174         363 else { push @components, $_; $instr = 1 }
  174         291  
289             }
290 8         44 my $i = 0;
291             $_ = join '',
292 8 100       35 map { (ref) ? $; . pack('N', $i++) . $; : $_ }
  340         1089  
293             @components;
294 8         119 @components = grep ref, @components;
295 8         131 s/
296             \b &? ([^\W\d]\w+) \s* (?!=>) (\(?)
297             /
298             $skip{$1} ? "$1 $2"
299 91 100       605 : exists $core{$1} ? corewrap $1, $2
    100          
300             : subwrap $1, $2, prototype($caller.'::'.$1)
301             /sexg;
302 8         203 s/\Q$;\E([\x00-\xff]{4})\Q$;\E/${$components[unpack('N',$1)]}/g;
  166         684  
303 8 100       48 $_ .= $lastline if defined $lastline;
304 8         3905 return $count;
305             }
306 6         79 }
307              
308             sub unimport {
309 1     1   18 $^H{with} = undef;
310 1         11 filter_del;
311             }
312              
313             =head1 HOW DOES IT WORK
314              
315             The main problem to address is that lexical scoping and source modification can only occur at compile time, while object creation and method resolution happen at run-time.
316              
317             The C statement stores an address to the variable C<$obj> in the C field of the hints hash C<%^H>.
318             It also starts a source filter that replaces function calls with calls to C, passing the name of the original function as the first argument.
319             When the replaced function has a prototype or is part of the core, the call is deferred to a corresponding wrapper generated in the C namespace.
320             Some keywords that couldn't possibly be replaced are also completely skipped.
321             C undefines the hint and deletes the source filter, stopping any subsequent modification in the current scope.
322              
323             When the script is executed, deferred calls first fetch the default object back from the address stored into the hint.
324             If the object C<< ->can >> the original function name, a method call is issued.
325             If not, the calling namespace is inspected for a subroutine with the proper name, and if it's present the program Cs into it.
326             If that fails too, the core function with the same name is recalled if possible, or an "Undefined subroutine" error is thrown.
327              
328             =head1 IGNORED KEYWORDS
329              
330             A call will never be dispatched to a method whose name is one of :
331              
332             my our local sub do eval goto return
333             if else elsif unless given when or and
334             while until for foreach next redo last continue
335             eq ne lt gt le ge cmp
336             map grep system exec sort print say
337             new
338             STDIN STDOUT STDERR
339              
340             =head1 EXPORT
341              
342             No function or constant is exported by this pragma.
343              
344             =head1 CAVEATS
345              
346             Most likely slow.
347             Almost surely non thread-safe.
348             Contains source filters, hence brittle.
349             Messes with the dreadful prototypes.
350             Crazy.
351             Will have bugs.
352              
353             Don't put anything on the same line of C or C.
354              
355             When there's a function in the caller namespace that has a core function name, and when no method with the same name is present, the ambiguity is resolved in favor of the caller namespace.
356             That's different from the usual perl semantics where C gets resolved to CORE::push.
357              
358             If a method has the same name as a prototyped function in the caller namespace, and if a called is deferred to the method, it will have its arguments passed by value.
359              
360             =head1 DEPENDENCIES
361              
362             L 5.9.4.
363              
364             L (core module since perl 5).
365              
366             L, L and L (core since 5.7.3).
367              
368             L 0.08.
369              
370             =head1 AUTHOR
371              
372             Vincent Pit, C<< >>, L.
373              
374             You can contact me by mail or on C (vincent).
375              
376             =head1 BUGS
377              
378             Please report any bugs or feature requests to C, or through the web interface at L.
379             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
380              
381             =head1 SUPPORT
382              
383             You can find documentation for this module with the perldoc command.
384              
385             perldoc with
386              
387             =head1 ACKNOWLEDGEMENTS
388              
389             A fair part of this module is widely inspired from L (especially C), but a complete integration was needed in order to add hints support and more placeholder patterns.
390              
391             =head1 COPYRIGHT & LICENSE
392              
393             Copyright 2008,2017 Vincent Pit, all rights reserved.
394              
395             This program is free software; you can redistribute it and/or modify it
396             under the same terms as Perl itself.
397              
398             =cut
399              
400             1; # End of with