File Coverage

blib/lib/Language/P/Toy/Generator.pm
Criterion Covered Total %
statement 232 241 96.2
branch 57 62 91.9
condition 9 11 81.8
subroutine 43 45 95.5
pod 1 8 12.5
total 342 367 93.1


line stmt bran cond sub pod time code
1             package Language::P::Toy::Generator;
2              
3 18     18   3010 use strict;
  18         39  
  18         794  
4 18     18   112 use warnings;
  18         39  
  18         656  
5 18     18   102 use base qw(Language::P::ParseTree::Visitor);
  18         35  
  18         9739  
6              
7             __PACKAGE__->mk_ro_accessors( qw(runtime) );
8             __PACKAGE__->mk_accessors( qw(_code _pending _block_map _temporary_map
9             _options _generated _intermediate) );
10              
11 18     18   12676 use Language::P::Intermediate::Generator;
  18         83  
  18         410  
12 18     18   968 use Language::P::Opcodes qw(:all);
  18         48  
  18         21246  
13 18     18   13503 use Language::P::Toy::Opcodes qw(o);
  18         62  
  18         1833  
14 18     18   230 use Language::P::Toy::Value::StringNumber;
  18         36  
  18         190  
15 18     18   535 use Language::P::Toy::Value::Handle;
  18         38  
  18         166  
16 18     18   15584 use Language::P::Toy::Value::ScratchPad;
  18         55  
  18         192  
17 18     18   570 use Language::P::Toy::Value::Code;
  18         38  
  18         235  
18 18     18   10069 use Language::P::Toy::Value::Regex;
  18         56  
  18         257  
19 18     18   612 use Language::P::ParseTree qw(:all);
  18         37  
  18         53203  
20 18     18   164 use Language::P::Keywords qw(:all);
  18         41  
  18         95894  
21              
22             my %sigil_to_slot =
23             ( VALUE_SCALAR() => 'scalar',
24             VALUE_SUB() => 'subroutine',
25             VALUE_ARRAY() => 'array',
26             VALUE_HANDLE() => 'io',
27             );
28              
29             sub new {
30 18     18 1 80 my( $class, $args ) = @_;
31 18         274 my $self = $class->SUPER::new( $args );
32              
33 18         141 $self->_options( {} );
34 18         516 $self->_intermediate( Language::P::Intermediate::Generator->new
35             ( { file_name => 'a.ir',
36             } ) );
37              
38 18         141 return $self;
39             }
40              
41             sub set_option {
42 0     0 0 0 my( $self, $option, $value ) = @_;
43              
44 0 0       0 if( $option eq 'dump-ir' ) {
45 0         0 $self->_options->{$option} = 1;
46 0         0 $self->_intermediate->set_option( 'dump-ir' );
47             }
48              
49 0         0 return 0;
50             }
51              
52             sub _add {
53 0     0   0 my( $self, @bytecode ) = @_;
54              
55 0         0 push @{$self->_code->bytecode}, @bytecode;
  0         0  
56             }
57              
58             sub process {
59 142     142 0 1673 my( $self, $tree ) = @_;
60              
61 142         186 push @{$self->{_pending}}, $tree;
  142         355  
62              
63 142         524 return;
64             }
65              
66             sub add_declaration {
67 13     13 0 95 my( $self, $name ) = @_;
68              
69 13         148 my $sub = Language::P::Toy::Value::Subroutine::Stub->new
70             ( { name => $name,
71             } );
72 13         79 $self->runtime->symbol_table->set_symbol( $name, '&', $sub );
73             }
74              
75             my %opcode_map =
76             ( OP_GLOBAL() => \&_global,
77             OP_LEXICAL() => \&_lexical,
78             OP_LEXICAL_CLEAR() => \&_lexical_clear,
79             OP_CONSTANT_STRING() => \&_const_string,
80             OP_FRESH_STRING() => \&_fresh_string,
81             OP_CONSTANT_INTEGER() => \&_const_integer,
82             OP_CONSTANT_FLOAT() => \&_const_float,
83             OP_CONSTANT_UNDEF() => \&_const_undef,
84             OP_CONSTANT_SUB() => \&_const_codelike,
85             OP_CONSTANT_REGEX() => \&_const_codelike,
86             OP_JUMP_IF_TRUE() => \&_cond_jump_simple,
87             OP_JUMP_IF_FALSE() => \&_cond_jump_simple,
88             OP_JUMP_IF_F_LT() => \&_cond_jump_simple,
89             OP_JUMP_IF_S_LT() => \&_cond_jump_simple,
90             OP_JUMP_IF_F_GT() => \&_cond_jump_simple,
91             OP_JUMP_IF_S_GT() => \&_cond_jump_simple,
92             OP_JUMP_IF_F_LE() => \&_cond_jump_simple,
93             OP_JUMP_IF_S_LE() => \&_cond_jump_simple,
94             OP_JUMP_IF_F_GE() => \&_cond_jump_simple,
95             OP_JUMP_IF_S_GE() => \&_cond_jump_simple,
96             OP_JUMP_IF_F_EQ() => \&_cond_jump_simple,
97             OP_JUMP_IF_S_EQ() => \&_cond_jump_simple,
98             OP_JUMP_IF_F_NE() => \&_cond_jump_simple,
99             OP_JUMP_IF_S_NE() => \&_cond_jump_simple,
100             OP_JUMP_IF_NULL() => \&_cond_jump_simple,
101             OP_JUMP() => \&_direct_jump,
102             OP_TEMPORARY() => \&_temporary,
103             OP_TEMPORARY_SET() => \&_temporary_set,
104             OP_LOCALIZE_GLOB_SLOT() => \&_map_slot_index,
105             OP_RESTORE_GLOB_SLOT() => \&_map_slot_index,
106             OP_END() => \&_end,
107              
108             OP_RX_QUANTIFIER() => \&_rx_quantifier,
109             OP_RX_START_GROUP() => \&_direct_jump,
110             OP_RX_TRY() => \&_direct_jump,
111             );
112              
113             sub _generate_segment {
114 46     46   109 my( $self, $segment, $outer ) = @_;
115 46         235 my $is_sub = $segment->is_sub;
116 46         203 my $is_regex = $segment->is_regex;
117 46         384 my $pad = Language::P::Toy::Value::ScratchPad->new;
118              
119 46         116 my $code;
120 46 100       539 if( $is_sub ) {
    100          
121 15         59 $code = Language::P::Toy::Value::Subroutine->new
122             ( { bytecode => [],
123             name => $segment->name,
124             lexicals => $pad,
125             outer => $outer,
126             } );
127             } elsif( $is_regex ) {
128 16         196 $code = Language::P::Toy::Value::Regex->new
129             ( { bytecode => [],
130             stack_size => 0,
131             } );
132             } else {
133 15         236 $code = Language::P::Toy::Value::Code->new
134             ( { bytecode => [],
135             lexicals => $pad,
136             outer => $outer,
137             } );
138             }
139              
140 46         235 $self->_generated->{$segment} = $code;
141              
142 46         325 foreach my $inner ( @{$segment->inner} ) {
  46         199  
143 15         192 _generate_segment( $self, $inner, $code );
144             }
145              
146 46         364 $self->_code( $code );
147 46         457 $self->_block_map( {} );
148 46         394 $self->_temporary_map( {} );
149              
150 46         295 my @converted;
151 46         678 foreach my $block ( @{$segment->basic_blocks} ) {
  46         170  
152 407         3632 my @bytecode;
153 407         965 push @converted, [ $block, \@bytecode ];
154              
155 407         504 foreach my $ins ( @{$block->bytecode} ) {
  407         1057  
156 2412 100       7331 next if $ins->{label};
157 2025         4207 my $name = $NUMBER_TO_NAME{$ins->{opcode_n}};
158              
159 2025 50       3836 die "Invalid $ins->{opcode}/$ins->{opcode_n}" unless $name;
160              
161 2025 100       4306 if( my $sub = $opcode_map{$ins->{opcode_n}} ) {
162 1133         2588 $sub->( $self, \@bytecode, $ins );
163             } else {
164 892 100       1963 my %p = $ins->{attributes} ? %{$ins->{attributes}} : ();
  276         1048  
165 892 100       1791 $p{slot} = $sigil_to_slot{$p{slot}} if $p{slot};
166 892         2267 push @bytecode, o( $name, %p );
167             }
168             }
169             }
170              
171 46         131 foreach my $block ( @converted ) {
172 407         804 my $start = @{$self->_code->bytecode};
  407         1170  
173 407         2590 push @{$self->_code->bytecode}, @{$block->[1]};
  407         982  
  407         3129  
174              
175 407         547 foreach my $op ( @{$self->_block_map->{$block->[0]}} ) {
  407         1190  
176 442         2566 $op->{to} = $start;
177             }
178             }
179              
180 46         434 $self->_allocate_lexicals( $is_sub );
181 46 100       183 $self->runtime->symbol_table->set_symbol( $segment->name, '&', $code )
182             if defined $segment->name;
183              
184 46         972 return $code;
185             }
186              
187             sub process_regex {
188 16     16 0 117 my( $self, $regex ) = @_;
189              
190 16         74 $self->start_code_generation;
191              
192 16         264 return $self->_process_code_segments
193             ( $self->_intermediate->generate_regex( $regex ) );
194             }
195              
196             sub finished {
197 15     15 0 36 my( $self ) = @_;
198 15         111 my $pending = $self->_pending;
199              
200 15         156 return $self->_process_code_segments
201             ( $self->_intermediate->generate_bytecode( $pending ) );
202             }
203              
204             sub _process_code_segments {
205 31     31   735 my( $self, $code_segments ) = @_;
206              
207 31         177 $self->_generated( {} );
208 31         234 foreach my $segment ( @$code_segments ) {
209 46 100       210 next if $self->_generated->{$segment};
210 31         537 _generate_segment( $self, $segment, undef );
211             }
212              
213 31         171 my $res = $self->_generated->{$code_segments->[0]};
214              
215 31         339 $self->_cleanup;
216              
217 31         3842 return $res;
218             }
219              
220             sub _cleanup {
221 31     31   75 my( $self ) = @_;
222              
223 31         148 $self->_pending( [] );
224 31         270 $self->_code( undef );
225 31         215 $self->_block_map( undef );
226 31         350 $self->_temporary_map( undef );
227 31         3887 $self->_generated( undef );
228             }
229              
230             sub start_code_generation {
231 31     31 0 275 my( $self, $args ) = @_;
232              
233 31 100 66     263 $self->_intermediate->file_name( $args->{file_name} )
234             if $args && $args->{file_name};
235 31         310 $self->_pending( [] );
236             }
237              
238             sub end_code_generation {
239 15     15 0 322 my( $self ) = @_;
240 15         94 my $res = $self->finished;
241              
242 15         96 return $res;
243             }
244              
245             sub _end {
246 30     30   69 my( $self, $bytecode, $op ) = @_;
247              
248 30 100       139 if( $self->_code->isa( 'Language::P::Toy::Value::Subroutine' ) ) {
249             # could be avoided in most cases, but simplifies code generation
250 15         170 push @$bytecode,
251             o( 'make_list', count => 0 ),
252             o( 'return' );
253             } else {
254 15         358 push @$bytecode, o( 'end' );
255             }
256             }
257              
258             sub _global {
259 294     294   389 my( $self, $bytecode, $op ) = @_;
260              
261 294 100       793 if( $op->{attributes}{slot} == VALUE_GLOB ) {
262 1         6 push @$bytecode,
263             o( 'glob', name => $op->{attributes}{name}, create => 1 );
264 1         4 return;
265             }
266              
267 293         604 my $slot = $sigil_to_slot{$op->{attributes}{slot}};
268 293 50       597 die $op->{attributes}{slot} unless $slot;
269              
270 293         891 push @$bytecode,
271             o( 'glob', name => $op->{attributes}{name}, create => 1 ),
272             o( 'glob_slot_create', slot => $slot );
273             }
274              
275             sub _lexical {
276 28     28   46 my( $self, $bytecode, $op ) = @_;
277              
278 28 100       144 push @$bytecode,
279             o( $op->{attributes}{lexical}->closed_over ? 'lexical_pad' : 'lexical',
280             lexical => $op->{attributes}{lexical},
281             level => $op->{attributes}{level},
282             );
283             }
284              
285             sub _lexical_clear {
286 8     8   32 my( $self, $bytecode, $op ) = @_;
287              
288 8 100       32 push @$bytecode,
289             o( $op->{attributes}{lexical}->closed_over ? 'lexical_pad_clear' : 'lexical_clear',
290             lexical => $op->{attributes}{lexical},
291             level => $op->{attributes}{level},
292             );
293             }
294              
295             sub _const_string {
296 186     186   263 my( $self, $bytecode, $op ) = @_;
297              
298 186         1146 my $v = Language::P::Toy::Value::StringNumber->new
299             ( { string => $op->{parameters}[0] } );
300 186         2151 push @$bytecode,
301             o( 'constant', value => $v );
302             }
303              
304             sub _fresh_string {
305 21     21   33 my( $self, $bytecode, $op ) = @_;
306              
307 21         75 push @$bytecode,
308             o( 'fresh_string', value => $op->{parameters}[0] );
309             }
310              
311             sub _const_integer {
312 172     172   278 my( $self, $bytecode, $op ) = @_;
313              
314 172         1003 my $v = Language::P::Toy::Value::StringNumber->new
315             ( { integer => $op->{parameters}[0] } );
316 172         2054 push @$bytecode,
317             o( 'constant', value => $v );
318             }
319              
320             sub _const_float {
321 2     2   4 my( $self, $bytecode, $op ) = @_;
322              
323 2         15 my $v = Language::P::Toy::Value::StringNumber->new
324             ( { float => $op->{parameters}[0] } );
325 2         28 push @$bytecode,
326             o( 'constant', value => $v );
327             }
328              
329             sub _const_undef {
330 2     2   3 my( $self, $bytecode, $op ) = @_;
331              
332 2         6 my $v = Language::P::Toy::Value::StringNumber->new;
333 2         18 push @$bytecode,
334             o( 'constant', value => $v );
335             }
336              
337             sub _const_codelike {
338 2     2   3 my( $self, $bytecode, $op ) = @_;
339              
340 2         7 my $sub = $self->_generated->{$op->{parameters}[0]};
341 2         14 push @$bytecode,
342             o( 'constant', value => $sub );
343             }
344              
345             sub _temporary_index {
346 43     43   64 my( $self, $index ) = @_;
347 43 100       115 return $self->_temporary_map->{$index}
348             if exists $self->_temporary_map->{$index};
349 19         155 my $offset = $self->_temporary_map->{$index} = $self->_code->stack_size;
350 19         240 ++$self->_code->{stack_size};
351 19         116 return $offset;
352             }
353              
354             sub _temporary {
355 6     6   10 my( $self, $bytecode, $op ) = @_;
356              
357 6         18 push @$bytecode,
358             o( 'lexical', index => _temporary_index( $self, $op->{attributes}{index} ) );
359             }
360              
361             sub _temporary_set {
362 5     5   10 my( $self, $bytecode, $op ) = @_;
363              
364 5         20 push @$bytecode,
365             o( 'lexical_set', index => _temporary_index( $self, $op->{attributes}{index} ) );
366             }
367              
368             sub _map_slot_index {
369 32     32   121 my( $self, $bytecode, $op ) = @_;
370              
371 32         204 push @$bytecode,
372             o( $NUMBER_TO_NAME{$op->{opcode_n}},
373             name => $op->{attributes}{name},
374             slot => $sigil_to_slot{$op->{attributes}{slot}},
375             index => _temporary_index( $self, $op->{attributes}{index} ),
376             );
377             }
378              
379             sub _direct_jump {
380 248     248   334 my( $self, $bytecode, $op ) = @_;
381              
382 248         728 push @$bytecode,
383             o( $NUMBER_TO_NAME{$op->{opcode_n}} );
384 248         326 push @{$self->_block_map->{$op->{attributes}{to}}}, $bytecode->[-1];
  248         757  
385             }
386              
387             sub _cond_jump_simple {
388 86     86   131 my( $self, $bytecode, $op ) = @_;
389              
390 86         285 push @$bytecode,
391             o( $NUMBER_TO_NAME{$op->{opcode_n}} ),
392             o( 'jump' );
393 86         124 push @{$self->_block_map->{$op->{attributes}{true}}}, $bytecode->[-2];
  86         270  
394 86         848 push @{$self->_block_map->{$op->{attributes}{false}}}, $bytecode->[-1];
  86         238  
395             }
396              
397             sub _rx_quantifier {
398 11     11   26 my( $self, $bytecode, $op ) = @_;
399 11         13 my %params = %{$op->{attributes}};
  11         76  
400 11         37 delete $params{true}; delete $params{false};
  11         19  
401              
402 11         48 push @$bytecode,
403             o( 'rx_quantifier', %params ),
404             o( 'jump' );
405 11         28 push @{$self->_block_map->{$op->{attributes}{true}}}, $bytecode->[-2];
  11         43  
406 11         99 push @{$self->_block_map->{$op->{attributes}{false}}}, $bytecode->[-1];
  11         34  
407             }
408              
409             my %lex_map;
410              
411             sub _find_add_value {
412 10     10   15 my( $pad, $lexical ) = @_;
413              
414 10 100       44 return $lex_map{$pad}{$lexical} if exists $lex_map{$pad}{$lexical};
415 6         19 return $lex_map{$pad}{$lexical} = $pad->add_value( $lexical );
416             }
417              
418             sub _uplevel {
419 10     10   42 my( $code, $level ) = @_;
420              
421 10         41 $code = $code->outer foreach 1 .. $level;
422              
423 10         46 return $code;
424             }
425              
426             sub _allocate_lexicals {
427 46     46   101 my( $self, $is_sub ) = @_;
428              
429 46         175 my $pad = $self->_code->lexicals;
430 46 100       487 return unless $pad;
431 30 100       154 my %map = $lex_map{$pad} ? %{ delete $lex_map{$pad} } : ();
  3         25  
432 30         53 my %clear;
433             my $needs_pad;
434 30         59 foreach my $op ( @{$self->_code->bytecode} ) {
  30         98  
435 2299 100       5178 next if !$op->{lexical};
436              
437 38 100       127 if( !exists $map{$op->{lexical}} ) {
438 16 100 66     78 if( $op->{lexical}->name eq '_'
    100          
439             && $op->{lexical}->sigil == VALUE_ARRAY ) {
440 5         85 $map{$op->{lexical}} = 0; # arguments are always first
441             } elsif( $op->{lexical}->closed_over ) {
442 4         6 my $level = $op->{level};
443              
444 4 50       16 if( $level ) {
445 4         13 my $code_from = _uplevel( $self->_code, $level );
446 4         15 my $pad_from = $code_from->lexicals;
447 4         23 my $val = _find_add_value( $pad_from, $op->{lexical} );
448 4 100       34 if( $code_from->is_subroutine ) {
449 2         7 foreach my $index ( -$level .. -1 ) {
450 3         8 my $inner_code = _uplevel( $self->_code, -$index - 1 );
451 3         13 my $outer_code = _uplevel( $inner_code, 1 );
452 3         7 my $outer_pad = $outer_code->lexicals;
453 3         15 my $inner_pad = $inner_code->lexicals;
454              
455 3         12 my $outer_idx = _find_add_value( $outer_pad, $op->{lexical} );
456 3         7 my $inner_idx = _find_add_value( $inner_pad, $op->{lexical} );
457 3         17 push @{$inner_code->closed},
  3         12  
458             [$outer_idx, $inner_idx];
459 3 100       28 $map{$op->{lexical}} = $inner_idx
460             if $index == -1;
461             }
462             } else {
463 2         8 $map{$op->{lexical}} =
464             $pad->add_value( $op->{lexical},
465             $pad_from->values->[ $val ] );
466             }
467             } else {
468 0         0 $map{$op->{lexical}} = _find_add_value( $pad, $op->{lexical} );
469             }
470             } else {
471 7         32 $map{$op->{lexical}} = $self->_code->stack_size;
472 7         89 ++$self->_code->{stack_size};
473             }
474             }
475              
476 38 100       141 if( $op->{lexical}->closed_over ) {
477 11         12 $needs_pad = 1;
478             }
479 38         106 $op->{index} = $map{$op->{lexical}};
480 38 100 100     103 $clear{$op->{index}} ||= 1 if $op->{lexical}->closed_over && !$op->{level};
      100        
481 38         79 delete $op->{lexical};
482 38         72 delete $op->{level};
483             }
484              
485 30 100       63 $self->_code->{closed} = undef unless @{$self->_code->closed};
  30         109  
486 30 100       589 if( !$needs_pad ) {
487 24         105 $self->_code->{lexicals} = undef;
488             }
489 30         213 $pad->{clear} = [ keys %clear ];
490             }
491              
492             1;