File Coverage

blib/lib/Language/P/Intermediate/Generator.pm
Criterion Covered Total %
statement 524 561 93.4
branch 127 164 77.4
condition 23 29 79.3
subroutine 64 68 94.1
pod 1 10 10.0
total 739 832 88.8


line stmt bran cond sub pod time code
1             package Language::P::Intermediate::Generator;
2              
3 37     37   31917 use strict;
  37         92  
  37         1367  
4 37     37   211 use warnings;
  37         76  
  37         1199  
5 37     37   195 use base qw(Language::P::ParseTree::Visitor);
  37         71  
  37         4892  
6              
7             __PACKAGE__->mk_accessors( qw(_code_segments _current_basic_block _options
8             _label_count _temporary_count _current_block
9             _group_count file_name) );
10              
11 37     37   233 use Scalar::Util qw();
  37         76  
  37         718  
12              
13 37     37   20368 use Language::P::Intermediate::Code;
  37         119  
  37         303  
14 37     37   28580 use Language::P::Intermediate::BasicBlock;
  37         116  
  37         385  
15 37     37   1376 use Language::P::Opcodes qw(:all);
  37         109  
  37         51511  
16 37     37   12674 use Language::P::ParseTree::PropagateContext;
  37         102  
  37         763  
17 37     37   1852 use Language::P::ParseTree qw(:all);
  37         126  
  37         110742  
18 37     37   344 use Language::P::Keywords qw(:all);
  37         147  
  37         14475  
19 37     37   251 use Language::P::Assembly qw(:all);
  37         82  
  37         360218  
20              
21             sub new {
22 84     84 1 3213 my( $class, $args ) = @_;
23 84         1002 my $self = $class->SUPER::new( $args );
24              
25 84 100       420 $self->_options( {} ) unless $self->_options;
26 84         1431 $self->_label_count( 0 );
27 84         681 $self->_temporary_count( 0 );
28 84         691 $self->_group_count( 0 );
29              
30 84         584 return $self;
31             }
32              
33             sub set_option {
34 0     0 0 0 my( $self, $option, $value ) = @_;
35              
36 0 0       0 if( $option eq 'dump-ir' ) {
37 0         0 $self->_options->{$option} = 1;
38             }
39              
40 0         0 return 0;
41             }
42              
43             sub _add_bytecode {
44 2012     2012   23385 my( $self, @bytecode ) = @_;
45              
46 2012         2313 push @{$self->_current_basic_block->bytecode}, @bytecode;
  2012         8770  
47             }
48              
49             sub _add_jump {
50 467     467   5401 my( $self, $op, @to ) = @_;
51              
52 467         6545 $self->_current_basic_block->add_jump( $op, @to );
53             }
54              
55             sub _add_blocks {
56 570     570   1528 my( $self, @blocks ) = @_;
57              
58 570         683 push @{$self->_code_segments->[0]->basic_blocks}, @blocks;
  570         1496  
59 570         5133 _current_basic_block( $self, $blocks[-1] );
60             }
61              
62 150     150   563 sub _new_blocks { map _new_block( $_[0] ), 1 .. $_[1] }
63             sub _new_block {
64 641     641   1258 my( $self ) = @_;
65              
66 641         3414 return Language::P::Intermediate::BasicBlock
67             ->new_from_label( 'L' . ++$self->{_label_count} );
68             }
69              
70             sub push_block {
71 179     179 0 322 my( $self, $is_sub ) = @_;
72              
73 179   100     618 $self->_current_block
74             ( { outer => $self->_current_block,
75             is_sub => $is_sub || 0,
76             bytecode => [],
77             } );
78              
79 179         5567 return $self->_current_block;
80             }
81              
82             sub pop_block {
83 179     179 0 283 my( $self ) = @_;
84 179         484 my $to_ret = $self->_current_block;
85              
86 179         1031 $self->_current_block( $to_ret->{outer} );
87              
88 179         1866 return $to_ret;
89             }
90              
91             sub generate_regex {
92 16     16 0 134 my( $self, $regex ) = @_;
93              
94 16         133 _generate_regex( $self, $regex, undef );
95             }
96              
97             sub _generate_regex {
98 16     16   39 my( $self, $regex, $outer ) = @_;
99              
100 16         85 $self->_code_segments( [] );
101 16         308 $self->_group_count( 0 );
102              
103 16         85 push @{$self->_code_segments},
  16         51  
104             Language::P::Intermediate::Code->new
105             ( { type => 3,
106             basic_blocks => [],
107             lexicals => {},
108             } );
109 16 50       70 if( $outer ) {
110 0         0 push @{$outer->inner}, $self->_code_segments->[-1];
  0         0  
111 0         0 Scalar::Util::weaken( $outer->inner->[-1] );
112             }
113              
114 16         56 _add_blocks $self, _new_block( $self );
115 16         277 _add_bytecode $self, opcode_n( OP_RX_START_MATCH );
116              
117 16         146 foreach my $e ( @{$regex->components} ) {
  16         75  
118 31         449 $self->dispatch_regex( $e );
119             }
120              
121 16         224 _add_bytecode $self,
122             opcode_nm( OP_RX_ACCEPT, groups => $self->_group_count );
123              
124 16 50       174 die "Flags not supported" if $regex->flags;
125              
126 16         112 return $self->_code_segments;
127             }
128              
129             sub generate_subroutine {
130 0     0 0 0 my( $self, $tree, $outer ) = @_;
131              
132 0         0 my $context = Language::P::ParseTree::PropagateContext->new;
133 0         0 $context->visit( $tree, CXT_VOID );
134              
135 0         0 _generate_bytecode( $self, 1, $tree->name, $outer, $tree->lines );
136             }
137              
138             sub generate_bytecode {
139 60     60 0 443 my( $self, $statements ) = @_;
140              
141 60         710 my $context = Language::P::ParseTree::PropagateContext->new;
142 60         175 foreach my $tree ( @$statements ) {
143 201         1332 $context->visit( $tree, CXT_VOID );
144             }
145              
146 60         501 _generate_bytecode( $self, 0, undef, undef, $statements );
147             }
148              
149             sub _generate_bytecode {
150 81     81   498 my( $self, $is_sub, $name, $outer, $statements ) = @_;
151              
152 81         422 $self->_code_segments( [] );
153              
154 81 100       486 push @{$self->_code_segments},
  81         258  
155             Language::P::Intermediate::Code->new
156             ( { type => $is_sub ? 2 : 1,
157             name => $name,
158             basic_blocks => [],
159             outer => $outer,
160             lexicals => {},
161             } );
162 81 100       417 if( $outer ) {
163 21         35 push @{$outer->inner}, $self->_code_segments->[-1];
  21         87  
164 21         592 Scalar::Util::weaken( $outer->inner->[-1] );
165             }
166              
167 81         453 _add_blocks $self, _new_block( $self );
168 81         788 $self->push_block( $is_sub );
169              
170 81         426 foreach my $tree ( @$statements ) {
171 235         2695 $self->dispatch( $tree );
172 235         2928 _discard_if_void( $self, $tree );
173             }
174              
175 81         710 $self->pop_block;
176              
177 81         278 _add_bytecode $self, opcode_n( OP_END );
178              
179             # eliminate edges from a node with multiple successors to a node
180             # with multiple predecessors by inserting an empty node and
181             # splitting the edge
182 81         779 foreach my $block ( @{$self->_code_segments->[0]->basic_blocks} ) {
  81         265  
183 567 100       2443 next if @{$block->successors} != 2;
  567         1543  
184 135         790 my @to_change;
185 135         186 foreach my $succ ( @{$block->successors} ) {
  135         349  
186 270 100       1273 push @to_change, $succ if @{$succ->predecessors} >= 2;
  270         745  
187             }
188             # in two steps to avoid changing successors while iterating
189 135         916 foreach my $succ ( @to_change ) {
190 50         115 _add_blocks $self, _new_block( $self );
191 50         378 _add_jump $self, opcode_nm( OP_JUMP, to => $succ ), $succ;
192 50         526 $block->_change_successor( $succ, $self->_current_basic_block );
193             }
194             }
195              
196 81 50       730 if( $self->_options->{'dump-ir'} ) {
197 0         0 ( my $outfile = $self->file_name ) =~ s/(\.\w+)?$/.ir/;
198 0   0     0 open my $ir_dump, '>', $outfile || die "Can't open '$outfile': $!";
199              
200 0         0 foreach my $cs ( @{$self->_code_segments} ) {
  0         0  
201 0         0 foreach my $bb ( @{$cs->basic_blocks} ) {
  0         0  
202 0         0 foreach my $ins ( @{$bb->bytecode} ) {
  0         0  
203 0         0 print $ir_dump $ins->as_string( \%NUMBER_TO_NAME );
204             }
205             }
206             }
207             }
208              
209 81         692 return $self->_code_segments;
210             }
211              
212             my %dispatch =
213             ( 'Language::P::ParseTree::FunctionCall' => '_function_call',
214             'Language::P::ParseTree::Builtin' => '_builtin',
215             'Language::P::ParseTree::Overridable' => '_builtin',
216             'Language::P::ParseTree::BuiltinIndirect' => '_indirect',
217             'Language::P::ParseTree::UnOp' => '_unary_op',
218             'Language::P::ParseTree::Local' => '_local',
219             'Language::P::ParseTree::BinOp' => '_binary_op',
220             'Language::P::ParseTree::Constant' => '_constant',
221             'Language::P::ParseTree::Symbol' => '_symbol',
222             'Language::P::ParseTree::LexicalDeclaration' => '_lexical_declaration',
223             'Language::P::ParseTree::LexicalSymbol' => '_lexical_symbol',
224             'Language::P::ParseTree::List' => '_list',
225             'Language::P::ParseTree::Conditional' => '_cond',
226             'Language::P::ParseTree::ConditionalLoop' => '_cond_loop',
227             'Language::P::ParseTree::For' => '_for',
228             'Language::P::ParseTree::Foreach' => '_foreach',
229             'Language::P::ParseTree::Ternary' => '_ternary',
230             'Language::P::ParseTree::Block' => '_block',
231             'Language::P::ParseTree::BareBlock' => '_bare_block',
232             'Language::P::ParseTree::NamedSubroutine' => '_subroutine',
233             'Language::P::ParseTree::SubroutineDeclaration' => '_subroutine_decl',
234             'Language::P::ParseTree::AnonymousSubroutine' => '_anon_subroutine',
235             'Language::P::ParseTree::QuotedString' => '_quoted_string',
236             'Language::P::ParseTree::Subscript' => '_subscript',
237             'Language::P::ParseTree::Jump' => '_jump',
238             'Language::P::ParseTree::Pattern' => '_pattern',
239             'Language::P::ParseTree::Parentheses' => '_parentheses',
240             );
241              
242             my %dispatch_cond =
243             ( 'Language::P::ParseTree::BinOp' => '_binary_op_cond',
244             'DEFAULT' => '_anything_cond',
245             );
246              
247             my %dispatch_regex =
248             ( 'Language::P::ParseTree::RXQuantifier' => '_regex_quantifier',
249             'Language::P::ParseTree::RXGroup' => '_regex_group',
250             'Language::P::ParseTree::Constant' => '_regex_exact',
251             'Language::P::ParseTree::RXAlternation' => '_regex_alternate',
252             'Language::P::ParseTree::RXAssertion' => '_regex_assertion',
253             );
254              
255             sub dispatch {
256 1422     1422 0 7756 my( $self, $tree, @args ) = @_;
257              
258 1422         4758 return $self->visit_map( \%dispatch, $tree, @args );
259             }
260              
261             sub dispatch_cond {
262 112     112 0 817 my( $self, $tree, $true, $false ) = @_;
263              
264 112         676 return $self->visit_map( \%dispatch_cond, $tree, $true, $false );
265             }
266              
267             sub dispatch_regex {
268 53     53 0 127 my( $self, $tree, $true, $false ) = @_;
269              
270 53         231 return $self->visit_map( \%dispatch_regex, $tree, $true, $false );
271             }
272              
273             my %conditionals =
274             ( OP_NUM_LT() => OP_JUMP_IF_F_LT,
275             OP_STR_LT() => OP_JUMP_IF_S_LT,
276             OP_NUM_GT() => OP_JUMP_IF_F_GT,
277             OP_STR_GT() => OP_JUMP_IF_S_GT,
278             OP_NUM_LE() => OP_JUMP_IF_F_LE,
279             OP_STR_LE() => OP_JUMP_IF_S_LE,
280             OP_NUM_GE() => OP_JUMP_IF_F_GE,
281             OP_STR_GE() => OP_JUMP_IF_S_GE,
282             OP_NUM_EQ() => OP_JUMP_IF_F_EQ,
283             OP_STR_EQ() => OP_JUMP_IF_S_EQ,
284             OP_NUM_NE() => OP_JUMP_IF_F_NE,
285             OP_STR_NE() => OP_JUMP_IF_S_NE,
286             );
287              
288             my %builtins_no_list = map { $_ => 1 }
289             ( OP_ABS, OP_DEFINED, OP_UNDEF, OP_WANTARRAY );
290              
291             sub _indirect {
292 112     112   578 my( $self, $tree ) = @_;
293 112         253 _emit_label( $self, $tree );
294              
295 112 50       394 if( $tree->indirect ) {
296 0         0 $self->dispatch( $tree->indirect );
297             } else {
298 112         764 _add_bytecode $self,
299             opcode_nm( OP_GLOBAL, name => 'STDOUT', slot => VALUE_HANDLE );
300             }
301              
302 112         1067 foreach my $arg ( @{$tree->arguments} ) {
  112         344  
303 112         668 $self->dispatch( $arg );
304             }
305              
306 112         370 _add_bytecode $self,
307 112         1125 opcode_nm( OP_MAKE_LIST, count => @{$tree->arguments} + 1 ),
308             opcode_n( $tree->function );
309             }
310              
311             sub _builtin {
312 42     42   95 my( $self, $tree ) = @_;
313              
314 42 100 66     133 if( $tree->function == OP_UNDEF && !$tree->arguments ) {
    100          
315 2         22 _emit_label( $self, $tree );
316 2         6 _add_bytecode $self, opcode_n( OP_CONSTANT_UNDEF );
317             } elsif( $builtins_no_list{$tree->function} ) {
318 18         242 _emit_label( $self, $tree );
319 18 100       25 foreach my $arg ( @{$tree->arguments || []} ) {
  18         52  
320 12         96 $self->dispatch( $arg );
321             }
322              
323 18         197 _add_bytecode $self, opcode_n( $tree->function );
324             } else {
325 22         352 return _function_call( $self, $tree );
326             }
327             }
328              
329             sub _function_call {
330 66     66   131 my( $self, $tree ) = @_;
331 66         128 _emit_label( $self, $tree );
332              
333 66 100       100 foreach my $arg ( @{$tree->arguments || []} ) {
  66         195  
334 41         310 $self->dispatch( $arg );
335             }
336              
337 66 100       203 _add_bytecode $self,
338 66         724 opcode_nm( OP_MAKE_LIST, count => scalar @{$tree->arguments || []} );
339              
340 66 100       678 if( ref( $tree->function ) ) {
341 44         290 $self->dispatch( $tree->function );
342 44         547 _add_bytecode $self,
343             opcode_nm( OP_CALL, context => $tree->get_attribute( 'context' ) & CXT_CALL_MASK );
344             } else {
345 22 50       187 if( $tree->function == OP_RETURN ) {
346 22         175 my $block = $self->_current_block;
347 22         145 while( $block ) {
348 27         79 _exit_scope( $self, $block );
349 27 100       165 last if $block->{is_sub};
350 5         13 $block = $block->{outer};
351             }
352             }
353              
354 22         83 _add_bytecode $self, opcode_n( $tree->function );
355             }
356             }
357              
358             sub _list {
359 18     18   36 my( $self, $tree ) = @_;
360 18         46 _emit_label( $self, $tree );
361              
362 18         33 foreach my $arg ( @{$tree->expressions} ) {
  18         623  
363 39         734 $self->dispatch( $arg );
364             }
365              
366 18         61 _add_bytecode $self,
367 18         301 opcode_nm( OP_MAKE_LIST, count => @{$tree->expressions} + 0 );
368             }
369              
370             sub _unary_op {
371 15     15   29 my( $self, $tree ) = @_;
372 15         39 _emit_label( $self, $tree );
373              
374 15         55 $self->dispatch( $tree->left );
375              
376 15         167 _add_bytecode $self, opcode_n( $tree->op );
377             }
378              
379             sub _local {
380 14     14   25 my( $self, $tree ) = @_;
381 14         33 _emit_label( $self, $tree );
382              
383 14 50       54 die "Can only localize global for now"
384             unless $tree->left->isa( 'Language::P::ParseTree::Symbol' );
385              
386 14         511 my $index = $self->{_temporary_count}++;
387 14         42 _add_bytecode $self,
388             opcode_nm( OP_LOCALIZE_GLOB_SLOT,
389             name => $tree->left->name,
390             slot => $tree->left->sigil,
391             index => $index,
392             );
393              
394 14         129 push @{$self->_current_block->{bytecode}},
  14         39  
395             [ opcode_nm( OP_RESTORE_GLOB_SLOT,
396             name => $tree->left->name,
397             slot => $tree->left->sigil,
398             index => $index,
399             ),
400             ];
401             }
402              
403             sub _parentheses {
404 5     5   11 my( $self, $tree ) = @_;
405              
406 5         21 $self->dispatch( $tree->left );
407             }
408              
409             sub _binary_op {
410 165     165   261 my( $self, $tree ) = @_;
411 165         390 _emit_label( $self, $tree );
412              
413 165 100 100     547 if( $tree->op == OP_LOG_AND || $tree->op == OP_LOG_OR ) {
    100          
414 19         213 $self->dispatch( $tree->left );
415              
416 19         218 my( $right, $end ) = _new_blocks( $self, 2 );
417              
418             # jump to $end if evalutating right is not necessary
419 19         88 _add_bytecode $self,
420             opcode_n( OP_DUP );
421 19 100       208 _add_jump $self,
422             opcode_nm( OP_JUMP_IF_TRUE,
423             $tree->op == OP_LOG_AND ?
424             ( true => $right, false => $end ) :
425             ( true => $end, false => $right ) ),
426             $right, $end;
427              
428 19         212 _add_blocks $self, $right;
429              
430             # evalutates right only if this is the correct return value
431 19         133 _add_bytecode $self, opcode_n( OP_POP );
432 19         191 $self->dispatch( $tree->right );
433 19         229 _add_jump $self, opcode_nm( OP_JUMP, to => $end ), $end;
434 19         193 _add_blocks $self, $end;
435             } elsif( $tree->op == OP_ASSIGN ) {
436 106         2514 $self->dispatch( $tree->right );
437 106         1509 $self->dispatch( $tree->left );
438              
439 106         1415 _add_bytecode $self,
440             opcode_n( OP_SWAP ),
441             opcode_n( $tree->op );
442             } else {
443 40         699 $self->dispatch( $tree->left );
444 40         494 $self->dispatch( $tree->right );
445              
446 40         770 _add_bytecode $self, opcode_n( $tree->op );
447             }
448             }
449              
450             sub _binary_op_cond {
451 89     89   166 my( $self, $tree, $true, $false ) = @_;
452              
453 89 100 66     271 if( $tree->op == OP_LOG_AND || $tree->op == OP_LOG_OR ) {
    100          
454 3         25 my $right = _new_block( $self );
455              
456 3 50       14 $self->dispatch_cond( $tree->left,
457             $tree->op == OP_LOG_AND ?
458             ( $right, $false ) :
459             ( $true, $right ) );
460              
461 3         31 _add_blocks $self, $right;
462              
463             # evalutates right only if this is the correct return value
464 3         30 $self->dispatch_cond( $tree->right, $true, $false );
465              
466 3         30 return;
467             } elsif( !$conditionals{$tree->op} ) {
468 1         22 _anything_cond( $self, $tree, $true, $false );
469              
470 1         10 return;
471             }
472              
473 85         1676 _emit_label( $self, $tree );
474 85         276 $self->dispatch( $tree->left );
475 85         962 $self->dispatch( $tree->right );
476              
477 85         1060 _add_jump $self, opcode_nm( $conditionals{$tree->op},
478             true => $true, false => $false ), $true, $false;
479             }
480              
481             sub _anything_cond {
482 24     24   45 my( $self, $tree, $true, $false ) = @_;
483              
484 24         72 $self->dispatch( $tree );
485              
486 24         285 _add_jump $self, opcode_nm( OP_JUMP_IF_TRUE, true => $true, false => $false ), $true, $false;
487             }
488              
489             sub _constant {
490 436     436   590 my( $self, $tree ) = @_;
491 436         770 _emit_label( $self, $tree );
492 436         656 my $v;
493              
494 436 100       1373 if( $tree->is_number ) {
    50          
495 242 100       745 if( $tree->flags & NUM_INTEGER ) {
    50          
    0          
    0          
    0          
496 240         2443 _add_bytecode $self,
497             opcode_n( OP_CONSTANT_INTEGER, $tree->value );
498             } elsif( $tree->flags & NUM_FLOAT ) {
499 2         28 _add_bytecode $self,
500             opcode_n( OP_CONSTANT_FLOAT, $tree->value );
501             } elsif( $tree->flags & NUM_OCTAL ) {
502 0         0 _add_bytecode $self,
503             opcode_n( OP_CONSTANT_INTEGER, oct '0' . $tree->value );
504             } elsif( $tree->flags & NUM_HEXADECIMAL ) {
505 0         0 _add_bytecode $self,
506             opcode_n( OP_CONSTANT_INTEGER, oct '0x' . $tree->value );
507             } elsif( $tree->flags & NUM_BINARY ) {
508 0         0 _add_bytecode $self,
509             opcode_n( OP_CONSTANT_INTEGER, oct '0b' . $tree->value );
510             } else {
511 0         0 die "Unhandled flags value";
512             }
513             } elsif( $tree->is_string ) {
514 194         557 _add_bytecode $self,
515             opcode_n( OP_CONSTANT_STRING, $tree->value );
516             } else {
517 0         0 die "Neither number nor string";
518             }
519             }
520              
521             sub _symbol {
522 309     309   572 my( $self, $tree ) = @_;
523 309         608 _emit_label( $self, $tree );
524              
525 309         1052 _add_bytecode $self,
526             opcode_nm( OP_GLOBAL, name => $tree->name, slot => $tree->sigil );
527             }
528              
529             sub _lexical_symbol {
530 22     22   41 my( $self, $tree ) = @_;
531 22         42 _emit_label( $self, $tree );
532              
533 22         91 _do_lexical_access( $self, $tree->declaration, $tree->level, 0 );
534             }
535              
536             sub _lexical_declaration {
537 8     8   14 my( $self, $tree ) = @_;
538 8         30 _emit_label( $self, $tree );
539              
540 8         22 _do_lexical_access( $self, $tree, 0, 1 );
541             }
542              
543             sub _do_lexical_access {
544 30     30   195 my( $self, $tree, $level, $is_decl ) = @_;
545              
546             # maybe to it while parsing, in _find_symbol/_process_lexical_declaration
547 30   100     99 my $lex_info = $self->_code_segments->[0]->lexicals->{$tree}
548             ||= { level => $level, lexical => $tree };
549              
550 30         463 _add_bytecode $self,
551             opcode_nm( OP_LEXICAL,
552             lexical => $tree,
553             level => $level,
554             );
555              
556 30 100       339 if( $is_decl ) {
557 8         24 $lex_info->{declaration} = 1;
558              
559 8         13 push @{$self->_current_block->{bytecode}},
  8         58  
560             [ opcode_nm( OP_LEXICAL_CLEAR,
561             lexical => $tree,
562             level => $level,
563             ),
564             ];
565             }
566             }
567              
568             sub _cond_loop {
569 12     12   26 my( $self, $tree ) = @_;
570 12         34 _emit_label( $self, $tree );
571              
572 12         64 my $is_until = $tree->block_type eq 'until';
573 12         82 my( $start_cond, $start_loop, $start_continue, $end_loop ) = _new_blocks( $self, 4 );
574 12 100       52 $tree->set_attribute( 'lbl_next', $tree->continue ? $start_continue :
575             $start_cond );
576 12         38 $tree->set_attribute( 'lbl_last', $end_loop );
577 12         33 $tree->set_attribute( 'lbl_redo', $start_loop );
578              
579 12         36 _add_jump $self,
580             opcode_nm( OP_JUMP, to => $start_cond ), $start_cond;
581              
582 12         184 $self->push_block;
583              
584 12         56 _add_blocks $self, $start_cond;
585 12 100       86 $self->dispatch_cond( $tree->condition,
586             $is_until ? ( $end_loop, $start_loop ) :
587             ( $start_loop, $end_loop ) );
588              
589 12         136 _add_blocks $self, $start_loop;
590 12         87 $self->dispatch( $tree->block );
591 12 50       219 _discard_if_void( $self, $tree->block )
592             unless $tree->block->isa( 'Language::P::ParseTree::Block' );
593              
594 12 100       143 if( $tree->continue ) {
595 4         30 _add_jump $self, opcode_nm( OP_JUMP, to => $start_continue ), $start_continue;
596              
597 4         39 _add_blocks $self, $start_continue;
598 4         31 $self->dispatch( $tree->continue );
599             }
600              
601 12         75 _add_jump $self, opcode_nm( OP_JUMP, to => $start_cond ), $start_cond;
602              
603 12         128 _add_blocks $self, $end_loop;
604 12         88 _exit_scope( $self, $self->_current_block );
605 12         41 $self->pop_block;
606             }
607              
608             sub _foreach {
609 7     7   13 my( $self, $tree ) = @_;
610 7         35 _emit_label( $self, $tree );
611              
612 7         27 my $is_lexical = $tree->variable->isa( 'Language::P::ParseTree::LexicalDeclaration' );
613              
614 7         123 my( $start_step, $start_loop, $start_continue, $exit_loop, $end_loop ) =
615             _new_blocks( $self, 5 );
616 7 100       31 $tree->set_attribute( 'lbl_next', $tree->continue ? $start_continue :
617             $start_step );
618 7         24 $tree->set_attribute( 'lbl_last', $end_loop );
619 7         21 $tree->set_attribute( 'lbl_redo', $start_loop );
620              
621 7         21 $self->push_block;
622              
623 7         42 $self->dispatch( $tree->expression );
624 7         80 _add_bytecode $self, opcode_nm( OP_MAKE_LIST, count => 1 );
625              
626 7         69 my $iterator = $self->{_temporary_count}++;
627 7         21 my( $glob, $slot );
628 7         28 _add_bytecode $self,
629             opcode_nm( OP_ITERATOR ),
630             opcode_nm( OP_TEMPORARY_SET, index => $iterator );
631              
632 7 100       79 if( !$is_lexical ) {
633 4         12 $glob = $self->{_temporary_count}++;
634 4         8 $slot = $self->{_temporary_count}++;
635              
636 4         31 _add_bytecode $self,
637             opcode_nm( OP_GLOBAL, name => $tree->variable->name, slot => VALUE_GLOB ),
638             opcode_n( OP_DUP ),
639             opcode_nm( OP_GLOB_SLOT, slot => VALUE_SCALAR ),
640             opcode_nm( OP_TEMPORARY_SET, index => $slot ),
641             opcode_nm( OP_TEMPORARY_SET, index => $glob );
642              
643 4         55 push @{$self->_current_block->{bytecode}},
  4         16  
644             [ opcode_nm( OP_TEMPORARY, index => $glob ),
645             opcode_nm( OP_TEMPORARY, index => $slot ),
646             opcode_nm( OP_GLOB_SLOT_SET, slot => VALUE_SCALAR ),
647             ];
648             }
649              
650 7         66 _add_jump $self, opcode_nm( OP_JUMP, to => $start_step ), $start_step;
651 7         396 _add_blocks $self, $start_step;
652              
653 7 100       55 if( !$is_lexical ) {
654 4         16 _add_bytecode $self,
655             opcode_nm( OP_TEMPORARY, index => $iterator ),
656             opcode_nm( OP_ITERATOR_NEXT ),
657             opcode_n( OP_DUP );
658 4         45 _add_jump $self,
659             opcode_nm( OP_JUMP_IF_NULL, true => $exit_loop, false => $start_loop ), $exit_loop, $start_loop;
660              
661 4         38 _add_blocks $self, $start_loop;
662 4         30 _add_bytecode $self,
663             opcode_nm( OP_TEMPORARY, index => $glob ),
664             opcode_n( OP_SWAP ),
665             opcode_nm( OP_GLOB_SLOT_SET, slot => VALUE_SCALAR );
666             } else {
667 3         14 _add_bytecode $self,
668             opcode_nm( OP_TEMPORARY, index => $iterator ),
669             opcode_n( OP_ITERATOR_NEXT ),
670             opcode_n( OP_DUP );
671 3         39 _add_jump $self,
672             opcode_nm( OP_JUMP_IF_NULL, true => $exit_loop, false => $start_loop ), $exit_loop, $start_loop;
673              
674 3         34 _add_blocks $self, $start_loop;
675 3         23 _add_bytecode $self,
676             opcode_nm( OP_LEXICAL_SET, lexical => $tree->variable );
677              
678 3         35 $self->_code_segments->[0]->lexicals->{$tree->variable}
679             = { level => 0,
680             lexical => $tree->variable,
681             };
682             }
683              
684 7         117 $self->dispatch( $tree->block );
685 7 50       28 _discard_if_void( $self, $tree->block )
686             unless $tree->block->isa( 'Language::P::ParseTree::Block' );
687              
688 7 100       83 if( $tree->continue ) {
689 1         11 _add_jump $self, opcode_nm( OP_JUMP, to => $start_continue ), $start_continue;
690              
691 1         12 _add_blocks $self, $start_continue;
692 1         18 $self->dispatch( $tree->continue );
693             }
694              
695 7         47 _add_jump $self, opcode_nm( OP_JUMP, to => $start_step ), $start_step;
696              
697 7         71 _add_blocks $self, $exit_loop;
698 7         49 _add_bytecode $self, opcode_n( OP_POP );
699 7         83 _add_jump $self, opcode_nm( OP_JUMP, to => $end_loop ), $end_loop;
700 7         71 _add_blocks $self, $end_loop;
701              
702 7         55 _exit_scope( $self, $self->_current_block );
703 7         51 $self->pop_block;
704             }
705              
706             sub _for {
707 4     4   10 my( $self, $tree ) = @_;
708 4         15 _emit_label( $self, $tree );
709              
710 4         205 my( $start_cond, $start_loop, $start_step, $end_loop ) = _new_blocks( $self, 4 );
711 4         45 $tree->set_attribute( 'lbl_next', $start_step );
712 4         16 $tree->set_attribute( 'lbl_last', $end_loop );
713 4         13 $tree->set_attribute( 'lbl_redo', $start_loop );
714              
715 4         20 $self->push_block;
716              
717 4         28 $self->dispatch( $tree->initializer );
718 4         54 _discard_if_void( $self, $tree->initializer );
719              
720 4         52 _add_jump $self,
721             opcode_nm( OP_JUMP, to => $start_cond ), $start_cond;
722 4         49 _add_blocks $self, $start_cond;
723              
724 4         36 $self->dispatch_cond( $tree->condition, $start_loop, $end_loop );
725              
726 4         54 _add_blocks $self, $start_loop;
727 4         32 $self->dispatch( $tree->block );
728 4 50       19 _discard_if_void( $self, $tree->block )
729             unless $tree->block->isa( 'Language::P::ParseTree::Block' );
730              
731 4         72 _add_jump $self,
732             opcode_nm( OP_JUMP, to => $start_step ), $start_step;
733              
734 4         42 _add_blocks $self, $start_step;
735 4         33 $self->dispatch( $tree->step );
736 4         59 _discard_if_void( $self, $tree->step );
737 4         232 _add_jump $self, opcode_nm( OP_JUMP, to => $start_cond ), $start_cond;
738              
739 4         47 _add_blocks $self, $end_loop;
740 4         31 _exit_scope( $self, $self->_current_block );
741 4         13 $self->pop_block;
742             }
743              
744             sub _cond {
745 19     19   88 my( $self, $tree ) = @_;
746 19         48 _emit_label( $self, $tree );
747              
748 19         47 $self->push_block;
749              
750 19         79 my @blocks;
751 19         61 my $current = $self->_code_segments->[0]->basic_blocks->[-1];
752 19         298 push @blocks, _new_block( $self );
753 19 100       80 if( $tree->iffalse ) {
754 6         39 push @blocks, _new_block( $self );
755 6         25 _current_basic_block( $self, $blocks[-1] );
756 6         42 $self->dispatch( $tree->iffalse->block );
757 6         22 _add_jump $self, opcode_nm( OP_JUMP, to => $blocks[0] ), $blocks[0];
758             }
759 19         142 foreach my $elsif ( reverse @{$tree->iftrues} ) {
  19         70  
760 23         136 my $next = $blocks[-1];
761 23         75 my $is_unless = $elsif->block_type eq 'unless';
762 23         231 my( $cond_block, $then_block ) = _new_blocks( $self, 2 );
763 23         487 _current_basic_block( $self, $cond_block );
764 23 100       166 $self->dispatch_cond( $elsif->condition,
765             $is_unless ? ( $next, $then_block ) :
766             ( $then_block, $next ) );
767 23         295 push @blocks, $then_block, $cond_block;
768 23         61 _current_basic_block( $self, $then_block );
769 23         157 $self->dispatch( $elsif->block );
770 23 100       143 _discard_if_void( $self, $elsif->block )
771             unless $elsif->block->isa( 'Language::P::ParseTree::Block' );
772              
773 23         173 _add_jump $self, opcode_nm( OP_JUMP, to => $blocks[0] ), $blocks[0];
774             }
775              
776 19         227 $current->add_jump( opcode_nm( OP_JUMP, to => $blocks[-1] ), $blocks[-1] );
777 19         133 _add_blocks $self, reverse @blocks;
778              
779 19         137 _exit_scope( $self, $self->_current_block );
780 19         48 $self->pop_block;
781             }
782              
783             sub _ternary {
784 67     67   110 my( $self, $tree ) = @_;
785 67         181 _emit_label( $self, $tree );
786              
787 67         180 my( $end, $true, $false ) = _new_blocks( $self, 3 );
788 67         481 $self->dispatch_cond( $tree->condition, $true, $false );
789              
790 67         870 _add_blocks $self, $true;
791 67         687 $self->dispatch( $tree->iftrue );
792 67         830 _add_jump $self, opcode_nm( OP_JUMP, to => $end ), $end;
793              
794 67         789 _add_blocks $self, $false;
795 67         498 $self->dispatch( $tree->iffalse );
796 67         719 _add_jump $self, opcode_nm( OP_JUMP, to => $end ), $end;
797              
798 67         703 _add_blocks $self, $end;
799             }
800              
801             sub _block {
802 49     49   84 my( $self, $tree ) = @_;
803 49         100 _emit_label( $self, $tree );
804              
805 49         126 $self->push_block;
806              
807 49         190 foreach my $line ( @{$tree->lines} ) {
  49         208  
808 64         587 $self->dispatch( $line );
809 64         674 _discard_if_void( $self, $line );
810             }
811              
812 49         798 _exit_scope( $self, $self->_current_block );
813 49         167 $self->pop_block;
814             }
815              
816             sub _bare_block {
817 7     7   14 my( $self, $tree ) = @_;
818 7         23 _emit_label( $self, $tree );
819              
820 7         23 my( $start_loop, $start_continue, $end_loop ) = _new_blocks( $self, 3 );
821 7         59 $tree->set_attribute( 'lbl_next', $end_loop );
822 7         25 $tree->set_attribute( 'lbl_last', $end_loop );
823 7         109 $tree->set_attribute( 'lbl_redo', $start_loop );
824              
825 7         269 _add_jump $self,
826             opcode_nm( OP_JUMP, to => $start_loop ), $start_loop;
827 7         81 _add_blocks $self, $start_loop;
828              
829 7         52 $self->push_block;
830              
831 7         30 foreach my $line ( @{$tree->lines} ) {
  7         29  
832 34         264 $self->dispatch( $line );
833 34         381 _discard_if_void( $self, $line );
834             }
835              
836 7         68 _exit_scope( $self, $self->_current_block );
837 7         62 $self->pop_block;
838              
839 7 100       37 if( $tree->continue ) {
840 2         18 _add_jump $self, opcode_nm( OP_JUMP, to => $start_continue ), $start_continue;
841              
842 2         23 _add_blocks $self, $start_continue;
843 2         16 $self->dispatch( $tree->continue );
844             }
845              
846 7         64 _add_jump $self,
847             opcode_nm( OP_JUMP, to => $end_loop ), $end_loop;
848 7         242 _add_blocks $self, $end_loop;
849             }
850              
851             sub _subroutine_decl {
852 0     0   0 my( $self, $tree ) = @_;
853              
854             # nothing to do
855             }
856              
857             sub _anon_subroutine {
858 4     4   8 my( $self, $tree ) = @_;
859 4         13 my $sub = _subroutine( $self, $tree );
860              
861 4         16 _add_bytecode $self,
862             opcode_n( OP_CONSTANT_SUB, $sub ),
863             opcode_n( OP_MAKE_CLOSURE );
864             }
865              
866             sub _subroutine {
867 21     21   408 my( $self, $tree ) = @_;
868 21         53 _emit_label( $self, $tree );
869              
870 21         172 my $generator = Language::P::Intermediate::Generator->new
871 21         37 ( { _options => { %{$self->{_options}},
872             # performed by caller
873             'dump-ir' => 0,
874             },
875             } );
876 21         98 my $code_segments =
877             _generate_bytecode( $generator, 1, $tree->name,
878             $self->_code_segments->[0], $tree->lines );
879 21         127 push @{$self->_code_segments}, @$code_segments;
  21         71  
880              
881 21         447 return $code_segments->[0];
882             }
883              
884             sub _quoted_string {
885 25     25   55 my( $self, $tree ) = @_;
886 25         66 _emit_label( $self, $tree );
887              
888 25 50       38 if( @{$tree->components} == 1 ) {
  25         108  
889 0         0 $self->dispatch( $tree->components->[0] );
890              
891 0         0 _add_bytecode $self, opcode_n( OP_STRINGIFY );
892              
893 0         0 return;
894             }
895              
896 25         197 _add_bytecode $self, opcode_n( OP_FRESH_STRING, '' );
897 25         314 for( my $i = 0; $i < @{$tree->components}; ++$i ) {
  102         911  
898 77         479 $self->dispatch( $tree->components->[$i] );
899              
900 77         912 _add_bytecode $self, opcode_n( OP_CONCAT_ASSIGN );
901             }
902             }
903              
904             sub _subscript {
905 6     6   13 my( $self, $tree ) = @_;
906 6         17 _emit_label( $self, $tree );
907              
908 6 50       24 die if $tree->reference;
909              
910 6         48 $self->dispatch( $tree->subscript );
911 6         80 $self->dispatch( $tree->subscripted );
912              
913 6 50       50 if( $tree->type == VALUE_ARRAY ) {
    0          
914 6         57 _add_bytecode $self, opcode_n( OP_ARRAY_ELEMENT );
915             } elsif( $tree->type == VALUE_HASH ) {
916 0         0 _add_bytecode $self, opcode_n( OP_HASH_ELEMENT );
917             } else {
918 0         0 die $tree->type;
919             }
920             }
921              
922             # find the node that is the target of a goto or the loop node that
923             # last/redo/next controls
924             sub _find_jump_target {
925 15     15   22 my( $self, $node ) = @_;
926 15 100       74 return $node->get_attribute( 'target' ) if $node->has_attribute( 'target' );
927 12 50       38 return if ref $node->left; # dynamic jump
928 12 50       104 return if $node->op == OP_GOTO;
929              
930             # search for the closest loop (for unlabeled jumps) or the closest
931             # loop with matching label
932 12         80 my $target_label = $node->left;
933 12         64 while( $node ) {
934 40         134 $node = $node->parent;
935 40 50       462 last if $node->isa( 'Language::P::ParseTree::Subroutine' );
936 40 100       135 next unless $node->is_loop;
937             # found loop
938 14 100       46 return $node if !$target_label;
939 5 50       14 next unless $node->has_attribute( 'label' );
940 5 100       16 return $node if $node->get_attribute( 'label' ) eq $target_label;
941             }
942              
943 0         0 return;
944             }
945              
946             # number of blocks to unwind when jumping out of a loop/nested scope
947             sub _unwind_level {
948 15     15   26 my( $self, $node, $to_outer ) = @_;
949 15         30 my $level = 0;
950              
951 15   100     140 while( $node && ( !$to_outer || $node != $to_outer ) ) {
      66        
952 44 100 100     519 ++$level if $node->isa( 'Language::P::ParseTree::Block' )
953             && !$node->isa( 'Language::P::ParseTree::BareBlock' );
954 44 100       139 ++$level if $node->is_loop;
955 44         99 $node = $node->parent;
956             }
957              
958 15         36 return $level;
959             }
960              
961             # find the common ancestor of two nodes (assuming they are in the same
962             # subroutine)
963             sub _find_ancestor {
964 3     3   435 my( $self, $from, $to ) = @_;
965 3         6 my %parents;
966              
967 3         13 for( my $node = $from; $node; $node = $node->parent ) {
968 6         21 $parents{$node} = 1;
969 6 50       90 last if $node->isa( 'Language::P::ParseTree::Subroutine' );
970             }
971              
972 3         11 for( my $node = $to; $node; $node = $node->parent ) {
973 7 100       26 return $node if $parents{$node};
974 5 50       87 die "Can't happen" if $node->isa( 'Language::P::ParseTree::Subroutine' );
975             }
976              
977 1         4 return;
978             }
979              
980             sub _jump {
981 15     15   30 my( $self, $tree ) = @_;
982 15         62 my $target = _find_jump_target( $self, $tree );
983              
984 15 50       40 die "Jump without static target" unless $target; # requires stack unwinding
985              
986 15 100       50 my $unwind_to = $tree->op == OP_GOTO ?
987             _find_ancestor( $self, $tree, $target ) :
988             $target;
989 15         231 my $level = _unwind_level( $self, $tree, $unwind_to );
990              
991 15         52 my $block = $self->_current_block;
992 15         92 foreach ( 1 .. $level ) {
993 13         34 _exit_scope( $self, $block );
994 13         57 $block = $block->{outer};
995             }
996              
997 15         26 my $label_to;
998 15 100       49 if( $tree->op == OP_GOTO ) {
999 3         21 $label_to = $target->get_attribute( 'lbl_label' );
1000 3 50       10 if( !$label_to ) {
1001 3         9 $target->set_attribute( 'lbl_label', $label_to = _new_block( $self ) );
1002             }
1003             } else {
1004 12 100       73 my $label = $tree->op == OP_NEXT ? 'lbl_next' :
    100          
1005             $tree->op == OP_LAST ? 'lbl_last' :
1006             'lbl_redo';
1007 12 50       156 $label_to = $target->get_attribute( $label )
1008             or die "Missing loop control label";
1009             }
1010              
1011 15         57 _add_jump $self, opcode_nm( OP_JUMP, to => $label_to ), $label_to;
1012 15         110 _add_blocks( $self, _new_block( $self ) );
1013             }
1014              
1015             sub _emit_label {
1016 1487     1487   1906 my( $self, $tree ) = @_;
1017 1487 100       6472 return unless $tree->has_attribute( 'label' );
1018              
1019 7 100       24 if( !$tree->has_attribute( 'lbl_label' ) ) {
1020 4         9 $tree->set_attribute( 'lbl_label', _new_block( $self ) );
1021             }
1022              
1023 7         24 my $to = $tree->get_attribute( 'lbl_label' );
1024 7         25 _add_jump $self, opcode_nm( OP_JUMP, to => $to ), $to;
1025 7         90 _add_blocks $self, $tree->get_attribute( 'lbl_label' );
1026             }
1027              
1028             sub _discard_if_void {
1029 351     351   1082 my( $self, $tree ) = @_;
1030 351   100     1651 my $context = ( $tree->get_attribute( 'context' ) || 0 ) & CXT_CALL_MASK;
1031 351 100       1074 return if $context != CXT_VOID;
1032              
1033 248         627 _add_bytecode $self, opcode_n( OP_POP );
1034             }
1035              
1036             sub _pattern {
1037 0     0   0 my( $self, $tree ) = @_;
1038 0         0 my $generator = Language::P::Intermediate::Generator->new
1039             ( { _options => $self->{_options},
1040             } );
1041              
1042 0         0 my $re = $generator->_generate_regex( $tree, $self->_code_segments->[0] );
1043 0         0 _add_bytecode $self, opcode_n( OP_CONSTANT_REGEX, $re->[0] );
1044             }
1045              
1046             sub _exit_scope {
1047 138     138   932 my( $self, $block ) = @_;
1048              
1049 138         195 foreach my $code ( reverse @{$block->{bytecode}} ) {
  138         383  
1050 30         159 _add_bytecode $self, @$code;
1051             }
1052             }
1053              
1054             my %regex_assertions =
1055             ( START_SPECIAL => OP_RX_START_SPECIAL,
1056             END_SPECIAL => OP_RX_END_SPECIAL,
1057             );
1058              
1059             sub _regex_assertion {
1060 7     7   15 my( $self, $tree ) = @_;
1061 7         101 my $type = $tree->type;
1062              
1063 7 50       57 die "Unsupported assertion '$type'" unless $regex_assertions{$type};
1064              
1065 7         26 _add_bytecode $self, opcode_n( $regex_assertions{$type} );
1066             }
1067              
1068             sub _regex_quantifier {
1069 11     11   23 my( $self, $tree ) = @_;
1070              
1071 11         28 my( $start, $quant, $end ) = _new_blocks( $self, 3 );
1072 11         42 _add_bytecode $self, opcode_nm( OP_RX_START_GROUP, to => $quant );
1073 11         101 _add_blocks $self, $start;
1074              
1075 11         76 my $is_group = $tree->node->isa( 'Language::P::ParseTree::RXGroup' );
1076 11 100       155 my $capture = $is_group ? $tree->node->capture : 0;
1077 11         60 my $start_group = $self->_group_count;
1078 11 100       68 $self->_group_count( $start_group + 1 ) if $capture;
1079              
1080 11 100       69 if( $capture ) {
1081 2         3 foreach my $c ( @{$tree->node->components} ) {
  2         7  
1082 2         18 $self->dispatch_regex( $c );
1083             }
1084             } else {
1085 9         28 $self->dispatch_regex( $tree->node );
1086             }
1087              
1088 11         174 _add_bytecode $self, opcode_nm( OP_JUMP, to => $quant );
1089 11         107 _add_blocks $self, $quant;
1090 11 100       75 _add_bytecode $self,
1091             opcode_nm( OP_RX_QUANTIFIER,
1092             min => $tree->min, max => $tree->max,
1093             greedy => $tree->greedy,
1094             group => ( $capture ? $start_group : undef ),
1095             subgroups_start => $start_group,
1096             subgroups_end => $self->_group_count,
1097             true => $start, false => $end );
1098 11         100 _add_blocks $self, $end;
1099             }
1100              
1101             sub _regex_group {
1102 5     5   10 my( $self, $tree ) = @_;
1103              
1104 5 50       18 if( $tree->capture ) {
1105 5         42 _add_bytecode $self,
1106             opcode_nm( OP_RX_CAPTURE_START, group => $self->_group_count );
1107             }
1108              
1109 5         47 foreach my $c ( @{$tree->components} ) {
  5         19  
1110 5         106 $self->dispatch_regex( $c );
1111             }
1112              
1113 5 50       60 if( $tree->capture ) {
1114 5         40 _add_bytecode $self,
1115             opcode_nm( OP_RX_CAPTURE_END, group => $self->_group_count );
1116 5         57 $self->_group_count( $self->_group_count + 1 );
1117             }
1118             }
1119              
1120             sub _regex_exact {
1121 28     28   49 my( $self, $tree ) = @_;
1122              
1123 28         95 _add_bytecode $self,
1124             opcode_nm( OP_RX_EXACT, string => $tree->value,
1125             length => length( $tree->value ) );
1126             }
1127              
1128             sub _regex_alternate {
1129 4     4   24 my( $self, $tree, $end ) = @_;
1130 4         14 my $is_last = !$tree->right->[0]
1131             ->isa( 'Language::P::ParseTree::RXAlternation' );
1132 4         42 my $next_l = _new_block( $self );
1133 4   66     20 $end ||= _new_block( $self );
1134              
1135 4         15 _add_bytecode $self, opcode_nm( OP_RX_TRY, to => $next_l );
1136              
1137 4         39 foreach my $c ( @{$tree->left} ) {
  4         13  
1138 4         27 $self->dispatch_regex( $c );
1139             }
1140              
1141 4         56 _add_bytecode $self, opcode_nm( OP_JUMP, to => $end );
1142 4         40 _add_blocks $self, $next_l;
1143              
1144 4 100       29 if( !$is_last ) {
1145 2         17 _regex_alternate( $self, $tree->right->[0], $end );
1146             } else {
1147 2         3 foreach my $c ( @{$tree->right} ) {
  2         7  
1148 2         12 $self->dispatch_regex( $c );
1149             }
1150              
1151 2         32 _add_bytecode $self, opcode_nm( OP_JUMP, to => $end );
1152 2         22 _add_blocks $self, $end;
1153             }
1154             }
1155              
1156             1;