File Coverage

blib/lib/PPIx/Regexp/Token/Literal.pm
Criterion Covered Total %
statement 99 105 94.2
branch 87 100 87.0
condition 24 29 82.7
subroutine 18 19 94.7
pod 5 5 100.0
total 233 258 90.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PPIx::Regexp::Token::Literal - Represent a literal character
4              
5             =head1 SYNOPSIS
6              
7             use PPIx::Regexp::Dumper;
8             PPIx::Regexp::Dumper->new( 'qr{foo}smx' )
9             ->print();
10              
11             =head1 INHERITANCE
12              
13             C<PPIx::Regexp::Token::Literal> is a
14             L<PPIx::Regexp::Token|PPIx::Regexp::Token>.
15              
16             C<PPIx::Regexp::Token::Literal> has no descendants.
17              
18             =head1 DESCRIPTION
19              
20             This class represents a literal character, no matter how specified.
21              
22             =head1 METHODS
23              
24             This class provides the following public methods. Methods not documented
25             here are private, and unsupported in the sense that the author reserves
26             the right to change or remove them without notice.
27              
28             =cut
29              
30             package PPIx::Regexp::Token::Literal;
31              
32 9     9   46 use strict;
  9         15  
  9         285  
33 9     9   33 use warnings;
  9         12  
  9         362  
34              
35 9     9   32 use base qw{ PPIx::Regexp::Token };
  9         15  
  9         790  
36              
37 9         1281 use PPIx::Regexp::Constant qw{
38             COOKIE_CLASS COOKIE_REGEX_SET
39             LITERAL_LEFT_CURLY_ALLOWED
40             LITERAL_LEFT_CURLY_REMOVED_PHASE_1
41             LITERAL_LEFT_CURLY_REMOVED_PHASE_2
42             LITERAL_LEFT_CURLY_REMOVED_PHASE_3
43             MINIMUM_PERL MSG_PROHIBITED_BY_STRICT
44             TOKEN_UNKNOWN
45             @CARP_NOT
46 9     9   42 };
  9         46  
47              
48 9     9   59 use PPIx::Regexp::Util qw{ :width_one };
  9         12  
  9         24264  
49              
50             our $VERSION = '0.091';
51              
52             sub __new {
53 1284     1284   3133 my ( $class, $content, %arg ) = @_;
54              
55 1284 50       3533 my $self = $class->SUPER::__new( $content, %arg )
56             or return;
57              
58             defined $arg{ordinal}
59 1284 100       2622 and $self->{ordinal} = $arg{ordinal};
60              
61 1284         2596 return $self;
62             }
63              
64             # Return true if the token can be quantified, and false otherwise
65             # sub can_be_quantified { return };
66              
67             sub explain {
68 1     1 1 2 return 'Literal character';
69             }
70              
71             =head2 is_matcher
72              
73             This method returns a true value because a literal matches itself.
74              
75             =cut
76              
77 8     8 1 22 sub is_matcher { return 1; }
78              
79             sub perl_version_introduced {
80 258     258 1 7227 my ( $self ) = @_;
81             exists $self->{perl_version_introduced}
82 258 100       689 and return $self->{perl_version_introduced};
83 176         333 my $content = $self->content();
84 176         444 my $main = $self->main_structure();
85             $main
86             and $content =~ m/ \A \\ N \{ /smx
87             and not $main->interpolates()
88 176 100 100     530 and return ( $self->{perl_version_introduced} = '5.029010' );
      66        
89             $content =~ m/ \A \\ o /smx
90 175 100       347 and return ( $self->{perl_version_introduced} = '5.013003' );
91             $content =~ m/ \A \\ N [{] U [+] /smx
92 174 100       274 and return ( $self->{perl_version_introduced} = '5.008' );
93             $content =~ m/ \A \\ x [{] /smx # }
94 173 100       292 and return ( $self->{perl_version_introduced} = '5.006' );
95             $content =~ m/ \A \\ N /smx
96 172 100       270 and return ( $self->{perl_version_introduced} = '5.006001' );
97 171         403 return ( $self->{perl_version_introduced} = MINIMUM_PERL );
98             }
99              
100             {
101             my %removed = (
102             q<{> => sub {
103             my ( $self ) = @_;
104              
105             my $prev;
106              
107             if ( $prev = $self->sprevious_sibling() ) {
108             # When an unescaped left curly follows something else in
109             # the same structure, the logic on whether it is allowed
110             # lives, for better or worse, on the sibling.
111             return $prev->__following_literal_left_curly_disallowed_in();
112             } elsif ( $prev = $self->sprevious_element() ) {
113             # Perl 5.27.8 deprecated unescaped literal left curlys
114             # after a left paren that introduces a group. Therefore
115             # the left curly has no previous sibling. But the curly
116             # is still legal at the beginning of a regex, even one
117             # delimited by parens, so we can not return when we find
118             # a PPIx::Regexp::Token::Delimiter, which is a subclass
119             # of PPIx::Regexp::Token::Structure.
120             $prev->isa( 'PPIx::Regexp::Token::Structure' )
121             and q<(> eq $prev->content()
122             and not $prev->isa( 'PPIx::Regexp::Token::Delimiter' )
123             and return LITERAL_LEFT_CURLY_REMOVED_PHASE_3;
124             }
125             # When this mess started, the idea was to always allow
126             # unescaped literal left curlies that started a regex or a
127             # group
128             return LITERAL_LEFT_CURLY_ALLOWED;
129             },
130             );
131              
132             sub perl_version_removed {
133 227     227 1 15170 my ( $self ) = @_;
134             exists $self->{perl_version_removed}
135 227 100       593 and return $self->{perl_version_removed};
136 145         162 my $code;
137             return ( $self->{perl_version_removed} =
138 145 100       277 ( $code = $removed{$self->content()} ) ?
139             scalar $code->( $self ) : undef
140             );
141             }
142             }
143              
144             # Some characters may or may not be literals depending on whether we are
145             # inside a character class. The following hash identifies those
146             # characters and says what we should return when outside (index 0) or
147             # inside (index 1) a character class, as judged by the presence of the
148             # relevant cookie.
149             my %double_agent = (
150             '.' => [ undef, 1 ],
151             '*' => [ undef, 1 ],
152             '?' => [ undef, 1 ],
153             '+' => [ undef, 1 ],
154             '-' => [ 1, undef ],
155             '|' => [ undef, 1 ],
156             );
157              
158             # These are the characters that other external tokenizers need to see,
159             # or at least that we need to take a closer look at. All others can be
160             # unconditionally made into single-character literals.
161             my %extra_ordinary = map { $_ => 1 }
162             split qr{}smx, '$@*+?.\\(){}[]^|-#';
163             # $ -> Token::Interpolation, Token::Assertion
164             # @ -> Token::Interpolation
165             # * -> Token::Quantifier
166             # + ? -> Token::Quantifier, Token::Greediness
167             # . -> Token::CharClass::Simple
168             # \ -> Token::Control, Token::CharClass::Simple, Token::Assertion,
169             # Token::Backreference
170             # ( ) { } [ ] -> Token::Structure
171             # ^ -> Token::Assertion
172             # | - -> Token::Operator
173              
174             my %regex_set_operator = map { $_ => 1 } qw{ & + | - ^ ! };
175              
176             # The regex for the extended white space available under regex sets in
177             # Perl 5.17.8 and in general in perl 5.17.9. I have been unable to get
178             # this to work under Perl 5.6.2, so for that we fall back to ASCII white
179             # space. The stringy eval is because I have been unable to get
180             # satisfaction out of either interpolated characters (in general) or
181             # eval-ed "\N{U+...}" (under 5.6.2) or \x{...} (ditto).
182             #
183             # See PPIx::Regexp::Structure::RegexSet for the documentation of this
184             # mess.
185             # my $white_space_re = $] >= 5.008 ?
186             # 'qr< \\A [\\s\\N{U+0085}\\N{U+200E}\\N{U+200F}\\N{U+2028}\\N{U+2029}]+ >smx' :
187             # 'qr< \\A \\s+ >smx';
188             #
189             # RT #91798
190             # The above turns out to be wrong, because \s matches too many
191             # characters. We need the following to get the right match. Note that
192             # \cK was added experimentally in 5.17.0 and made it into 5.18. The \N{}
193             # characters were NOT added (as I originally thought) but were simply
194             # made characters that generated warnings when escaped, in preparation
195             # for adding them. When they actually get added, I will have to add back
196             # the trinary operator. Sigh.
197             # my $white_space_re = 'qr< \A [\t\n\cK\f\r ] >smx';
198             #
199             # The extended white space characters came back in Perl 5.21.1.
200             my $white_space_re = $] >= 5.008 ?
201             'qr< \\A [\\t\\n\\cK\\f\\r \\N{U+0085}\\N{U+200E}\\N{U+200F}\\N{U+2028}\\N{U+2029}]+ >smx' :
202             'qr< \\A [\\t\\n\\cK\\f\\r ]+ >smx';
203             $white_space_re = eval $white_space_re; ## no critic (ProhibitStringyEval)
204              
205             my %regex_pass_on = map { $_ => 1 } ( qw{ [ ] ( ) $ }, "\\" );
206              
207             sub __PPIX_TOKENIZER__regexp {
208 2835     2835   4596 my ( undef, $tokenizer, $character ) = @_; # Invocant, $char_type unused
209              
210 2835 100       5481 if ( $tokenizer->cookie( COOKIE_REGEX_SET ) ) {
211             # If we're inside a regex set no literals are allowed, but not
212             # all characters that get here are seen as literals.
213              
214 105 100       197 $regex_set_operator{$character}
215             and return $tokenizer->make_token(
216             length $character, 'PPIx::Regexp::Token::Operator' );
217              
218 92         97 my $accept;
219              
220             # As of 5.23.4, only space and horizontal tab are legal white
221             # space inside a bracketed class inside an extended character
222             # class
223 92 100       143 $accept = $tokenizer->find_regexp(
    100          
224             $tokenizer->cookie( COOKIE_CLASS ) ?
225             qr{ \A [ \t] }smx :
226             $white_space_re
227             )
228             and return $tokenizer->make_token(
229             $accept, 'PPIx::Regexp::Token::Whitespace' );
230              
231 59 50       140 $accept = _escaped( $tokenizer, $character )
232             and return $accept;
233              
234 59 100       157 $regex_pass_on{$character}
235             and return;
236              
237             # At this point we have a single character which is poised to be
238             # interpreted as a literal. These are not legal in a regex set
239             # except when also in a bracketed class.
240 8 50       18 return $tokenizer->cookie( COOKIE_CLASS ) ?
241             length $character :
242             $tokenizer->make_token(
243             length $character, TOKEN_UNKNOWN, {
244             error => 'Literal not valid in Regex set',
245             },
246             );
247              
248             } else {
249              
250             # Otherwise handle the characters that may or may not be
251             # literals depending on whether or not we are in a character
252             # class.
253 2730 100       5843 if ( my $class = $double_agent{$character} ) {
254 178 100       480 my $inx = $tokenizer->cookie( COOKIE_CLASS ) ? 1 : 0;
255 178         469 return $class->[$inx];
256             }
257             }
258              
259             # If /x is in effect _and_ we are not inside a character class, \s
260             # is whitespace, and '#' introduces a comment. Otherwise they are
261             # both literals.
262             # NOTE that the mode check is necessary for this section of code
263             # because we call this code in both 'regexp' and 'repl' mode.
264 2552         5180 my $heed_x = $tokenizer->get_mode() eq 'regexp';
265 2552 100 100     6690 if ( $heed_x && $tokenizer->modifier( 'x*' ) &&
    100 100        
      100        
      66        
266             ! $tokenizer->cookie( COOKIE_CLASS ) ) {
267             # We are parsing a regex (not a replacement) AND
268             # We have at least 1 /x modifier AND
269             # We are NOT inside a character class
270 269         350 my $accept;
271 269 100       469 $accept = $tokenizer->find_regexp( $white_space_re )
272             and return $tokenizer->make_token(
273             $accept, 'PPIx::Regexp::Token::Whitespace' );
274 172 100       447 $accept = $tokenizer->find_regexp(
275             qr{ \A \# [^\n]* (?: \n | \z) }smx )
276             and return $tokenizer->make_token(
277             $accept, 'PPIx::Regexp::Token::Comment' );
278             } elsif ( $heed_x && $tokenizer->modifier( 'xx' ) &&
279             $tokenizer->cookie( COOKIE_CLASS ) ) {
280             # We are parsing a regex (not a replacement) AND
281             # We have exactly two /x modifiers AND
282             # We are inside a character class
283 10         26 my $accept;
284 10 100       37 $accept = $tokenizer->find_regexp( qr{ \A [ \t] }smx )
285             and return $tokenizer->make_token(
286             $accept, 'PPIx::Regexp::Token::Whitespace',
287             { perl_version_introduced => '5.025009' },
288             );
289             } else {
290             # Under any other circumstances
291 2273 100 100     8008 ( $character eq '#' || $character =~ m/ \A \s \z /smx )
292             and return 1;
293             }
294              
295 2389         2969 my $accept;
296 2389 100       4567 $accept = _escaped( $tokenizer, $character )
297             and return $accept;
298              
299             # All other characters which are not extra ordinary get accepted.
300 2312 100       5501 $extra_ordinary{$character} or return 1;
301              
302 1233         2465 return;
303             }
304              
305             =begin comment
306              
307             The following is from perlop:
308              
309             The character following "\c" is mapped to some other character by
310             converting letters to upper case and then (on ASCII systems) by
311             inverting the 7th bit (0x40). The most interesting range is from '@' to
312             '_' (0x40 through 0x5F), resulting in a control character from 0x00
313             through 0x1F. A '?' maps to the DEL character. On EBCDIC systems only
314             '@', the letters, '[', '\', ']', '^', '_' and '?' will work, resulting
315             in 0x00 through 0x1F and 0x7F.
316              
317             =end comment
318              
319             =cut
320              
321             # Recognize all the escaped constructions that generate literal
322             # characters in one gigantic regexp. Technically \1.. through \7.. are
323             # octal literals too, but we can not disambiguate these from back
324             # references until we know how many there are. So the lexer gets another
325             # dirty job.
326              
327             {
328             my %special = (
329             '\\N{}' => sub {
330             my ( $tokenizer, $accept ) = @_;
331              
332             =begin comment
333              
334             $tokenizer->strict()
335             or return $tokenizer->make_token( $accept,
336             'PPIx::Regexp::Token::NoOp', {
337             perl_version_removed => '5.027001',
338             },
339             );
340             return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, {
341             error => join( ' ',
342             'Empty Unicode character name',
343             MSG_PROHIBITED_BY_STRICT ),
344             perl_version_introduced => '5.023008',
345             perl_version_removed => '5.027001',
346             },
347             );
348              
349             =end comment
350              
351             =cut
352              
353             return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, {
354             error => 'Empty Unicode character name',
355             perl_version_introduced => '5.023008',
356             perl_version_removed => '5.027001',
357             },
358             );
359             },
360              
361             '\\o{}' => sub {
362             my ( $tokenizer, $accept ) = @_;
363             return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, {
364             error => q<Empty \\o{} is an error>,
365             },
366             );
367             },
368              
369             '\\x{}' => sub {
370             my ( $tokenizer, $accept ) = @_;
371             $tokenizer->strict()
372             and return $tokenizer->make_token( $accept,
373             TOKEN_UNKNOWN, {
374             error =>
375             q<Empty \\x{} is an error under "use re 'strict'">,
376             },
377             );
378             return $accept;
379             },
380             );
381              
382             sub _escaped {
383 2448     2448   3439 my ( $tokenizer, $character ) = @_;
384              
385 2448 100       5202 $character eq '\\'
386             or return;
387              
388 304 100       1138 if ( my $accept = $tokenizer->find_regexp( # {
389             qr< \A \\ ( [ox] ) [{] ( [^}]* ) [}] >smx
390             ) ) {
391 8         19 my $match = $tokenizer->match();
392 8         11 my $code;
393 8 100       28 $code = $special{$match}
394             and return $code->( $tokenizer, $accept );
395 5         13 my ( $kind, $value ) = $tokenizer->capture();
396             my $invalid = {
397             o => qr<[^0-7]>smx,
398             x => qr<[[:^xdigit:]]>smx,
399 5         40 }->{$kind};
400 5 100       125 $value =~ m/ $invalid /smxg # /g for pos()
401             or return $accept;
402             $tokenizer->strict()
403             and return $tokenizer->make_token( $accept,
404             TOKEN_UNKNOWN, {
405             error => sprintf(
406             'Non-%s character in \\%s{...}',
407             {
408             o => 'octal',
409             x => 'hex',
410 2 100       7 }->{$kind},
411             $kind,
412             ),
413             },
414             );
415             return $tokenizer->make_token( $accept, __PACKAGE__, {
416             ordinal => {
417 1     1   5 o => sub { oct $_[0] },
418 0     0   0 x => sub { hex $_[0] },
419 1   50     13 }->{$kind}->( substr( $value, 0, pos $value ) || 0 ),
420             },
421             );
422             }
423              
424 296 100       946 if ( my $accept = $tokenizer->find_regexp(
425             qr< \A \\ (?:
426             [^\w\s] | # delimiters/metas
427             [tnrfae] | # C-style escapes
428             0 [01234567]{0,2} | # octal
429             # [01234567]{1,3} | # made from backref by lexer
430             c [][\@[:alpha:]\\^_?] | # control characters
431             ## x (?: \{ [[:xdigit:]]* \} | [[:xdigit:]]{0,2} ) | # hex
432             ## o [{] [01234567]+ [}] | # octal as of 5.13.3
433             x [[:xdigit:]]{0,2} | # hex - brackets handled above
434             ## N (?: \{ (?: [[:alpha:]] [\w\s:()-]* | # must begin w/ alpha
435             ## U [+] [[:xdigit:]]+ ) \} ) | # unicode
436             N (?: [{] (?= [^0-9] ) [^\}]* [}] ) # unicode
437             ) >smx ) ) {
438 69         160 my $match = $tokenizer->match();
439 69         85 my $code;
440 69 100       169 $code = $special{$match}
441             and return $code->( $tokenizer, $accept );
442 63         241 return $accept;
443             }
444              
445 227         518 return;
446             }
447             }
448              
449             =head2 ordinal
450              
451             print 'The ordinal of ', $token->content(),
452             ' is ', $token->ordinal(), "\n";
453              
454             This method returns the ordinal of the literal if it can figure it out.
455             It is analogous to the C<ord> built-in.
456              
457             It will not attempt to determine the ordinal of a unicode name
458             (C<\N{...}>) unless L<charnames|charnames> has been loaded, and supports
459             the L<vianame()|charnames/charnames::vianame(name)> function.
460             Instead, it will return C<undef>. Users of Perl 5.6.2 and older may be
461             out of luck here.
462              
463             Unicode code points (e.g. C<\N{U+abcd}>) should work independently of
464             L<charnames|charnames>, and just return the value of C<abcd>.
465              
466             It will never attempt to return the ordinal of an octet (C<\C{...}>)
467             because I don't understand the syntax.
468              
469             =cut
470              
471             {
472              
473             my %escapes = (
474             '\\t' => ord "\t",
475             '\\n' => ord "\n",
476             '\\r' => ord "\r",
477             '\\f' => ord "\f",
478             '\\a' => ord "\a",
479             '\\b' => ord "\b",
480             '\\e' => ord "\e",
481             '\\c?' => ord "\c?",
482             '\\c@' => ord "\c@",
483             '\\cA' => ord "\cA",
484             '\\ca' => ord "\cA",
485             '\\cB' => ord "\cB",
486             '\\cb' => ord "\cB",
487             '\\cC' => ord "\cC",
488             '\\cc' => ord "\cC",
489             '\\cD' => ord "\cD",
490             '\\cd' => ord "\cD",
491             '\\cE' => ord "\cE",
492             '\\ce' => ord "\cE",
493             '\\cF' => ord "\cF",
494             '\\cf' => ord "\cF",
495             '\\cG' => ord "\cG",
496             '\\cg' => ord "\cG",
497             '\\cH' => ord "\cH",
498             '\\ch' => ord "\cH",
499             '\\cI' => ord "\cI",
500             '\\ci' => ord "\cI",
501             '\\cJ' => ord "\cJ",
502             '\\cj' => ord "\cJ",
503             '\\cK' => ord "\cK",
504             '\\ck' => ord "\cK",
505             '\\cL' => ord "\cL",
506             '\\cl' => ord "\cL",
507             '\\cM' => ord "\cM",
508             '\\cm' => ord "\cM",
509             '\\cN' => ord "\cN",
510             '\\cn' => ord "\cN",
511             '\\cO' => ord "\cO",
512             '\\co' => ord "\cO",
513             '\\cP' => ord "\cP",
514             '\\cp' => ord "\cP",
515             '\\cQ' => ord "\cQ",
516             '\\cq' => ord "\cQ",
517             '\\cR' => ord "\cR",
518             '\\cr' => ord "\cR",
519             '\\cS' => ord "\cS",
520             '\\cs' => ord "\cS",
521             '\\cT' => ord "\cT",
522             '\\ct' => ord "\cT",
523             '\\cU' => ord "\cU",
524             '\\cu' => ord "\cU",
525             '\\cV' => ord "\cV",
526             '\\cv' => ord "\cV",
527             '\\cW' => ord "\cW",
528             '\\cw' => ord "\cW",
529             '\\cX' => ord "\cX",
530             '\\cx' => ord "\cX",
531             '\\cY' => ord "\cY",
532             '\\cy' => ord "\cY",
533             '\\cZ' => ord "\cZ",
534             '\\cz' => ord "\cZ",
535             '\\c[' => ord "\c[",
536             '\\c\\\\' => ord "\c\\", # " # Get Vim's head straight.
537             '\\c]' => ord "\c]",
538             '\\c^' => ord "\c^",
539             '\\c_' => ord "\c_",
540             );
541              
542             sub ordinal {
543 17     17 1 36 my ( $self ) = @_;
544 17 100       42 exists $self->{ordinal} and return $self->{ordinal};
545 16         30 return ( $self->{ordinal} = $self->_ordinal() );
546             }
547              
548             my %octal = map {; "$_" => 1 } ( 0 .. 7 );
549              
550             sub _ordinal {
551 16     16   23 my ( $self ) = @_;
552 16         40 my $content = $self->content();
553              
554 16 100       75 $content =~ m/ \A \\ /smx or return ord $content;
555              
556 8 100       27 exists $escapes{$content} and return $escapes{$content};
557              
558 6         16 my $indicator = substr $content, 1, 1;
559              
560 6 100       19 $octal{$indicator} and return oct substr $content, 1;
561              
562 5 100       15 if ( $indicator eq 'x' ) {
563 3 100       21 $content =~ m/ \A \\ x \{ ( [[:xdigit:]]* ) /smx
564             and return hex "0$1";
565 1 50       9 $content =~ m/ \A \\ x ( [[:xdigit:]]{0,2} ) \z /smx
566             and return hex $1;
567 0         0 return;
568             }
569              
570 2 50       8 if ( $indicator eq 'o' ) {
571 0 0       0 $content =~ m/ \A \\ o [{] ( [01234567]* ) \z /smx
572             and return oct "0$1";
573 0         0 return; # Shouldn't happen, but ...
574             }
575              
576 2 50       5 if ( $indicator eq 'N' ) {
577 2 100       15 $content =~ m/ \A \\ N \{ U [+] ( [[:xdigit:]]+ ) \} \z /smx
578             and return hex $1;
579 1 50       9 $content =~ m/ \A \\ N [{] ( .+ ) [}] \z /smx
    50          
580             and return (
581             _have_charnames_vianame() ?
582             charnames::vianame( $1 ) :
583             undef
584             );
585 0         0 return; # Shouldn't happen, but ...
586             }
587              
588 0         0 return ord $indicator;
589             }
590              
591             }
592              
593             sub __following_literal_left_curly_disallowed_in {
594 2     2   28 return LITERAL_LEFT_CURLY_REMOVED_PHASE_2;
595             }
596              
597             {
598             my $have_charnames_vianame;
599              
600             sub _have_charnames_vianame {
601 1 50   1   4 defined $have_charnames_vianame
602             and return $have_charnames_vianame;
603             return (
604 1 50       21 $have_charnames_vianame =
605             charnames->can( 'vianame' ) ? 1 : 0
606             );
607              
608             }
609             }
610              
611             sub __perl_requirements_setup {
612 4     4   6 my ( $self ) = @_;
613 4         4 my $prev;
614 4 50 66     8 q<{> eq $self->content() # }
      66        
615             and $prev = $self->sprevious_sibling()
616             and $prev->isa( 'PPIx::Regexp::Token::Literal' )
617             or return $self->SUPER::__perl_requirements_setup();
618             return (
619             {
620 1         7 introduced => MINIMUM_PERL,
621             removed => LITERAL_LEFT_CURLY_REMOVED_PHASE_1,
622             },
623             # TODO the following will be needed if this construction is
624             # re-allowed in 5.26.1:
625             # {
626             # introduced => '5.026001',
627             # removed => '6.027000',
628             # },
629             {
630             introduced => '5.027001',
631             removed => LITERAL_LEFT_CURLY_REMOVED_PHASE_2,
632             },
633             );
634             }
635              
636             *__PPIX_TOKENIZER__repl = \&__PPIX_TOKENIZER__regexp;
637              
638             1;
639              
640             __END__
641              
642             =head1 SUPPORT
643              
644             Support is by the author. Please file bug reports at
645             L<https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp>,
646             L<https://github.com/trwyant/perl-PPIx-Regexp/issues>, or in
647             electronic mail to the author.
648              
649             =head1 AUTHOR
650              
651             Thomas R. Wyant, III F<wyant at cpan dot org>
652              
653             =head1 COPYRIGHT AND LICENSE
654              
655             Copyright (C) 2009-2023, 2025 by Thomas R. Wyant, III
656              
657             This program is free software; you can redistribute it and/or modify it
658             under the same terms as Perl 5.10.0. For more details, see the full text
659             of the licenses in the directory LICENSES.
660              
661             This program is distributed in the hope that it will be useful, but
662             without any warranty; without even the implied warranty of
663             merchantability or fitness for a particular purpose.
664              
665             =cut
666              
667             # ex: set textwidth=72 :