File Coverage

blib/lib/Babble/Plugin/PostfixDeref.pm
Criterion Covered Total %
statement 36 38 94.7
branch 12 12 100.0
condition 2 6 33.3
subroutine 5 6 83.3
pod 0 2 0.0
total 55 64 85.9


line stmt bran cond sub pod time code
1             package Babble::Plugin::PostfixDeref;
2              
3 1     1   70934 use Moo;
  1         7238  
  1         6  
4 1     1   1746 use Babble::Config;
  1         2  
  1         689  
5              
6             my $term_derefable = q{
7             # Copied from <PerlTerm> rule in PPR::X@0.001002
8             # The remaining alternatives can all take postfix dereferencers...
9             # ...
10             (?:
11             (?= \$ ) (?&PerlScalarAccess)
12             |
13             (?= \@ ) (?&PerlArrayAccess)
14             |
15             (?= % ) (?&PerlHashAccess)
16             |
17             (?&PerlAnonymousSubroutine)
18             |
19             (?>(?&PerlNullaryBuiltinFunction)) (?! (?>(?&PerlOWS)) \( )
20             |
21             (?&PerlDoBlock) | (?&PerlEvalBlock)
22             |
23             (?&PerlCall)
24             |
25             (?&PerlVariableDeclaration)
26             |
27             (?&PerlTypeglob)
28             |
29             (?>(?&PerlParenthesesList))
30              
31             # Can optionally do a [...] lookup straight after the parens,
32             # followd by any number of other look-ups
33             (?:
34             (?>(?&PerlOWS)) (?&PerlArrayIndexer)
35             (?:
36             (?>(?&PerlOWS))
37             (?>
38             (?&PerlArrayIndexer)
39             | (?&PerlHashIndexer)
40             | (?&PerlParenthesesList)
41             )
42             )*+
43             )?+
44             |
45             (?&PerlAnonymousArray)
46             |
47             (?&PerlAnonymousHash)
48             |
49             (?&PerlDiamondOperator)
50             |
51             (?&PerlContextualMatch)
52             |
53             (?&PerlQuotelikeS)
54             |
55             (?&PerlQuotelikeTR)
56             |
57             (?&PerlQuotelikeQX)
58             |
59             (?&PerlLiteral)
60             )
61             };
62              
63             my $scalarnospace_post = q{
64             # Copied from <PerlScalarAccessNoSpace> rule in PPR::X@0.001002
65             # Then any nuber of arrowed accesses
66             # (this is an inlined subset of (?&PerlTermPostfixDereference))...
67             (?:
68             ->
69             (?>
70             # A series of simple brackets can omit interstitial arrows...
71             (?: (?&PerlArrayIndexer)
72             | (?&PerlHashIndexer)
73             )++
74              
75             | # An array or hash slice...
76             \@ (?> (?>(?&PerlArrayIndexer)) | (?>(?&PerlHashIndexer)) )
77             )
78             )*+
79              
80             # Followed by at most one of these terminal arrowed dereferences...
81             (?:
82             ->
83             (?>
84             # An array or scalar deref...
85             [\@\$] \*
86              
87             | # An array count deref...
88             \$ \# \*
89             )
90             )?+
91             };
92              
93             sub transform_to_plain {
94 21     21 0 20929 my ($self, $top) = @_;
95 21         73 for my $argument (qw(postderef postderef_qq)) {
96 42         294 $top->remove_use_argument(experimental => $argument);
97 42         479 $top->remove_use_argument(feature => $argument);
98             }
99             my $tf = sub {
100 23     23   56 my ($m, $in_quotelike) = @_;
101 23 100       194 return if Babble::Config::BAIL_OUT_LATE && $m->text !~ m/ \s* -> \s* [\@%\$] /xs;
102 17   66     80 my $interpolate = defined $in_quotelike && $in_quotelike;
103 17         121 my ($term, $postfix) = $m->subtexts(qw(term postfix));
104             #warn "Term: $term"; warn "Postfix: $postfix";
105 17         331 my $grammar = $m->grammar_regexp;
106 17         806210 my $strip_re = qr{
107             ( (?>(?&PerlOWS)) -> (?>(?&PerlOWS))
108             (?>
109             \$\#\*
110             | \$\*
111             | (?> (?&PerlQualifiedIdentifier) | (?&PerlVariableScalar) )
112             (?: (?>(?&PerlOWS)) (?&PerlParenthesesList) )?+
113             | (?:
114             (?>(?&PerlOWS))
115             (?> (?&PerlParenthesesList) | (?&PerlArrayIndexer) | (?&PerlHashIndexer) )
116             )++
117             )
118             )
119             ${grammar}
120             }x;
121 17         826394 while ($postfix =~ s/^${strip_re}//) {
122 10         2192 my $stripped = $1;
123 10 100       121 if ($stripped =~ /(\$\#?)\*$/) {
124 5         16 my $sigil = $1;
125 5         28 $term = $sigil.'{'.$term.'}';
126 5 100       151700 if( $interpolate ) {
127 2         87925 $term = "\@{[ $term ]}";
128             }
129             } else {
130 5         248733 $term .= $stripped;
131             }
132             }
133 17 100       2801 if ($postfix) {
134 12         193 my ($sigil, $rest) = ($postfix =~ /^\s*->\s*([\@%])(.*)$/);
135 12 100       94 $rest = '' if $rest eq '*';
136 12         76 $term = $sigil.'{'.$term.'}'.$rest;
137 12 100       78 if( $interpolate ) {
138             # NOTE This can be interpolated safely
139             # because:
140             # 1. The delimiters are balanced so use inside of
141             # `qq{ ... }` or `qq[ ... ]` is safe.
142             # 2. The contents of $term can only contain expressions that
143             # have `$` and `@` sigils, so any expression contained in
144             # $term which is used within `qq@ ... @` will not have the
145             # `@` sigil (same with the `qq$ ... $` and `$` sigil).
146 3         15 $term = "\@{[ $term ]}";
147             }
148             }
149 17         954 $m->submatches->{term}->replace_text($term);
150 17         314 $m->submatches->{postfix}->replace_text('');
151 21         183 };
152 21         213 $top->each_match_within(Term => [
153             [ term => "(?> $term_derefable )" ],
154             [ postfix => '(?&PerlTermPostfixDereference)' ],
155             ] => $tf);
156              
157             # NOTE ScalarAccessNoSpace is used within the
158             # ScalarAccessNoSpaceNoArrow rule, but any such
159             # matches here via that rule would be invalid input
160             # to begin with.
161             $top->each_match_within(ScalarAccessNoSpace => [
162             [ term => q{
163             (?>(?&PerlVariableScalarNoSpace))
164              
165             # Optional arrowless access(es) to begin...
166             (?: (?&PerlArrayIndexer) | (?&PerlHashIndexer) )*+
167             } ],
168             [ postfix => $scalarnospace_post ],
169 21     10   298 ] => sub { $tf->(shift, 1) });
  10         37  
170             # NOTE ArrayAccessNoSpace also needs to implemented.
171             }
172              
173             sub check_bail_out_early {
174 0     0 0   my ($self, $top) = @_;
175 0   0       $top->text !~ m/postderef|postderef_qq/xs
176             && $top->text !~ m/ \s* -> \s* [\@%\$] /xs;
177             }
178              
179             1;
180             __END__
181              
182             =head1 NAME
183              
184             Babble::Plugin::PostfixDeref - Plugin for postfix dereferencing
185              
186             =head1 SYNOPSIS
187              
188             Converts usage of the postderef syntax from
189              
190             $foo->@*
191              
192             to
193              
194             @{$foo}
195              
196             =head1 SEE ALSO
197              
198             L<postderef feature|feature/"The 'postderef' and 'postderef_qq' features">
199              
200             =cut