File Coverage

blib/lib/Language/P/ParseTree/PropagateContext.pm
Criterion Covered Total %
statement 117 141 82.9
branch 33 54 61.1
condition 7 14 50.0
subroutine 27 34 79.4
pod 0 1 0.0
total 184 244 75.4


line stmt bran cond sub pod time code
1             package Language::P::ParseTree::PropagateContext;
2              
3 88     88   190612 use strict;
  88         225  
  88         3982  
4 88     88   530 use warnings;
  88         194  
  88         3569  
5 88     88   508 use base qw(Language::P::ParseTree::Visitor);
  88         164  
  88         46960  
6              
7 88     88   610 use Language::P::ParseTree qw(:all);
  88         198  
  88         364759  
8              
9             my %dispatch =
10             ( 'Language::P::ParseTree::FunctionCall' => '_function_call',
11             'Language::P::ParseTree::BuiltinIndirect' => '_builtin_indirect',
12             'Language::P::ParseTree::Builtin' => '_function_call',
13             'Language::P::ParseTree::Overridable' => '_function_call',
14             'Language::P::ParseTree::MethodCall' => '_method_call',
15             'Language::P::ParseTree::UnOp' => '_unary_op',
16             'Language::P::ParseTree::Parentheses' => '_parentheses',
17             'Language::P::ParseTree::Dereference' => '_dereference',
18             'Language::P::ParseTree::Local' => '_local',
19             'Language::P::ParseTree::Jump' => '_jump',
20             'Language::P::ParseTree::BinOp' => '_binary_op',
21             'Language::P::ParseTree::Symbol' => '_symbol',
22             'Language::P::ParseTree::Constant' => '_set_context',
23             'Language::P::ParseTree::LexicalDeclaration' => '_symbol',
24             'Language::P::ParseTree::LexicalSymbol' => '_symbol',
25             'Language::P::ParseTree::List' => '_list',
26             'Language::P::ParseTree::Conditional' => '_cond',
27             'Language::P::ParseTree::ConditionalLoop' => '_cond_loop',
28             'Language::P::ParseTree::Ternary' => '_ternary',
29             'Language::P::ParseTree::Block' => '_block',
30             'Language::P::ParseTree::BareBlock' => '_bare_block',
31             'Language::P::ParseTree::Subroutine' => '_subroutine',
32             'Language::P::ParseTree::AnonymousSubroutine' => '_subroutine',
33             'Language::P::ParseTree::SubroutineDeclaration' => '_noop',
34             'Language::P::ParseTree::QuotedString' => '_quoted_string',
35             'Language::P::ParseTree::Subscript' => '_subscript',
36             'Language::P::ParseTree::Slice' => '_slice',
37             'Language::P::ParseTree::ReferenceConstructor' => '_ref_constr',
38             'Language::P::ParseTree::Pattern' => '_noop',
39             'Language::P::ParseTree::InterpolatedPattern' => '_pattern',
40             'Language::P::ParseTree::Substitution' => '_substitution',
41             'Language::P::ParseTree::Foreach' => '_foreach',
42             'Language::P::ParseTree::For' => '_for',
43             'Language::P::ParseTree::Package' => '_noop',
44             'Language::P::ParseTree::Empty' => '_noop',
45             'DEFAULT' => '_noisy_noop',
46             );
47              
48 1903     1903 0 6243 sub method_map { \%dispatch }
49              
50 90     90   1011 sub _lv { return $_[0] | ( $_[1] & CXT_LVALUE ) }
51              
52             sub _noop {
53 0     0   0 my( $self, $tree, $cxt ) = @_;
54              
55             # nothing to do
56             }
57              
58             sub _noisy_noop {
59 0     0   0 my( $self, $tree, $cxt ) = @_;
60              
61 0   0     0 Carp::confess( "Unhandled context for ", ref( $tree ) || $tree, "\n" );
62             }
63              
64             sub _set_context {
65 514     514   863 my( $self, $tree, $cxt ) = @_;
66              
67 514         1402 $tree->set_attribute( 'context', $cxt );
68             }
69              
70             sub _symbol {
71 484     484   732 my( $self, $tree, $cxt ) = @_;
72              
73 484 100 66     2208 if( $tree->sigil == VALUE_ARRAY || $tree->sigil == VALUE_HASH ) {
74 17         186 $tree->set_attribute( 'context', $cxt );
75             } else {
76 467 100       7683 $tree->set_attribute( 'context', $cxt & CXT_LIST ? _lv( CXT_SCALAR, $cxt ) : $cxt );
77             }
78             }
79              
80             sub _quoted_string {
81 29     29   47 my( $self, $tree, $cxt ) = @_;
82              
83 29         56 foreach my $component ( @{$tree->components} ) {
  29         101  
84 87         414 $self->visit( $component, CXT_SCALAR );
85             }
86             }
87              
88             sub _subscript {
89 8     8   14 my( $self, $tree, $cxt ) = @_;
90              
91 8         46 $tree->set_attribute( 'context', $cxt );
92              
93 8 50       26 $self->visit( $tree->subscripted, $tree->reference ? CXT_SCALAR|CXT_VIVIFY :
94             CXT_LIST );
95 8         29 $self->visit( $tree->subscript, CXT_SCALAR );
96             }
97              
98             sub _slice {
99 0     0   0 my( $self, $tree, $cxt ) = @_;
100              
101 0         0 $tree->set_attribute( 'context', $cxt );
102              
103 0 0       0 $self->visit( $tree->subscripted, $tree->reference ? CXT_SCALAR|CXT_VIVIFY :
104             CXT_LIST );
105 0         0 $self->visit( $tree->subscript, CXT_LIST );
106             }
107              
108             sub _list {
109 22     22   52 my( $self, $tree, $cxt ) = @_;
110              
111 22         180 $tree->set_attribute( 'context', $cxt );
112 22 50       111 my $inner = _lv( $cxt & CXT_LIST ? CXT_LIST :
    100          
113             $cxt & CXT_CALLER ? CXT_CALLER :
114             CXT_VOID, $cxt );
115 22 50       38 if( @{$tree->expressions} ) {
  22         79  
116 22         173 $self->visit( $tree->expressions->[-1], $cxt );
117 22         396 for( my $i = $#{$tree->expressions} - 1; $i >= 0; --$i ) {
  22         75  
118 25         180 $self->visit( $tree->expressions->[$i], $inner );
119             }
120             }
121             }
122              
123             sub _block {
124 76     76   131 my( $self, $tree, $cxt ) = @_;
125              
126 76 50       108 if( @{$tree->lines} ) {
  76         227  
127 76         597 $self->visit( $tree->lines->[-1], $cxt );
128 76         279 for( my $i = $#{$tree->lines} - 1; $i >= 0; --$i ) {
  76         230  
129 45         400 $self->visit( $tree->lines->[$i], CXT_VOID );
130             }
131             }
132             }
133              
134             sub _bare_block {
135 8     8   20 my( $self, $tree, $cxt ) = @_;
136              
137 8         47 _block( $self, $tree, $cxt );
138 8 100       43 $self->visit( $tree->continue, CXT_VOID ) if $tree->continue;
139             }
140              
141             sub _function_call {
142 222     222   340 my( $self, $tree, $cxt ) = @_;
143              
144 222         885 $tree->set_attribute( 'context', $cxt );
145 222   100     776 my $arg_cxts = $tree->runtime_context || [ CXT_LIST ];
146 222 100       1838 $self->visit( $tree->function, CXT_SCALAR ) if ref $tree->function;
147              
148 222 100       1323 if( $tree->arguments ) {
149 185         960 my $arg_index = 0;
150 185         216 foreach my $arg ( @{$tree->arguments} ) {
  185         569  
151 185 50       1173 my $arg_cxt = $arg_index <= $#$arg_cxts ? $arg_cxts->[$arg_index] :
152             $arg_cxts->[-1];
153 185         582 $self->visit( $arg, $arg_cxt );
154 185         733 ++$arg_index;
155             }
156             }
157             }
158              
159             sub _method_call {
160 0     0   0 my( $self, $tree, $cxt ) = @_;
161              
162 0         0 $tree->set_attribute( 'context', $cxt );
163 0         0 $self->visit( $tree->invocant, CXT_SCALAR );
164 0 0       0 $self->visit( $tree->method, CXT_SCALAR ) if ref $tree->method;
165              
166 0 0       0 if( $tree->arguments ) {
167 0         0 foreach my $arg ( @{$tree->arguments} ) {
  0         0  
168 0         0 $self->visit( $arg, CXT_LIST );
169             }
170             }
171             }
172              
173             sub _builtin_indirect {
174 122     122   198 my( $self, $tree, $cxt ) = @_;
175              
176 122         309 $self->_function_call( $tree, $cxt );
177 122 50       394 if( $tree->indirect ) {
178 0 0 0     0 my $arg_cxt = $tree->function == OP_MAP || $tree->function == OP_GREP ?
179             CXT_LIST : CXT_SCALAR;
180 0         0 $self->visit( $tree->indirect, $arg_cxt );
181             }
182             }
183              
184             sub _unary_op {
185 9     9   16 my( $self, $tree, $cxt ) = @_;
186              
187 9         46 $tree->set_attribute( 'context', $cxt );
188 9         31 $self->visit( $tree->left, CXT_SCALAR );
189             }
190              
191             sub _parentheses {
192 5     5   11 my( $self, $tree, $cxt ) = @_;
193              
194 5         40 $tree->set_attribute( 'context', $cxt );
195 5         18 $self->visit( $tree->left, $cxt );
196             }
197              
198             sub _dereference {
199 9     9   15 my( $self, $tree, $cxt ) = @_;
200              
201 9 50       49 $tree->set_attribute( 'context', $cxt | ( $cxt & CXT_LVALUE ? CXT_VIVIFY : 0 ) );
202 9         26 $self->visit( $tree->left, CXT_SCALAR );
203             }
204              
205             sub _local {
206 14     14   34 my( $self, $tree, $cxt ) = @_;
207              
208 14         55 $tree->set_attribute( 'context', $cxt );
209 14         42 $self->visit( $tree->left, $cxt|CXT_LVALUE );
210             }
211              
212             sub _jump {
213 17     17   31 my( $self, $tree, $cxt ) = @_;
214              
215 17 50       46 $self->visit( $tree->left, CXT_SCALAR ) if ref $tree->left;
216             }
217              
218             sub _binary_op {
219 330     330   504 my( $self, $tree, $cxt ) = @_;
220              
221 330         1047 $tree->set_attribute( 'context', $cxt );
222              
223             # FIXME some binary operators do not force scalar context
224              
225 330 100 100     912 if( $tree->op == OP_LOG_OR || $tree->op == OP_LOG_AND ) {
    100          
226 43         459 $self->visit( $tree->left, CXT_SCALAR );
227 43 50       157 $self->visit( $tree->right, _lv( $cxt & CXT_VOID ? CXT_VOID :
    50          
228             $cxt & CXT_CALLER ? CXT_CALLER :
229             CXT_SCALAR,
230             $cxt ) );
231             } elsif( $tree->op == OP_ASSIGN ) {
232 138         3806 my $left_cxt = $tree->left->lvalue_context;
233              
234 138         2253 $self->visit( $tree->left, $left_cxt|CXT_LVALUE );
235 138         501 $self->visit( $tree->right, $left_cxt );
236             } else {
237 149         2651 $self->visit( $tree->left, CXT_SCALAR );
238 149         510 $self->visit( $tree->right, CXT_SCALAR );
239             }
240             }
241              
242             sub _pattern {
243 0     0   0 my( $self, $tree, $cxt ) = @_;
244              
245 0         0 $self->visit( $tree->string, CXT_SCALAR );
246             }
247              
248             sub _substitution {
249 0     0   0 my( $self, $tree, $cxt ) = @_;
250              
251 0         0 $self->visit( $tree->pattern, CXT_SCALAR );
252 0         0 $self->visit( $tree->replacement, CXT_SCALAR );
253             }
254              
255             sub _foreach {
256 11     11   23 my( $self, $tree, $cxt ) = @_;
257              
258 11         38 $self->visit( $tree->variable, CXT_SCALAR );
259 11         48 $self->visit( $tree->expression, CXT_LIST );
260 11         44 $self->visit( $tree->block, CXT_VOID );
261 11 100       146 $self->visit( $tree->continue, CXT_VOID ) if $tree->continue;
262             }
263              
264             sub _for {
265 6     6   15 my( $self, $tree, $cxt ) = @_;
266              
267 6         21 $self->visit( $tree->condition, CXT_SCALAR );
268 6         30 $self->visit( $tree->initializer, CXT_VOID );
269 6         27 $self->visit( $tree->step, CXT_VOID );
270 6         29 $self->visit( $tree->block, CXT_VOID );
271             }
272              
273             sub _cond_loop {
274 16     16   26 my( $self, $tree, $cxt ) = @_;
275              
276 16         54 $self->visit( $tree->condition, CXT_SCALAR );
277 16         61 $self->visit( $tree->block, CXT_VOID );
278 16 100       203 $self->visit( $tree->continue, CXT_VOID ) if $tree->continue;
279             }
280              
281             sub _cond {
282 26     26   47 my( $self, $tree, $cxt ) = @_;
283              
284 26 100       75 $self->visit( $tree->iffalse->block, $cxt ) if $tree->iffalse;
285 26         154 foreach my $iftrue ( @{$tree->iftrues} ) {
  26         84  
286 31         215 $self->visit( $iftrue->condition, CXT_SCALAR );
287 31         114 $self->visit( $iftrue->block, $cxt );
288             }
289             }
290              
291             sub _subroutine {
292 27     27   57 my( $self, $tree, $cxt ) = @_;
293              
294 27 50       103 if( @{$tree->lines} ) {
  27         92  
295 27         210 $self->visit( $tree->lines->[-1], CXT_CALLER );
296 27         89 for( my $i = $#{$tree->lines} - 1; $i >= 0; --$i ) {
  27         84  
297 14         105 $self->visit( $tree->lines->[$i], CXT_VOID );
298             }
299             }
300             }
301              
302             sub _ternary {
303 78     78   128 my( $self, $tree, $cxt ) = @_;
304              
305 78         280 $tree->set_attribute( 'context', $cxt );
306              
307 78         251 $self->visit( $tree->condition, CXT_SCALAR );
308 78         298 $self->visit( $tree->iftrue, $cxt );
309 78         240 $self->visit( $tree->iffalse, $cxt );
310             }
311              
312             sub _ref_constr {
313 0     0     my( $self, $tree, $cxt ) = @_;
314              
315 0 0         $self->visit( $tree->expression, CXT_LIST ) if $tree->expression;
316             }
317              
318             1;