File Coverage

blib/lib/Devel/Chitin/OpTree/BINOP.pm
Criterion Covered Total %
statement 143 150 95.3
branch 49 58 84.4
condition 31 51 60.7
subroutine 27 27 100.0
pod 1 13 7.6
total 251 299 83.9


line stmt bran cond sub pod time code
1             package Devel::Chitin::OpTree::BINOP;
2 35     35   191 use base 'Devel::Chitin::OpTree::UNOP';
  35         52  
  35         3279  
3              
4             our $VERSION = '0.12'; # TRIAL
5              
6 35     35   185 use strict;
  35         49  
  35         622  
7 35     35   135 use warnings;
  35         49  
  35         831  
8              
9 35     35   172 use Carp;
  35         67  
  35         17759  
10              
11             # probably an ex-lineseq with 2 kids
12             *pp_lineseq = \&Devel::Chitin::OpTree::LISTOP::pp_lineseq;
13              
14             sub last {
15 941     941 1 2238 shift->{children}->[-1];
16             }
17              
18             sub pp_sassign {
19 343     343 0 609 my($self, %params) = @_;
20             # normally, the args are ordered: value, variable
21             my($var, $value) = $params{is_swapped}
22 343 100       848 ? ($self->first->deparse, $self->last->deparse)
23             : ($self->last->deparse, $self->first->deparse);
24 343         1244 return join(' = ', $var, $value);
25             }
26              
27             sub pp_aassign {
28 98     98 0 159 my $self = shift;
29              
30 98         154 my $container;
31 98 100 66     179 if ($self->is_null
      66        
      33        
      33        
      33        
      33        
      33        
      33        
32             and
33             # assigning-to is optimized away
34             $self->last->is_null and $self->last->_ex_name eq 'pp_list'
35             and
36             $self->last->children->[1]->is_null and $self->last->children->[1]->is_array_container
37             and
38             # value is an in-place sort: @a = sort @a;
39             $self->first->is_null and $self->first->_ex_name eq 'pp_list'
40             and
41             $self->first->children->[1]->op->name eq 'sort'
42             and
43             $self->first->children->[1]->op->private & B::OPpSORT_INPLACE
44             ) {
45             # since we're optimized away, we can't find out what variable we're
46             # assigning . It's the variable the sort is acting on.
47 7         18 $container = $self->first->children->[1]->children->[-1]->deparse;
48              
49             } else {
50 91         202 $container = $self->last->deparse;
51             }
52              
53 98         309 "$container = " . $self->first->deparse;
54             }
55              
56             sub pp_refassign {
57 3     3 0 5 my $self = shift;
58              
59 3         4 my $left;
60 3 100       151 if ($self->op->flags & B::OPf_STACKED) {
61 2         5 $left = $self->last->deparse;
62             } else {
63 1         4 $left = $self->_padname_sv->PV;
64             }
65              
66 3         9 my $right = $self->first->deparse;
67 3         11 "\\${left} = $right";
68             }
69              
70             sub pp_list {
71 170     170 0 235 my $self = shift;
72              
73             # 'list' is usually a LISTOP, but if we got here's it's because we're
74             # actually a 'null' ex-list, and there's only one item in the list.
75             # $self->first will be a pushmark
76             # @list = @other_list;
77             # We can emit a value without surrounding parens unless it's a scalar
78             # being assigned to
79              
80 170         301 my $contents = $self->last->deparse;
81              
82 170 100 100     337 if ($self->last->is_scalar_container
83             or
84             $self->is_list_reference_alias
85             ) {
86 4         19 "(${contents})";
87              
88             } else {
89 166         599 $contents;
90             }
91             }
92              
93             foreach my $cond ( [lt => '<'],
94             [le => '<='],
95             [gt => '>'],
96             [ge => '>='],
97             [eq => '=='],
98             [ne => '!='],
99             [ncmp => '<=>'],
100             [slt => 'lt'],
101             [sle => 'le'],
102             [sgt => 'gt'],
103             [sge => 'ge'],
104             [seq => 'eq'],
105             [sne => 'ne'],
106             [scmp => 'cmp'],
107             )
108             {
109             my $expr = ' ' . $cond->[1] . ' ';
110             my $sub = sub {
111 35     35   56 my $self = shift;
112 35         70 return join($expr, $self->first->deparse, $self->last->deparse);
113             };
114             my $subname = 'pp_' . $cond->[0];
115 35     35   229 no strict 'refs';
  35         64  
  35         28588  
116             *$subname = $sub;
117             }
118              
119             sub pp_stringify {
120 5     5 0 10 my $self = shift;
121              
122 5 50 33     13 unless ($self->first->op->name eq 'null'
123             and
124             $self->first->_ex_name eq 'pp_pushmark'
125             ) {
126 0         0 die "unknown stringify ".$self->first->op->name;
127             }
128              
129 5         13 my $children = $self->children;
130 5 50       14 unless (@$children == 2) {
131             die "expected 2 children but got " . scalar(@$children)
132 0         0 . ': ' . join(', ', map { $_->op->name } @$children);
  0         0  
133             }
134              
135 5 100 100     12 if ($self->is_null
      66        
136             and $self->op->private & B::OPpTARGET_MY
137             and $children->[1]->op->name eq 'concat'
138             ) {
139 1         6 $children->[1]->deparse(skip_concat => 1, force_quotes => ['qq(', ')']);
140              
141             } else {
142 4         14 my $target = $self->_maybe_targmy;
143              
144 4         15 "${target}qq(" . $children->[1]->deparse(skip_concat => 1, skip_quotes => 1) . ')';
145             }
146             }
147              
148             sub pp_concat {
149 17     17 0 31 my $self = shift;
150 17         41 my %params = @_;
151              
152 17         39 my $first = $self->first;
153 17 100 100     35 if ($self->op->flags & B::OPf_STACKED
154             and
155             $first->op->name ne 'concat'
156             ) {
157             # This is an assignment-concat: $a .= 'foo'
158 1         10 $first->deparse . ' .= ' . $self->last->deparse;
159              
160             } else {
161 16         40 my $target = $self->_maybe_targmy;
162             my $concat_str = join($params{skip_concat} ? '' : ' . ',
163 16 100       82 $first->deparse(%params, $params{force_quotes} ? (skip_quotes => 1) : ()),
    100          
164             $self->last->deparse(%params));
165 16 100       42 if ($params{force_quotes}) {
166 1         3 $concat_str = join($concat_str, @{$params{force_quotes}});
  1         4  
167             }
168 16         65 $target . $concat_str;
169             }
170             }
171              
172             sub pp_reverse {
173             # a BINOP reverse is only acting on a single item
174             # 0th child is pushmark, skip it
175 2     2 0 17 'reverse(' . shift->last->deparse . ')';
176             }
177              
178             sub pp_leaveloop {
179 12     12 0 28 my $self = shift;
180              
181 12 50       31 if (my $deparsed = $self->_deparse_postfix_while) {
182 0         0 return $deparsed;
183             }
184              
185 12         31 my $enterloop = $self->first;
186 12 100       34 if ($enterloop->op->name eq 'enteriter') {
187 7         21 return $self->_deparse_foreach;
188              
189             } else {
190 5         13 return $self->_deparse_while_until;
191             }
192             }
193              
194             sub _deparse_while_until {
195 5     5   7 my $self = shift;
196              
197             # while loops are structured like this:
198             # leaveloop
199             # enterloop
200             # null
201             # and/or
202             # null
203             # condition
204             # lineseq
205             # loop contents
206 5         12 my $condition_op = $self->last->first; # the and/or
207 5         12 my $enterloop = $self->first;
208 5 100       11 my $loop_invocation = $condition_op->op->name eq 'and'
209             ? 'while'
210             : 'until';
211 5         10 my $continue_content = '';
212 5         8 my $loop_content;
213 5 100       22 if ($enterloop->nextop->op->name eq 'unstack') {
214             # no continue
215             # loop contents are wrapped in a lineseq
216 4         14 $loop_content = '{' . $self->_indent_block_text( $condition_op->other->deparse, force_multiline => 1 ) . '}';
217             } else {
218             # has continue
219             # loop and continue contents are wrapped in scopes
220 1         4 my $children = $condition_op->other->children;
221 1         4 $loop_content = $children->[0]->deparse(force_multiline => 1);
222 1         4 $continue_content = ' continue ' . $children->[1]->deparse(force_multiline => 1);
223             }
224              
225 5         18 my $loop_condition = $condition_op->first->deparse;
226 5 100       14 if ($condition_op->op->name eq 'and') {
227 4         8 $loop_invocation = 'while';
228              
229             } else {
230 1         4 $loop_invocation = 'until';
231 1         14 $loop_condition =~ s/^!//;
232             }
233              
234 5         24 "$loop_invocation ($loop_condition) ${loop_content}${continue_content}";
235             }
236              
237             sub _deparse_foreach {
238 7     7   11 my $self = shift;
239             # foreach loops look like this:
240             # leaveloop
241             # enteriter
242             # pushmark
243             # list
244             # ... (iterate-over list)
245             # iteration variable
246             # null
247             # and
248             # iter
249             # lineseq
250             # loop contents
251 7         14 my $enteriter = $self->first;
252              
253 7         19 my $list_op = $enteriter->children->[1];
254 7         10 my $iter_list;
255 7 100 100     15 if ($enteriter->op->flags & B::OPf_STACKED
    50          
256             and
257             $list_op->children->[2]
258             ) {
259             # range
260 1         3 $iter_list = '(' . join(' .. ', map { $_->deparse } @{$list_op->children}[1,2]) . ')';
  2         5  
  1         3  
261              
262             } elsif ($list_op->is_null) {# and $enteriter->op->private & B::OPpITER_REVERSED) {
263             # either foreach(reverse @list) or foreach (@list)
264 6         26 $iter_list = $list_op->Devel::Chitin::OpTree::LISTOP::pp_list;
265              
266             } else {
267 0         0 $iter_list = $list_op->deparse;
268             }
269              
270 7         21 my $var_op = $enteriter->children->[2];
271 7 100       28 my $var = $var_op
272             ? '$' . $var_op->deparse(skip_sigil => 1)
273             : $enteriter->pp_padsv;
274              
275 7         21 my $loop_content_op = $enteriter->sibling->first->first->sibling; # should be a lineseq
276 7         19 my $loop_content = $loop_content_op->deparse;
277              
278 7 100       19 if ($loop_content_op->first->isa('Devel::Chitin::OpTree::COP')) {
279 6         22 $loop_content = $self->_indent_block_text( $loop_content );
280 6         34 "foreach $var $iter_list {$loop_content}";
281             } else {
282 1 50       4 Carp::croak("In postfix foreach, expected loop var '\$_', but got '$var'") unless $var eq '$_';
283 1         5 "$loop_content foreach $iter_list"
284             }
285             }
286              
287             # leave is normally a LISTOP, but this happens when this is run
288             # in the debugger
289             # sort { ; } @list
290             # The leave is turned into a null:
291             # ex-leave
292             # enter
293             # stub
294             *pp_leave = \&Devel::Chitin::OpTree::LISTOP::pp_leave;
295              
296             # from B::Concise
297 35     35   257 use constant DREFAV => 32;
  35         67  
  35         2642  
298 35     35   213 use constant DREFHV => 64;
  35         56  
  35         1577  
299 35     35   180 use constant DREFSV => 96;
  35         50  
  35         17591  
300              
301             sub pp_helem {
302 2     2 0 4 my $self = shift;
303              
304 2         7 my $first = $self->first;
305 2         7 my($hash, $key) = ($first->deparse, $self->last->deparse);
306 2 50       8 if ($self->_is_chain_deref('rv2hv', DREFHV)) {
307             # This is a dereference, like $a->{foo}
308 0         0 substr($hash, 1) . '->{' . $key . '}';
309             } else {
310 2         11 substr($hash, 0, 1) = '$';
311 2         12 "${hash}{${key}}";
312             }
313             }
314              
315             sub _is_chain_deref {
316 4     4   12 my($self, $expected_first_op, $expected_flag) = @_;
317 4         10 my $child = $self->first;
318 4 100       25 return unless $child->isa('Devel::Chitin::OpTree::UNOP');
319              
320 1 50       3 $child->op->name eq $expected_first_op
321             and
322             $child->first->op->private & $expected_flag
323             }
324              
325             sub pp_aelem {
326 5     5 0 13 my $self = shift;
327 5         13 my $first = $self->first;
328              
329 5         16 my($array, $elt) = ($first->deparse, $self->last->deparse);
330 5 100 66     21 if ($self->is_null
    50 66        
      66        
331             and
332             ($first->op->name eq 'aelemfast_lex' or $first->op->name eq 'aelemfast')
333             and
334             $self->last->is_null
335             ) {
336 3         16 $array;
337              
338             } elsif ($self->_is_chain_deref('rv2av', DREFAV)) {
339             # This is a dereference, like $a->[1]
340 0         0 substr($array, 1) . '->[' . $elt . ']';
341              
342             } else {
343 2         9 substr($array, 0, 1) = '$';
344 2         6 my $idx = $self->last->deparse;
345 2         13 "${array}[${idx}]";
346             }
347             }
348              
349             sub pp_smartmatch {
350 3     3 0 6 my $self = shift;
351 3 50       8 if ($self->op->flags & B::OPf_SPECIAL) {
352 3         7 $self->last->deparse;
353             }
354             }
355              
356             sub pp_lslice {
357 2     2 0 3 my $self = shift;
358              
359 2         6 my $list = $self->last->deparse(skip_parens => 1);
360 2         6 my $idx = $self->first->deparse(skip_parens => 1);
361 2         8 "($list)[$idx]";
362             }
363              
364             # Operators
365             # OP name operator targmy?
366             foreach my $a ( [ pp_add => '+', 1 ],
367             [ pp_i_add => '+', 1 ],
368             [ pp_subtract => '-', 1 ],
369             [ pp_i_subtract => '-', 1 ],
370             [ pp_multiply => '*', 1 ],
371             [ pp_i_multiply => '*', 1 ],
372             [ pp_divide => '/', 1 ],
373             [ pp_i_divide => '/', 1 ],
374             [ pp_modulo => '%', 1 ],
375             [ pp_i_modulo => '%', 1 ],
376             [ pp_pow => '**', 1 ],
377             [ pp_left_shift => '<<', 1 ],
378             [ pp_right_shift => '>>', 1 ],
379             [ pp_repeat => 'x', 0 ],
380             [ pp_bit_and => '&', 0 ],
381             [ pp_bit_or => '|', 0 ],
382             [ pp_bit_xor => '^', 0 ],
383             [ pp_xor => 'xor', 0 ],
384             [ pp_sbit_and => '&.', 0 ],
385             [ pp_sbit_or => '|.', 0 ],
386             [ pp_sbit_xor => '^.', 0 ],
387             ) {
388             my($pp_name, $perl_name, $targmy) = @$a;
389             my $sub = sub {
390 54     54   86 my $self = shift;
391              
392 54 100       96 if ($self->op->flags & B::OPf_STACKED) {
393             # This is an assignment op: +=
394 13         31 my $first = $self->first->deparse;
395 13         45 "$first ${perl_name}= " . $self->last->deparse;
396             } else {
397 41 100       111 my $target = $targmy ? $self->_maybe_targmy : '';
398 41         85 $target . $self->first->deparse . " $perl_name " . $self->last->deparse;
399             }
400             };
401 35     35   212 no strict 'refs';
  35         62  
  35         1685  
402             *$pp_name = $sub;
403             }
404              
405             1;
406              
407             __END__