File Coverage

lib/Wanted.pm
Criterion Covered Total %
statement 98 105 93.3
branch 90 102 88.2
condition 30 36 83.3
subroutine 14 14 100.0
pod 7 9 77.7
total 239 266 89.8


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Wanted - ~/lib/Wanted.pm
3             ## Version v0.1.2
4             ## Copyright(c) 2026 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest
6             ## Created 2025/05/16
7             ## Modified 2026/06/14
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Wanted;
15 12     12   352223 use strict;
  12         16  
  12         396  
16 12     12   38 use warnings;
  12         22  
  12         14246  
17             require Exporter;
18             require DynaLoader;
19             our @ISA = qw( Exporter DynaLoader );
20             our @EXPORT = qw( want rreturn lnoreturn );
21             our @EXPORT_OK = qw( context howmany wantref );
22             our $VERSION = 'v0.1.2';
23             our $DEBUG;
24              
25             bootstrap Wanted $VERSION;
26              
27             my %reftype = (
28             ARRAY => 1,
29             HASH => 1,
30             CODE => 1,
31             GLOB => 1,
32             OBJECT => 1,
33             );
34              
35             sub bump_level
36             {
37 316     316 0 309 my( $level ) = @_;
38 316         233 for(;;)
39             {
40 346         1039 my( $p, $r ) = parent_op_name( $level + 1 );
41 346 50       507 if( !defined( $p ) )
42             {
43             # Return undef if parent_op_name fails (outside subroutine)
44 0         0 return;
45             }
46 346 100 66     882 if( $p eq 'return' ||
      66        
47             $p eq '(none)' && $r =~ /^leavesub(lv)?$/ )
48             {
49 30         19 ++$level
50             }
51             else
52             {
53 316         536 return( $level );
54             }
55             }
56             }
57              
58             sub context
59             {
60 11     11 1 3138 my $gimme = wantarray_up(1);
61 11 100       20 return( 'VOID' ) unless( defined( $gimme ) );
62 10         17 my $ref_type = wantref(2);
63 10 100       14 if( $ref_type )
    100          
    50          
    100          
    50          
64             {
65 7 100       16 return( $ref_type eq 'SCALAR' ? 'REFSCALAR' : $ref_type );
66             }
67             # Boolean must come before scalar
68             elsif( want_boolean( bump_level(1) ) )
69             {
70 1         2 return('BOOL');
71             }
72             elsif( !!wantassign(2) )
73             {
74 0         0 return( 'ASSIGN' );
75             }
76             elsif( $gimme )
77             {
78 1         2 return( 'LIST' );
79             }
80             elsif( $gimme == 0 )
81             {
82 1         3 return( 'SCALAR' );
83             }
84             # Should not happen
85             else
86             {
87 0         0 return( '' );
88             }
89             }
90              
91             sub double_return :lvalue;
92              
93             sub howmany ()
94             {
95 18     18 1 690 my $level = bump_level( @_, 1 );
96             # Return undef if bump_level fails
97 18 50       28 return unless( defined( $level ) );
98 18         38 my $count = want_count( $level );
99 18 100       34 return( $count < 0 ? undef : $count );
100             }
101              
102             sub want
103             {
104 209 100 100 209 1 769996 if( @_ == 1 && $_[0] eq 'ASSIGN' )
105             {
106 13         16 @_ = (1);
107 13         24 goto &wantassign;
108             }
109 196         319 want_uplevel( 1, @_ );
110             }
111              
112             sub want_uplevel
113             {
114 196     196 0 294 my( $level, @args ) = @_;
115              
116 196 100       263 if( 1 == @args )
117             {
118 171         1137 @_ = ( 1 + $level );
119 171 100       224 goto &wantref if( $args[0] eq 'REF' );
120 166 100       228 goto &howmany if( $args[0] eq 'COUNT' );
121 151 50       983 goto &wantassign if( $args[0] eq 'ASSIGN' );
122             }
123              
124 176         406 for my $arg ( map split, @args )
125             {
126 200         241 my $is_neg = substr( $arg, 0, 1 ) eq '!';
127 200 100       298 if( substr( $arg, 0, 1 ) eq '!' )
128             {
129 29         31 $is_neg = 1;
130 29         39 $arg = substr( $arg, 1 );
131             }
132 200         258 my $result = _wantone( 2 + $level, $arg );
133             # Return undef if context is invalid
134 200 100       266 return unless( defined( $result ) );
135 197 100 100     1661 return(0) if( ( !$is_neg && !$result ) || ( $is_neg && $result ) );
      100        
      100        
136             }
137 86         228 return(1);
138             }
139              
140             sub wantassign
141             {
142 21     21 1 21 my $uplevel = shift( @_ );
143 21 100       58 return unless( want_lvalue( $uplevel ) );
144 14         15 my $r = want_assign( bump_level( $uplevel ) );
145 14 100       17 if( want('BOOL') )
146             {
147 9   66     29 return( defined( $r ) && $r != 0 );
148             }
149             else
150             {
151 5 100       16 return( $r ? ( want('SCALAR') ? $r->[ $#$r ] : @$r ) : () );
    50          
152             }
153             }
154              
155             sub wantref
156             {
157 226     226 1 277726 my $level = bump_level( @_, 1 );
158             # Return undef if bump_level fails
159 226 50       261 return unless( defined( $level ) );
160 226         472 my $n = parent_op_name( $level );
161 226 50       297 return unless( defined( $n ) );
162 226 100 100     893 if( $n eq 'rv2av' )
    100 66        
    100          
    100          
    100          
    100          
    100          
163             {
164 16         21 return( 'ARRAY' );
165             }
166             elsif( $n eq 'rv2hv' )
167             {
168 18         19 return( 'HASH' );
169             }
170             elsif( $n eq 'rv2cv' || $n eq 'entersub' )
171             {
172 11         14 return( 'CODE' );
173             # Address issue No 47963: want() Confused by Prototypes (Jul 17, 2009)
174             # Not working... Need to modify the XS code.
175             }
176             elsif( $n eq 'rv2gv' || $n eq 'gelem' )
177             {
178 4         5 return( 'GLOB' );
179             }
180             elsif( $n eq 'rv2sv' )
181             {
182 2         4 return( 'SCALAR' );
183             }
184             elsif( $n eq 'method_call' )
185             {
186 3         8 return( 'OBJECT' );
187             }
188             elsif( $n eq 'multideref' )
189             {
190 7 50       12 if( $] >= 5.022000 )
191             {
192 7         24 return( first_multideref_type( $level ) );
193             }
194 0         0 return( '' );
195             }
196             else
197             {
198 165         209 return( '' );
199             }
200             }
201              
202             sub rreturn(@)
203             {
204 15 100   15 1 403437 if( want_lvalue(1) )
205             {
206 3         23 die( "Can't rreturn in lvalue context" );
207             }
208              
209             {
210 12         18 return( double_return( @_ ) );
  12         51  
211             }
212             }
213              
214             sub lnoreturn () : lvalue
215             {
216 12 100 66 12 1 3284 if( !want_lvalue(1) || !want_assign(1) )
217             {
218 3         18 die( "Can't lnoreturn except in ASSIGN context" );
219             }
220              
221 9 50       20 if( $] >= 5.019 )
222             {
223 9         33 return( double_return( disarm_temp( my $undef ) ) );
224             }
225 0         0 return( double_return( disarm_temp( my $undef ) ) );
226             }
227              
228             sub _wantone
229             {
230 200     200   221 my( $uplevel, $arg ) = @_;
231              
232 200         257 my $wantref = wantref( $uplevel + 1 );
233 200 100 66     2737 if( $arg =~ /^\d+$/ )
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
234             {
235 17         39 my $want_count = want_count( $uplevel );
236 17   100     39 return( $want_count == -1 || $want_count >= $arg );
237             }
238             elsif( lc( $arg ) eq 'infinity' )
239             {
240 6         19 return( want_count( $uplevel ) == -1 );
241             }
242             elsif( $arg eq 'REF' )
243             {
244 3         6 return( $wantref );
245             }
246             elsif( $reftype{ $arg } )
247             {
248 12     12   86 no warnings; # If $wantref is undef
  12         31  
  12         570  
249 42         57 return( $wantref eq $arg );
250             }
251             elsif( $arg eq 'REFSCALAR' )
252             {
253 12     12   41 no warnings; # If $wantref is undef
  12         24  
  12         3329  
254 6         30 return( $wantref eq 'SCALAR' );
255             }
256             elsif( $arg eq 'LVALUE' )
257             {
258 19         956 return( want_lvalue( $uplevel ) );
259             }
260             elsif( $arg eq 'RVALUE' )
261             {
262 7         20 return( !want_lvalue( $uplevel ) );
263             }
264             elsif( $arg eq 'VOID' )
265             {
266 4         12 return( !defined( wantarray_up( $uplevel ) ) );
267             }
268             elsif( $arg eq 'SCALAR' )
269             {
270 19         42 my $gimme = wantarray_up( $uplevel );
271             # Return undef if context is invalid
272 19 100       38 return unless( defined( $gimme ) );
273 17         30 return( $gimme == 0 );
274             }
275             elsif( $arg eq 'BOOL' || $arg eq 'BOOLEAN' )
276             {
277 55         75 return( want_boolean( bump_level( $uplevel ) ) );
278             }
279             elsif( $arg eq 'LIST' )
280             {
281 16         31 my $gimme = wantarray_up( $uplevel );
282             # Return undef if context is invalid
283 16 100       29 return unless( defined( $gimme ) );
284 15         21 return( $gimme );
285             }
286             elsif( $arg eq 'COUNT' )
287             {
288 0         0 die( "want: COUNT must be the *only* parameter" );
289             }
290             elsif( $arg eq 'ASSIGN' )
291             {
292 6         10 return( !!wantassign( $uplevel + 1 ) );
293             }
294             else
295             {
296 0           die( "want: Unrecognised specifier $arg" );
297             }
298             }
299              
300             *_wantref = \&wantref;
301              
302             *_wantassign = \&wantassign;
303              
304             1;
305             # NOTE: POD
306             __END__