File Coverage

blib/lib/PPIx/Regexp/Util.pm
Criterion Covered Total %
statement 66 78 84.6
branch 19 38 50.0
condition 12 18 66.6
subroutine 17 18 94.4
pod 3 3 100.0
total 117 155 75.4


line stmt bran cond sub pod time code
1             package PPIx::Regexp::Util;
2              
3 9     9   161269 use 5.006;
  9         26  
4              
5 9     9   43 use strict;
  9         10  
  9         162  
6 9     9   26 use warnings;
  9         12  
  9         392  
7              
8 9     9   41 use Carp;
  9         10  
  9         565  
9 9         1218 use PPIx::Regexp::Constant qw{
10             INFINITY
11             MINIMUM_PERL
12             @CARP_NOT
13 9     9   3260 };
  9         19  
14 9     9   37 use Scalar::Util qw{ blessed };
  9         9  
  9         301  
15              
16 9     9   33 use base qw{ Exporter };
  9         10  
  9         4866  
17              
18             our @EXPORT_OK = qw{
19             is_ppi_regexp_element
20             __choose_tokenizer_class
21             __instance
22             __is_ppi_regexp_element
23             __merge_perl_requirements
24             __ns_can
25             __post_rebless_error
26             raw_width
27             __to_ordinal_en
28             width
29             };
30              
31             our %EXPORT_TAGS = (
32             all => \@EXPORT_OK,
33             width_one => [ qw{ raw_width width } ],
34             );
35              
36             our $VERSION = '0.091';
37              
38             sub is_ppi_regexp_element {
39 11     11 1 24 my ( $elem ) = @_;
40 11 50       17 __instance( $elem, 'PPI::Element' )
41             or return;
42 11   66     51 return $elem->isa( 'PPI::Token::Regexp' ) ||
43             $elem->isa( 'PPI::Token::QuoteLike::Regexp' );
44             }
45              
46             sub __is_ppi_regexp_element {
47 0     0   0 Carp::cluck(
48             '__is_ppi_regexp_element is deprecated. Use is_ppi_regexp_element'
49             );
50 0         0 goto &is_ppi_regexp_element;
51             }
52              
53             # TODO ditch this once the deprecation period ends
54             sub __choose_tokenizer_class {
55             # my ( $content, $arg ) = @_;
56 541     541   1186 my ( undef, $arg ) = @_;
57 541 50       1571 if ( defined $arg->{parse} ) {
58 0         0 my $warning = q<The 'parse' argument is deprecated.>;
59             { guess => 1, string => 1 }->{$arg->{parse}}
60 0 0       0 and $warning = join ' ', $warning,
61             q<You should use PPIx::QuoteLike on quotish things>;
62 0         0 croak $warning;
63             }
64 541         1174 return 'PPIx::Regexp::Tokenizer';
65             }
66              
67             sub __instance {
68 56133     56133   74039 my ( $object, $class ) = @_;
69 56133 100       112433 blessed( $object ) or return;
70 23857         65260 return $object->isa( $class );
71             }
72              
73             sub __merge_perl_requirements { ## no critic (RequireArgUnpacking)
74             my @work =
75 113 50       171 sort { $a->[0] <=> $b->[0] || $b->[1] <=> $a->[1] }
76 30         85 map { ( [ $_->[0], 1 ], [ $_->[1], 0 ] ) }
77 9 100   9   15 map { [ $_->{introduced}, defined $_->{removed} ? $_->{removed} : INFINITY ] } @_;
  30         71  
78 9         18 my @rslt;
79 9         15 while ( @work ) {
80 11         13 my ( $intro, $rem );
81 11   66     69 $intro = ( shift @work )->[0] while @work && $work[0][1];
82 11 50       17 if ( @work ) {
83 11         14 $rem = $work[0][0];
84 11   100     51 shift @work while @work && ! $work[0][1];
85             }
86 11 50       14 defined $intro
87             or $intro = MINIMUM_PERL;
88 11 50       15 defined $rem
89             or $rem = INFINITY;
90 11 50       33 $intro != $rem
91             and push @rslt, {
92             introduced => $intro,
93             removed => $rem,
94             };
95             }
96             @rslt
97             and $rslt[-1]{removed} == INFINITY
98 9 50 33     30 and delete $rslt[-1]{removed};
99 9         32 return @rslt;
100             }
101              
102             sub __ns_can {
103 252     252   351 my ( $class, $name ) = @_;
104 252   33     676 my $fqn = join '::', ref $class || $class, $name;
105 9     9   49 no strict qw{ refs };
  9         12  
  9         3016  
106 252 50       1157 return defined &$fqn ? \&$fqn : undef;
107             }
108              
109             sub __post_rebless_error {
110 8     8   23 my ( $self, %arg ) = @_;
111 8         16 my $rslt = 0;
112 8 50       26 unless ( defined( $self->{error} = $arg{error} ) ) {
113 0         0 my $class = ref $self;
114 0         0 Carp::cluck( "Making $class with no error message" );
115 0         0 $self->{error} = 'Unspecified error';
116 0         0 $rslt++;
117             }
118             $self->{explanation} = defined $arg{explanation} ?
119             $arg{explanation} :
120 8 50       26 $arg{error};
121 8         19 return $rslt;
122              
123             }
124              
125             # Unquantified number of characters matched.
126             sub raw_width {
127 527     527 1 824 return ( 1, 1 );
128             }
129              
130             sub __to_ordinal_en {
131 1     1   2 my ( $num ) = @_;
132 1         2 $num += 0;
133 1 50       6 1 == int( ( $num % 100 ) / 10 ) # teens
134             and return "${num}th";
135 1 50       5 1 == $num % 10
136             and return "${num}st";
137 0 0       0 2 == $num % 10
138             and return "${num}nd";
139 0 0       0 3 == $num % 10
140             and return "${num}rd";
141 0         0 return "${num}th";
142             }
143              
144             sub width {
145 727     727 1 1081 my ( $self ) = @_;
146 727         1264 my @raw_width = $self->raw_width();
147 727         893 my ( $code, $next_sib );
148 727 100 100     1462 $next_sib = $self->snext_sibling()
149             and $code = $next_sib->can( '__quantified_width' )
150             or return @raw_width;
151 91         258 return $code->( $next_sib, @raw_width );
152             }
153              
154             1;
155              
156             __END__
157              
158             =head1 NAME
159              
160             PPIx::Regexp::Util - Utility functions for PPIx::Regexp;
161              
162             =head1 SYNOPSIS
163              
164             use PPIx::Regexp::Util qw{ __instance };
165             .
166             .
167             .
168             __instance( $foo, 'Bar' )
169             or die '$foo is not a Bar';
170              
171             =head1 DESCRIPTION
172              
173             This module contains utility functions for L<PPIx::Regexp|PPIx::Regexp>
174             which it is convenient to centralize.
175              
176             Double-underscore subroutines are B<private> to the C<PPIx-Regexp>
177             package. Their documentation is provided for the author's convenience
178             only, and they are subject to change without notice. I<Caveat user.>
179              
180             This module exports nothing by default.
181              
182             =head1 SUBROUTINES
183              
184             This module can export the following subroutines:
185              
186             =head2 is_ppi_regexp_element
187              
188             is_ppi_regexp_element( $elem )
189             and print "$elem is a regexp of some sort\n";
190              
191             This subroutine is public and supported.
192              
193             This subroutine takes as its argument a L<PPI::Element|PPI::Element>. It
194             returns a true value if the argument represents a regular expression of
195             some sort, and a false value otherwise.
196              
197             =head2 __instance
198              
199             __instance( $foo, 'Bar' )
200             and print '$foo isa Bar', "\n";
201              
202             This subroutine is B<private> to the C<PPIx-Regexp> package.
203              
204             This subroutine returns true if its first argument is an instance of the
205             class specified by its second argument. Unlike C<UNIVERSAL::isa>, the
206             result is always false unless the first argument is a reference.
207              
208             =head2 __is_ppi_regexp_element
209              
210             __is_ppi_regexp_element( $elem )
211             and print "$elem is a regexp of some sort\n";
212              
213             This subroutine is B<private> to the C<PPIx-Regexp> package.
214              
215             This is a synonym for L<is_ppi_regexp_element()|/is_ppi_regexp_element>,
216             and is deprecated in favor of it. If called, it will complain via
217             C<Carp::cluck()> and then C<goto &is_ppi_regexp_element>.
218              
219             =head2 __merge_perl_requirements
220              
221             This subroutine is B<private> to the C<PPIx-Regexp> package.
222              
223             This subroutine merges perl requirements as returned by the various
224             C<__perl_requirements()> methods.
225              
226             =head2 __ns_can
227              
228             This subroutine is B<private> to the C<PPIx-Regexp> package.
229              
230             This method is analogous to C<can()>, but returns a reference to the
231             code only if it is actually implemented by the invoking name space.
232              
233             =head2 __post_rebless_error
234              
235             This method is B<private> to the C<PPIx-Regexp> package. The intended
236             use is to alias it to C<__PPIX_ELEM__post_reblessing()>.
237              
238             It takes arguments as name/value pairs. Argument C<{error}> is the error
239             message; if it is omitted you get a warning with stack trace. Argument
240             C<{explanation}> defaults to C<{error}>.
241              
242             It returns the number of errors to add to the parse.
243              
244             =head2 raw_width
245              
246             This public method returns the minimum and maximum width matched by the
247             element before taking into account such details as what the element
248             actually is and how it is quantified.
249              
250             This implementation is appropriate to things that match exactly one
251             character -- i.e. it returns C<( 1, 1 )>.
252              
253             =head2 __to_ordinal_en
254              
255             This subroutine is B<private> to the C<PPIx-Regexp> package.
256              
257             This subroutine takes as its argument an integer and returns a string
258             representing its ordinal in English. For example
259              
260             say __to_ordinal_en( 17 );
261             # 17th
262              
263             =head2 width
264              
265             my ( $min_wid, $max_wid ) = $self->width();
266              
267             This public method (well, mixin) returns the minimum and maximum width
268             of the text matched by the element.
269              
270             Elements which import this method must also implement a C<raw_width()>
271             method which returns the unquantified width of the element.
272              
273             =head1 EXPORT TAGS
274              
275             The following export tags are defined by this module. All are private to
276             the C<PPIx-Regexp> package unless otherwise documented.
277              
278             =head2 all
279              
280             This tag exports everything exportable by this module.
281              
282             =head2 width_one
283              
284             This tag is appropriate to an element which, when unquantified, matches
285             exactly one character. It exports C<raw_width()> and C<width()>.
286              
287             =head1 SEE ALSO
288              
289             L<Params::Util|Params::Util>, which I recommend, but in the case of
290             C<PPIx::Regexp> I did not want to introduce a dependency on an XS module
291             when all I really wanted was the function of that module's
292             C<_INSTANCE()> subroutine.
293              
294             =head1 SUPPORT
295              
296             Support is by the author. Please file bug reports at
297             L<https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp>,
298             L<https://github.com/trwyant/perl-PPIx-Regexp/issues>, or in
299             electronic mail to the author.
300              
301             =head1 AUTHOR
302              
303             Thomas R. Wyant, III F<wyant at cpan dot org>
304              
305             =head1 COPYRIGHT AND LICENSE
306              
307             Copyright (C) 2010-2023, 2025 by Thomas R. Wyant, III
308              
309             This program is free software; you can redistribute it and/or modify it
310             under the same terms as Perl 5.10.0. For more details, see the full text
311             of the licenses in the directory LICENSES.
312              
313             This program is distributed in the hope that it will be useful, but
314             without any warranty; without even the implied warranty of
315             merchantability or fitness for a particular purpose.
316              
317             =cut
318              
319             # ex: set textwidth=72 :