File Coverage

blib/lib/Text/Xslate/Compiler.pm
Criterion Covered Total %
statement 702 728 96.4
branch 219 256 85.5
condition 67 89 75.2
subroutine 63 63 100.0
pod 0 9 0.0
total 1051 1145 91.7


line stmt bran cond sub pod time code
1             package Text::Xslate::Compiler;
2 169     169   181532 use Mouse;
  169         378033  
  169         1143  
3 169     169   62309 use Mouse::Util::TypeConstraints;
  169         396  
  169         1226  
4              
5 169     169   14557 use Scalar::Util ();
  169         372  
  169         2717  
6 169     169   921 use Carp ();
  169         335  
  169         3032  
7              
8 169     169   111031 use Text::Xslate::Parser;
  169         16425  
  169         7534  
9 169         42780 use Text::Xslate::Util qw(
10             $DEBUG
11             value_to_literal
12             is_int any_in
13             make_error
14             p
15 169     169   1123 );
  169         328  
16              
17             #use constant _VERBOSE => scalar($DEBUG =~ /\b verbose \b/xms);
18             use constant {
19 169         1801684 _DUMP_ASM => scalar($DEBUG =~ /\b dump=asm \b/xms),
20             _DUMP_AST => scalar($DEBUG =~ /\b dump=ast \b/xms),
21             _DUMP_GEN => scalar($DEBUG =~ /\b dump=gen \b/xms),
22             _DUMP_CAS => scalar($DEBUG =~ /\b dump=cascade \b/xms),
23              
24             _OP_NAME => 0,
25             _OP_ARG => 1,
26             _OP_LINE => 2,
27             _OP_FILE => 3,
28             _OP_LABEL => 4,
29             _OP_COMMENT => 5,
30              
31             _FOR_LOOP => 1,
32             _WHILE_LOOP => 2,
33 169     169   1001 };
  169         339  
34              
35              
36             our $OPTIMIZE = scalar(($DEBUG =~ /\b optimize=(\d+) \b/xms)[0]);
37             if(not defined $OPTIMIZE) {
38             $OPTIMIZE = 1; # enable optimization by default
39             }
40              
41             our @CARP_NOT = qw(Text::Xslate Text::Xslate::Parser);
42              
43             {
44             package Text::Xslate;
45             our %OPS; # to avoid 'once' warnings;
46             }
47              
48             my %binary = (
49             '==' => 'eq',
50             '!=' => 'ne',
51             '<' => 'lt',
52             '<=' => 'le',
53             '>' => 'gt',
54             '>=' => 'ge',
55              
56             '~~' => 'match',
57              
58             '<=>' => 'ncmp',
59             'cmp' => 'scmp',
60              
61             '+' => 'add',
62             '-' => 'sub',
63             '*' => 'mul',
64             '/' => 'div',
65             '%' => 'mod',
66              
67             '~' => 'concat',
68             'x' => 'repeat',
69              
70             '+|' => 'bitor',
71             '+&' => 'bitand',
72             '+^' => 'bitxor',
73              
74             'min' => 'lt', # a < b ? a : b
75             'max' => 'gt', # a > b ? a : b
76              
77             '[' => 'fetch_field',
78             );
79             my %logical_binary = (
80             '&&' => 'and',
81             '||' => 'or',
82             '//' => 'dor',
83             );
84              
85             my %unary = (
86             '!' => 'not',
87             '+' => 'noop',
88             '-' => 'minus',
89             '+^' => 'bitneg',
90              
91             'max_index' => 'max_index', # for loop context vars
92             );
93              
94             my %goto_family = map { $_ => undef } qw(
95             for_iter
96             and
97             dand
98             or
99             dor
100             goto
101             );
102              
103             my %builtin = (
104             'html_escape' => ['builtin_html_escape',
105             \&Text::Xslate::Util::html_escape],
106             'uri_escape' => ['builtin_uri_escape',
107             \&Text::Xslate::Util::uri_escape],
108             'mark_raw' => ['builtin_mark_raw',
109             \&Text::Xslate::Util::mark_raw],
110             'unmark_raw' => ['builtin_unmark_raw',
111             \&Text::Xslate::Util::unmark_raw],
112              
113             'raw' => ['builtin_mark_raw',
114             \&Text::Xslate::Util::mark_raw],
115              
116             'html' => ['builtin_html_escape',
117             \&Text::Xslate::Util::html_escape],
118             'uri' => ['builtin_uri_escape',
119             \&Text::Xslate::Util::uri_escape],
120              
121             'is_array_ref' => ['builtin_is_array_ref',
122             \&Text::Xslate::Util::is_array_ref],
123             'is_hash_ref' => ['builtin_is_hash_ref',
124             \&Text::Xslate::Util::is_hash_ref],
125             );
126              
127             has lvar_id => ( # local variable id
128             is => 'rw',
129             isa => 'Int',
130              
131             init_arg => undef,
132             );
133              
134             has lvar => ( # local variable id table
135             is => 'rw',
136             isa => 'HashRef[Int]',
137              
138             init_arg => undef,
139             );
140              
141             has const => (
142             is => 'rw',
143             isa => 'ArrayRef',
144              
145             init_arg => undef,
146             );
147              
148             has macro_table => (
149             is => 'rw',
150             isa => 'HashRef',
151              
152             predicate => 'has_macro_table',
153             init_arg => undef,
154             );
155              
156             has engine => ( # Xslate engine
157             is => 'ro',
158             isa => 'Object',
159             required => 0,
160             weak_ref => 1,
161             );
162              
163             has dependencies => (
164             is => 'ro',
165             isa => 'ArrayRef',
166             init_arg => undef,
167             );
168              
169             has type => (
170             is => 'rw',
171             isa => enum([qw(html xml text)]),
172             default => 'html',
173             );
174              
175             has syntax => (
176             is => 'rw',
177              
178             default => 'Kolon',
179             );
180              
181             has parser_option => (
182             is => 'rw',
183             isa => 'HashRef',
184              
185             default => sub { {} },
186             );
187              
188             has parser => (
189             is => 'rw',
190             isa => 'Object', # Text::Xslate::Parser
191              
192             handles => [qw(define_function)],
193              
194             lazy => 1,
195             builder => '_build_parser',
196             init_arg => undef,
197             );
198              
199             has input_layer => (
200             is => 'ro',
201             default => ':utf8',
202             );
203              
204             sub _build_parser {
205 238     238   8788 my($self) = @_;
206 238         2822 my $syntax = $self->syntax;
207 238 100       2845 if(ref($syntax)) {
208 1         6 return $syntax;
209             }
210             else {
211 237         3703 my $parser_class = Mouse::Util::load_first_existing_class(
212             "Text::Xslate::Syntax::" . $syntax,
213             $syntax,
214             );
215             return $parser_class->new(
216 237         36387 %{$self->parser_option},
  237         8560  
217             engine => $self->engine,
218             compiler => $self,
219             );
220             }
221             }
222              
223             has cascade => (
224             is => 'rw',
225             init_arg => undef,
226             );
227              
228             has [qw(header footer macro)] => (
229             is => 'rw',
230             isa => 'ArrayRef',
231             );
232              
233             has current_file => (
234             is => 'rw',
235              
236             init_arg => undef,
237             );
238              
239             has file => (
240             is => 'rw',
241              
242             init_arg => undef,
243             );
244              
245             has overridden_builtin => (
246             is => 'ro',
247             isa => 'HashRef',
248              
249             default => sub { +{} },
250             );
251              
252             sub lvar_use {
253 1459     1459 0 2398 my($self, $n) = @_;
254              
255 1459         5460 return $self->lvar_id + $n;
256             }
257              
258             sub filename {
259 48125     48125 0 69436 my($self) = @_;
260 48125         107750 my $file = $self->file;
261 48125 100       137788 return ref($file) ? '' : $file;
262             }
263              
264             sub compile {
265 3440     3440 0 21045 my($self, $input, %args) = @_;
266              
267             # each compiling process is independent
268 3440         11436 local $self->{macro_table} = {};
269 3440         9431 local $self->{lvar_id } = 0;
270 3440         8416 local $self->{lvar} = {};
271 3440         9605 local $self->{const} = [];
272 3440         8225 local $self->{in_loop} = 0;
273 3440         8198 local $self->{dependencies} = [];
274 3440         7811 local $self->{cascade};
275 3440         8386 local $self->{header} = $self->{header};
276 3440         8356 local $self->{footer} = $self->{footer};
277 3440         7693 local $self->{macro} = $self->{macro};
278 3440         8754 local $self->{current_file} = ''; # for opinfo
279 3440   100     16036 local $self->{file} = $args{file} || \$input;
280              
281 3440 100       16269 if(my $engine = $self->engine) {
282 3435         10001 my $ob = $self->overridden_builtin;
283 3435         9387 Internals::SvREADONLY($ob, 0);
284 3435         13916 foreach my $name(keys %builtin) {
285 30915         61695 my $f = $engine->{function}{$name};
286 30915         81564 $ob->{$name} = ( $builtin{$name}[1] != $f ) + 0;
287             }
288 3435         13216 Internals::SvREADONLY($ob, 1);
289             }
290              
291 3440         10825 my $parser = $self->parser;
292              
293 3440         8067 my $header = delete $self->{header};
294 3440         7291 my $footer = delete $self->{footer};
295 3440         7653 my $macro = delete $self->{macro};
296              
297 3440 100       10452 if(!$args{omit_augment}) {
298 2213 100       6608 if($header) {
299 9         36 substr $input, 0, 0, $self->_cat_files($header);
300             }
301 2213 100       8465 if($footer) {
302 9         29 $input .= $self->_cat_files($footer);
303             }
304             }
305 3440 100       8626 if($macro) {
306 2 50       5 if(!grep { $_ eq $self->current_file } @$macro) {
  2         14  
307 2         15 substr $input, 0, 0, $self->_cat_files($macro);
308             }
309             }
310              
311 3440         5704 my @code; # main code
312             {
313 3440         5663 my $ast = $parser->parse($input, %args);
  3440         17200  
314 3391         7471 print STDERR p($ast) if _DUMP_AST;
315 3391         16125 @code = (
316             $self->opcode(set_opinfo => undef, file => $self->current_file, line => 1),
317             $self->compile_ast($ast),
318             $self->opcode('end'),
319             );
320             }
321              
322 3376         13490 my $cascade = $self->cascade;
323 3376 100       9533 if(defined $cascade) {
324 73         287 $self->_process_cascade($cascade, \%args, \@code);
325             }
326              
327 3371 50       17685 push @code, $self->_flush_macro_table() if $self->has_macro_table;
328              
329 3371 50       9446 if($OPTIMIZE) {
330 3371         15800 $self->_optimize_vmcode(\@code) for 1 .. 3;
331             }
332              
333 3371         6016 print STDERR "// ", $self->filename, "\n",
334             $self->as_assembly(\@code, scalar($DEBUG =~ /\b ix \b/xms))
335             if _DUMP_ASM;
336              
337             {
338 3371         6188 my %uniq;
  3371         6047  
339             push @code,
340 84         313 map { [ depend => $_ ] }
341 3371   66     7070 grep { !ref($_) and !$uniq{$_}++ } @{$self->dependencies};
  100         696  
  3371         17968  
342             }
343              
344 3371         38180 return \@code;
345             }
346              
347             sub opcode { # build an opcode
348 51774     51774 0 125276 my($self, $name, $arg, %args) = @_;
349 51774         81487 my $symbol = $args{symbol};
350 51774         75411 my $file = $args{file};
351 51774         74335 my $label = $args{label};
352 51774 100       116458 if(not defined $file) {
353 47814         101837 $file = $self->filename;
354 47814 100 66     248897 if(defined $file and $file ne $self->current_file) {
355 1958         10398 $self->current_file($file);
356             }
357             else {
358 45856         85046 $file = undef;
359             }
360             }
361             # name, arg, label, line, file, comment
362             return [ $name => $arg,
363             $args{line} || (ref $symbol ? $symbol->line : undef),
364             $file,
365             $label,
366             $args{comment},
367 51774   66     427311 ];
368             }
369              
370             sub push_expr {
371 5905     5905 0 8227 my($self, $node) = @_;
372              
373 5905         14585 my $list_op = $node->arity eq 'range';
374 5905         11177 my @code = ($self->compile_ast($node));
375 5905 100       13018 if(not $list_op) {
376 5898         11694 push @code, $self->opcode('push');
377             }
378 5905         16915 return @code;
379             }
380              
381              
382             sub _cat_files {
383 20     20   34 my($self, $files) = @_;
384 20   33     83 my $engine = $self->engine || $self->_error("No Xslate engine which header/footer requires");
385 20         35 my $s = '';
386 20         25 foreach my $file(@{$files}) {
  20         50  
387 26         102 my $fullpath = $engine->find_file($file)->{fullpath};
388 26         169 $s .= $engine->slurp_template( $self->input_layer, $fullpath );
389 26         92 $self->requires($fullpath);
390             }
391 20         84 return $s;
392             }
393              
394             our $_lv = -1;
395              
396             sub compile_ast {
397 17851     17851 0 28884 my($self, $ast) = @_;
398 17851 100       39565 return if not defined $ast;
399              
400 17707         20769 local $_lv = $_lv + 1 if _DUMP_GEN;
401              
402 17707         22996 my @code;
403 17707 100       47395 foreach my $node(ref($ast) eq 'ARRAY' ? @{$ast} : $ast) {
  4586         13217  
404 28256 50       100401 Scalar::Util::blessed($node) or Carp::confess("[BUG] Not a node object: " . p($node));
405              
406 28256         42235 printf STDERR "%s"."generate %s (%s)\n", "." x $_lv, $node->arity, $node->id if _DUMP_GEN;
407              
408 28256   33     152562 my $generator = $self->can('_generate_' . $node->arity)
409             || Carp::confess("[BUG] Unexpected node: " . p($node));
410              
411 28256         67654 push @code, $self->$generator($node);
412             }
413              
414 17673         66754 return @code;
415             }
416              
417             sub _process_cascade {
418 73     73   144 my($self, $cascade, $args, $main_code) = @_;
419 73         101 printf STDERR "# cascade %s %s", $self->file, $cascade->dump if _DUMP_CAS;
420 73   33     312 my $engine = $self->engine
421             || $self->_error("Cannot cascade templates without Xslate engine", $cascade);
422              
423 73         107 my($base_file, $base_code);
424 73         186 my $base = $cascade->first;
425             my @components = $cascade->second
426 73 100       334 ? (map{ $self->_bare_to_file($_) } @{$cascade->second})
  13         38  
  11         36  
427             : ();
428 73         203 my $vars = $cascade->third;
429              
430 73 100       187 if(defined $base) { # pure cascade
431 66         208 $base_file = $self->_bare_to_file($base);
432 65         441 $base_code = $engine->load_file($base_file);
433 63         283 $self->requires( $engine->find_file($base_file)->{fullpath} );
434             }
435             else { # overlay
436 7         15 $base_file = $args->{file}; # only for error messages
437 7         165 $base_code = $main_code;
438              
439 7 50       23 if(defined $args->{fullpath}) {
440 0         0 $self->requires( $args->{fullpath} );
441             }
442              
443 7         11 push @{$main_code}, $self->_flush_macro_table();
  7         21  
444             }
445              
446 70         252 foreach my $cfile(@components) {
447 13         53 my $code = $engine->load_file($cfile);
448 13         59 my $fullpath = $engine->find_file($cfile)->{fullpath};
449              
450 13         59 my $mtable = $self->macro_table;
451 13         18 my $macro;
452 13         20 foreach my $c(@{$code}) {
  13         31  
453             # $c = [name, arg, line, file, symbol ]
454              
455             # retrieve macros from assembly code
456 158 100       431 if($c->[_OP_NAME] eq 'macro_begin' .. $c->[_OP_NAME] eq 'macro_end') {
    50          
457 100 100       388 if($c->[_OP_NAME] eq 'macro_begin') {
    50          
    50          
    100          
458 23         33 $macro = [];
459 23         111 $macro = {
460             name => $c->[_OP_ARG],
461             line => $c->[_OP_LINE],
462             file => $c->[_OP_FILE],
463             body => [],
464             };
465 23   50     33 push @{ $mtable->{$c->[_OP_ARG]} ||= [] }, $macro;
  23         163  
466             }
467             elsif($c->[_OP_NAME] eq 'macro_nargs') {
468 0         0 $macro->{nargs} = $c->[_OP_ARG];
469             }
470             elsif($c->[_OP_NAME] eq 'macro_outer') {
471 0         0 $macro->{outer} = $c->[_OP_ARG];
472             }
473             elsif($c->[_OP_NAME] eq 'macro_end') {
474             # noop
475             }
476             else {
477 54         66 push @{$macro->{body}}, $c;
  54         122  
478             }
479             }
480             elsif($c->[_OP_NAME] eq 'depend') {
481 0         0 $self->requires($c->[_OP_ARG]);
482             }
483             }
484 13         44 $self->requires($fullpath);
485 13         45 $self->_process_cascade_file($cfile, $base_code);
486             }
487              
488 70 100       193 if(defined $base) { # pure cascade
489 63         211 $self->_process_cascade_file($base_file, $base_code);
490 61 100       150 if(defined $vars) {
491 13         26 unshift @{$base_code}, $self->_localize_vars($vars);
  13         47  
492             }
493              
494 61         99 foreach my $c(@{$main_code}) {
  61         152  
495 166 50 66     672 if($c->[_OP_NAME] eq 'print_raw_s'
496             && $c->[_OP_ARG] =~ m{ [^ \t\r\n] }xms) {
497 0         0 Carp::carp("Xslate: Useless use of text '$c->[1]'");
498             }
499             }
500 61         99 @{$main_code} = @{$base_code};
  61         439  
  61         111  
501             }
502             else { # overlay
503 7         18 return;
504             }
505             }
506              
507             sub _process_cascade_file {
508 76     76   157 my($self, $file, $base_code) = @_;
509 76         98 printf STDERR "# cascade file %s\n", p($file) if _DUMP_CAS;
510 76         226 my $mtable = $self->macro_table;
511              
512 76         146 for(my $i = 0; $i < @{$base_code}; $i++) {
  803         1819  
513 729         941 my $c = $base_code->[$i];
514 729 100       1595 if($c->[_OP_NAME] ne 'macro_begin') {
515 661         930 next;
516             }
517              
518             # macro
519 68         121 my $name = $c->[_OP_ARG];
520 68         189 $name =~ s/\@.+$//;
521 68         88 printf STDERR "# macro %s\n", $name if _DUMP_CAS;
522              
523 68 100       200 if(exists $mtable->{$name}) {
524 2         6 my $m = $mtable->{$name};
525 2 50       8 if(ref($m) ne 'HASH') {
526 0         0 $self->_error('[BUG] Unexpected macro structure: '
527             . p($m) );
528             }
529              
530             $self->_error(
531             "Redefinition of macro/block $name in " . $file
532             . " (you must use block modifiers to override macros/blocks)",
533             $m->{line}
534 2         28 );
535             }
536              
537 66         244 my $before = delete $mtable->{$name . '@before'};
538 66         178 my $around = delete $mtable->{$name . '@around'};
539 66         169 my $after = delete $mtable->{$name . '@after'};
540              
541 66 100       176 if(defined $before) {
542 25         34 my $n = scalar @{$base_code};
  25         43  
543 25         42 foreach my $m(@{$before}) {
  25         50  
544 25         34 splice @{$base_code}, $i+1, 0, @{$m->{body}};
  25         41  
  25         97  
545             }
546 25         43 $i += scalar(@{$base_code}) - $n;
  25         48  
547             }
548              
549 66         160 my $macro_start = $i+1;
550 66         660 $i++ while($base_code->[$i][_OP_NAME] ne 'macro_end'); # move to the end
551              
552 66 100       163 if(defined $around) {
553 21         36 my @original = splice @{$base_code}, $macro_start, ($i - $macro_start);
  21         76  
554 21         38 $i = $macro_start;
555              
556 21         33 my @body;
557 21         33 foreach my $m(@{$around}) {
  21         44  
558 21         39 push @body, @{$m->{body}};
  21         81  
559             }
560 21         72 for(my $j = 0; $j < @body; $j++) {
561 142 100       479 if($body[$j][_OP_NAME] eq 'super') {
562 7         27 splice @body, $j, 1, @original;
563             }
564             }
565 21         46 splice @{$base_code}, $macro_start, 0, @body;
  21         59  
566              
567 21         103 $i += scalar(@body);
568             }
569              
570 66 100       217 if(defined $after) {
571 24         35 foreach my $m(@{$after}) {
  24         55  
572 24         31 splice @{$base_code}, $i, 0, @{$m->{body}};
  24         39  
  24         165  
573             }
574             }
575             }
576 74         221 return;
577             }
578              
579              
580             sub _flush_macro_table {
581 3378     3378   6802 my($self) = @_;
582 3378         9742 my $mtable = $self->macro_table;
583 3378         6186 my @code;
584 3378         6409 foreach my $macros(values %{$mtable}) {
  3378         13552  
585 258 100       789 foreach my $macro(ref($macros) eq 'ARRAY' ? @{$macros} : $macros) {
  29         62  
586             push @code,
587             $self->opcode( macro_begin => $macro->{name},
588             file => $macro->{file},
589 258         877 line => $macro->{line} );
590              
591             push @code, $self->opcode( macro_nargs => $macro->{nargs} )
592 258 100       1020 if $macro->{nargs};
593              
594             push @code, $self->opcode( macro_outer => $macro->{outer} )
595 258 100       710 if $macro->{outer};
596              
597 258         377 push @code, @{ $macro->{body} }, $self->opcode('macro_end');
  258         741  
598             }
599             }
600 3378         6721 %{$mtable} = ();
  3378         10083  
601 3378         10380 return @code;
602             }
603              
604             sub _generate_name {
605 449     449   1025 my($self, $node) = @_;
606              
607 449         2836 my $id = $node->value; # may be aliased
608 449 100       1862 if(defined(my $lvar_id = $self->lvar->{$id})) { # constants
609 71         177 my $code = $self->const->[$lvar_id];
610 71 100       161 if(defined $code) {
611             # because the constant value is very simple,
612             # its definition is optimized away.
613             # only its value remains.
614 23         28 return @{$code};
  23         79  
615             }
616             else {
617 48         125 return $self->opcode( load_lvar => $lvar_id, symbol => $node );
618             }
619             }
620              
621 378         1398 return $self->opcode( fetch_symbol => $id, line => $node->line );
622             }
623              
624             sub _generate_operator {
625 1     1   2 my($self, $node) = @_;
626             # This method is called when an operators is used as an expression,
627             # e.g. <: + :>, so simply throws the error
628 1         5 $self->_error("Invalid expression", $node);
629             }
630              
631             sub _can_optimize_print {
632 2382     2382   5962 my($self, $name, $node) = @_;
633              
634 2382 50       7083 return 0 if !$OPTIMIZE;
635 2382 50 66     8110 return 0 if !($name eq 'print' or $name eq 'print_raw');
636              
637 2382         7365 my $maybe_name = $node->first;
638             return $node->arity eq 'call'
639             && $maybe_name->arity eq 'name'
640             && @{$node->second} == 1 # args of the filter
641             && any_in($maybe_name->id, qw(raw mark_raw html))
642 2382   100     16911 && !$self->overridden_builtin->{$maybe_name->id};
643             }
644              
645             # also deal with smart escaping
646             sub _generate_print {
647 12647     12647   22443 my($self, $node) = @_;
648              
649 12647         18488 my @code;
650              
651 12647         33453 my $proc = $node->id;
652 12647 100 100     44582 if($proc eq 'print' and $self->type eq 'text') {
653 28         53 $proc = 'print_raw';
654             }
655              
656 12647         20115 foreach my $arg(@{ $node->first }){
  12647         44418  
657 12678 100 66     101185 if( $proc eq 'print' && $self->overridden_builtin->{html_escape} ) {
    100 100        
    100          
658             # default behaviour of print() is overridden
659 5         12 push @code,
660             $self->opcode('pushmark'),
661             $self->compile_ast($arg),
662             $self->opcode('push'),
663             $self->opcode('fetch_symbol' => 'html_escape'),
664             $self->opcode('funcall'),
665             $self->opcode('print_raw');
666             }
667             elsif(exists $Text::Xslate::OPS{$proc . '_s'}
668             && $arg->arity eq 'literal'){
669 10291         49732 push @code,
670             $self->opcode( $proc . '_s' => $arg->value,
671             line => $arg->line );
672             }
673             elsif($self->_can_optimize_print($proc, $arg)){
674 27         75 my $filter = $arg->first;
675 27         72 my $filter_name = $filter->id;
676 27 100       95 my $command = $builtin{ $filter_name }[0] eq 'builtin_mark_raw'
677             ? 'print_raw' # mark_raw, raw
678             : 'print'; # html
679              
680 27         109 push @code,
681             $self->compile_ast($arg->second->[0]),
682             $self->opcode(
683             $command => undef,
684             symbol => $filter );
685              
686             }
687             else {
688 2355         7994 push @code,
689             $self->compile_ast($arg),
690             $self->opcode( $proc => undef, line => $node->line );
691             }
692             }
693              
694 12638 50       33232 if(!@code) {
695 0         0 $self->_error("$node requires at least one argument", $node);
696             }
697 12638         44647 return @code;
698             }
699              
700             sub _generate_include {
701 1253     1253   2004 my($self, $node) = @_;
702              
703 1253         2654 my $file = $node->first;
704 1253 100       4184 my @code = (
705             ( ref($file) eq 'ARRAY'
706             ? $self->opcode( literal => $self->_bare_to_file($file) )
707             : $self->compile_ast($file) ),
708             $self->opcode( $node->id => undef, line => $node->line ),
709             );
710              
711 1253 100       4411 if(defined(my $vars = $node->second)) {
712 17         44 @code = ($self->opcode('enter'),
713             $self->_localize_vars($vars),
714             @code,
715             $self->opcode('leave'),
716             );
717             }
718 1252         3835 return @code;
719             }
720              
721             sub _bare_to_file {
722 82     82   146 my($self, $file) = @_;
723 82 100       274 if(ref($file) eq 'ARRAY') { # myapp::foo
    100          
724 68         109 return join('/', map { $_->value } @{$file}) . $self->{engine}->{suffix};
  120         974  
  68         129  
725             }
726             elsif($file->arity eq 'literal') {
727 13         49 return $file->value;
728             }
729             else {
730 1         5 $self->_error("Expected a name or string literal", $file);
731             }
732             }
733              
734             sub _generate_cascade {
735 73     73   133 my($self, $node) = @_;
736 73 50       314 if(defined $self->cascade) {
737 0         0 $self->_error("Cannot cascade twice in a template", $node);
738             }
739 73         193 $self->cascade( $node );
740 73         174 return;
741             }
742              
743             # XXX: need more consideration
744             sub _compile_loop_block {
745 195     195   347 my($self, $block) = @_;
746 195         478 my @block_code = $self->compile_ast($block);
747              
748 195         441 foreach my $op(@block_code) {
749 1620 100       4228 if(any_in( $op->[_OP_NAME], qw(pushmark loop_control))) {
750             # pushmark ... funcall (or something) may create mortal SVs
751             # so surround the block with ENTER and LEAVE
752 25         76 unshift @block_code, $self->opcode('enter');
753 25         75 push @block_code, $self->opcode('leave');
754 25         77 last;
755             }
756             }
757              
758 195         703 foreach my $i(1 .. (@block_code-1)) {
759 1640         2122 my $op = $block_code[$i];
760 1640 100       3846 if($op->[_OP_NAME] eq 'loop_control') {
761 10         15 my $type = $op->[_OP_ARG];
762 10         15 $op->[_OP_NAME] = 'goto';
763              
764 10         22 $op->[_OP_ARG] = (@block_code - $i);
765              
766 10 100       30 $op->[_OP_ARG] += 1 if $type eq 'last';
767             }
768             }
769              
770 195         834 return @block_code;
771             }
772              
773             sub _generate_for {
774 170     170   320 my($self, $node) = @_;
775 170         472 my $expr = $node->first;
776 170         402 my $vars = $node->second;
777 170         416 my $block = $node->third;
778              
779 170 50       229 if(@{$vars} != 1) {
  170         547  
780 0         0 $self->_error("A for-loop requires single variable for each item", $node);
781             }
782 170         277 local $self->{lvar} = { %{$self->lvar} }; # new scope
  170         939  
783 170         271 local $self->{const} = [ @{$self->const} ]; # new scope
  170         669  
784 170         400 local $self->{in_loop} = _FOR_LOOP;
785              
786 170         477 my @code = $self->compile_ast($expr);
787              
788 170         296 my($iter_var) = @{$vars};
  170         344  
789 170         514 my $lvar_id = $self->lvar_id;
790 170         649 my $lvar_name = $iter_var->id;
791              
792 170         805 $self->lvar->{$lvar_name} = $lvar_id;
793 170         498 $self->lvar->{'($_)'} = $lvar_id;
794              
795 170         450 push @code, $self->opcode( for_start => $lvar_id, symbol => $iter_var );
796              
797             # a for statement uses three local variables (container, iterator, and item)
798 170         475 local $self->{lvar_id} = $self->lvar_use(3);
799              
800 170         563 my @block_code = $self->_compile_loop_block($block);
801 170         601 push @code,
802             $self->opcode( literal_i => $lvar_id, symbol => $iter_var ),
803             $self->opcode( for_iter => scalar(@block_code) + 2 ),
804             @block_code,
805             $self->opcode( goto => -(scalar(@block_code) + 2), comment => "end for" );
806              
807 170         1866 return @code;
808             }
809              
810             sub _generate_for_else {
811 8     8   13 my($self, $node) = @_;
812              
813 8         23 my $for_block = $node->first;
814 8         26 my $else_block = $node->second;
815              
816 8         26 my @code = (
817             $self->compile_ast($for_block),
818             );
819              
820             # 'for' block sets __a with true if the loop count > 0
821 8         29 my @else = $self->compile_ast($else_block);
822 8         32 push @code, (
823             $self->opcode( or => scalar(@else) + 1, comment => 'for-else' ),
824             @else,
825             );
826              
827 8         52 return @code;
828             }
829              
830             sub _generate_while {
831 25     25   46 my($self, $node) = @_;
832 25         60 my $expr = $node->first;
833 25         58 my $vars = $node->second;
834 25         56 my $block = $node->third;
835              
836 25 50       31 if(@{$vars} > 1) {
  25         84  
837 0         0 $self->_error("A while-loop requires one or zero variable for each items", $node);
838             }
839              
840 25         74 (my $cond_op, undef, $expr) = $self->_prepare_cond_expr($expr);
841              
842             # TODO: combine all the loop contexts into single one
843 25         44 local $self->{lvar} = { %{$self->lvar} }; # new scope
  25         132  
844 25         36 local $self->{const} = [ @{$self->const} ]; # new scope
  25         94  
845 25         59 local $self->{in_loop} = _WHILE_LOOP;
846              
847 25         67 my @code = $self->compile_ast($expr);
848              
849 25         39 my($iter_var) = @{$vars};
  25         45  
850 25         35 my($lvar_id, $lvar_name);
851              
852 25 100       179 if(@{$vars}) {
  25         88  
853 10         59 $lvar_id = $self->lvar_id;
854 10         28 $lvar_name = $iter_var->id;
855 10         35 $self->lvar->{$lvar_name} = $lvar_id;
856 10         24 push @code, $self->opcode( save_to_lvar => $lvar_id, symbol => $iter_var );
857             }
858              
859 25         37 local $self->{lvar_id} = $self->lvar_use(scalar @{$vars});
  25         68  
860 25         94 my @block_code = $self->_compile_loop_block($block);
861 25         104 return @code,
862             $self->opcode( $cond_op => scalar(@block_code) + 2, symbol => $node ),
863             @block_code,
864             $self->opcode( goto => -(scalar(@block_code) + scalar(@code) + 1), comment => "end while" );
865              
866 0         0 return @code;
867             }
868              
869             sub _generate_loop_control {
870 12     12   21 my($self, $node) = @_;
871 12         30 my $type = $node->id;
872              
873 12 50       38 any_in($type, qw(last next))
874             or $self->_error("[BUG] Unknown loop control statement '$type'");
875              
876 12 100       34 if(not $self->{in_loop}) {
877 2         11 $self->_error("Use of loop control statement ($type) outside of loops");
878             }
879              
880 10         14 my @cleanup;
881 10 100 100     44 if( $self->{in_loop} == _FOR_LOOP && $type eq 'last' ) {
882 2         8 my $lvar_id = $self->lvar->{'($_)'};
883 2 50       6 defined($lvar_id)
884             or $self->_error('[BUG] Undefined loop iterator');
885              
886 2         7 @cleanup = (
887             $self->opcode( 'nil', undef,
888             comment => 'to clean the loop context' ),
889             $self->opcode( save_to_lvar => $lvar_id + 0), # item
890             $self->opcode( save_to_lvar => $lvar_id + 1), # iterator
891             $self->opcode( save_to_lvar => $lvar_id + 2), # body
892             $self->opcode( literal_i => 1 ), # for 'for-else'
893             );
894             }
895              
896 10         26 return $self->opcode('leave'),
897             @cleanup,
898             $self->opcode('loop_control' => $type, comment => $type);
899             }
900              
901             sub _generate_proc { # definition of macro, block, before, around, after
902 311     311   559 my($self, $node) = @_;
903 311         811 my $type = $node->id;
904 311         1032 my $name = $node->first->id;
905 311         422 my @args = map{ $_->id } @{$node->second};
  114         443  
  311         998  
906 311         830 my $block = $node->third;
907              
908 311         392 local $self->{lvar} = { %{$self->lvar} }; # new scope
  311         1485  
909 311         450 local $self->{const} = [ @{$self->const} ]; # new scope
  311         1197  
910              
911 311         837 my $lvar_used = $self->lvar_id;
912 311         444 my $arg_ix = 0;
913 311         611 foreach my $arg(@args) {
914             # to fetch ST(ix)
915             # Note that arg_ix must be start from 1
916 114         468 $self->lvar->{$arg} = $lvar_used + $arg_ix++;
917             }
918              
919 311         909 local $self->{lvar_id} = $self->lvar_use($arg_ix);
920              
921 311         843 my $opinfo = $self->opcode(set_opinfo => undef, file => $self->filename, line => $node->line);
922 311         995 my %macro = (
923             name => $name,
924             nargs => $arg_ix,
925             body => [ $opinfo, $self->compile_ast($block) ],
926             line => $opinfo->[2],
927             file => $opinfo->[3],
928             outer => $lvar_used,
929             );
930              
931 311 100       1467 if(any_in($type, qw(macro block))) {
932 235 100       1039 if(exists $self->macro_table->{$name}) {
933 2         8 my $m = $self->macro_table->{$name};
934 2 50       9 if(p(\%macro) ne p($m)) {
935 2         317 $self->_error("Redefinition of $type $name is forbidden", $node);
936             }
937             }
938 233         984 $self->macro_table->{$name} = \%macro;
939             }
940             else {
941 76         384 my $fq_name = sprintf '%s@%s', $name, $type;
942 76         146 $macro{name} = $fq_name;
943 76   50     103 push @{ $self->macro_table->{ $fq_name } ||= [] }, \%macro;
  76         655  
944             }
945 309         1492 return;
946             }
947              
948             sub _generate_lambda {
949 39     39   67 my($self, $node) = @_;
950              
951 39         93 my $macro = $node->first;
952 39         83 $self->compile_ast($macro);
953 39         196 return $self->opcode( fetch_symbol => $macro->first->id, line => $node->line );
954             }
955              
956             sub _prepare_cond_expr {
957 418     418   606 my($self, $expr) = @_;
958 418         593 my $t = "and";
959 418         609 my $f = "or";
960              
961 418         1611 while($expr->id eq '!') {
962 31         86 $expr = $expr->first;
963 31         138 ($t, $f) = ($f, $t);
964             }
965              
966 418 100 100     2182 if($expr->is_logical and any_in($expr->id, qw(== !=))) {
967 167         411 my $rhs = $expr->second;
968 167 100       618 if($rhs->arity eq "nil") {
969             # add prefix 'd' (i.e. "and" to "dand", "or" to "dor")
970 39         91 substr $t, 0, 0, 'd';
971 39         61 substr $f, 0, 0, 'd';
972              
973 39 100       189 if($expr->id eq "==") {
974 18         48 ($t, $f) = ($f, $t);
975             }
976 39         105 $expr = $expr->first;
977             }
978             }
979              
980 418         1244 return($t, $f, $expr);
981             }
982              
983             sub _generate_if {
984 393     393   670 my($self, $node) = @_;
985 393         964 my $first = $node->first;
986 393         851 my $second = $node->second;
987 393         930 my $third = $node->third;
988              
989 393         977 my($cond_true, $cond_false, $expr) = $self->_prepare_cond_expr($first);
990              
991 393         602 local $self->{lvar} = { %{$self->lvar} }; # new scope
  393         1922  
992 393         600 local $self->{const} = [ @{$self->const} ]; # new scope
  393         1450  
993 393         1244 my @cond = $self->compile_ast($expr);
994              
995 393         743 my @then = do {
996 393         879 local $self->{lvar} = { %{$self->lvar} }; # new scope
  393         1788  
997 393         684 local $self->{const} = [ @{$self->const} ]; # new scope
  393         1274  
998 393         1011 $self->compile_ast($second);
999             };
1000              
1001 393         617 my @else = do {
1002 393         523 local $self->{lvar} = { %{$self->lvar} }; # new scope
  393         1552  
1003 393         573 local $self->{const} = [ @{$self->const} ]; # new scope
  393         1257  
1004 393         861 $self->compile_ast($third);
1005             };
1006              
1007 393 50       999 if($OPTIMIZE) {
1008 393 100       961 if($self->_code_is_literal(@cond)) {
1009 100         180 my $value = $cond[0][_OP_ARG];
1010 100 100       316 if($cond_true eq 'and' ? $value : !$value) {
    100          
1011 75         502 return @then;
1012             }
1013             else {
1014 25         177 return @else;
1015             }
1016             }
1017             }
1018              
1019 293 100 100     1914 if( (@then and @else) or !$OPTIMIZE) {
    100 66        
1020             return(
1021 217         1045 @cond,
1022             $self->opcode( $cond_true => scalar(@then) + 2, comment => $node->id . ' (then)' ),
1023             @then,
1024             $self->opcode( goto => scalar(@else) + 1, comment => $node->id . ' (else)' ),
1025             @else,
1026             );
1027             }
1028             elsif(!@else) { # no @else
1029             return(
1030 72         376 @cond,
1031             $self->opcode( $cond_true => scalar(@then) + 1, comment => $node->id . ' (then/no-else)' ),
1032             @then,
1033             );
1034             }
1035             else { # no @then
1036             return(
1037 4         21 @cond,
1038             $self->opcode( $cond_false => scalar(@else) + 1, comment => $node->id . ' (else/no-then)'),
1039             @else,
1040             );
1041             }
1042             }
1043              
1044             sub _generate_given {
1045 39     39   84 my($self, $node) = @_;
1046 39         92 my $expr = $node->first;
1047 39         85 my $vars = $node->second;
1048 39         92 my $block = $node->third;
1049              
1050 39 50       50 if(@{$vars} > 1) {
  39         125  
1051 0         0 $self->_error("A given block requires one or zero variables", $node);
1052             }
1053 39         61 local $self->{lvar} = { %{$self->lvar} }; # new scope
  39         188  
1054 39         60 local $self->{const} = [ @{$self->const} ]; # new scope
  39         143  
1055              
1056 39         97 my @code = $self->compile_ast($expr);
1057              
1058 39         60 my($lvar) = @{$vars};
  39         71  
1059 39         99 my $lvar_id = $self->lvar_id;
1060 39         89 my $lvar_name = $lvar->id;
1061              
1062 39         130 $self->lvar->{$lvar_name} = $lvar_id;
1063              
1064 39         90 local $self->{lvar_id} = $self->lvar_use(1); # topic variable
1065 39         92 push @code, $self->opcode( save_to_lvar => $lvar_id, symbol => $lvar ),
1066             $self->compile_ast($block);
1067              
1068 39         267 return @code;
1069             }
1070              
1071             sub _generate_variable {
1072 3156     3156   6758 my($self, $node) = @_;
1073              
1074 3156 100       30017 if(defined(my $lvar_id = $self->lvar->{$node->value})) {
1075 420         1026 return $self->opcode( load_lvar => $lvar_id, symbol => $node );
1076             }
1077             else {
1078 2736         8439 my $name = $self->_variable_to_value($node);
1079 2736 100       9308 if($name =~ /~/) {
1080 8         33 $self->_error("Undefined iterator variable $node", $node);
1081             }
1082 2728         10643 return $self->opcode( fetch_s => $name, line => $node->line );
1083             }
1084             }
1085              
1086             sub _generate_super {
1087 7     7   15 my($self, $node) = @_;
1088              
1089 7         19 return return $self->opcode( super => undef, symbol => $node );
1090             }
1091              
1092             sub _generate_literal {
1093 7299     7299   10237 my($self, $node) = @_;
1094 7299         22892 return $self->opcode( literal => $node->value );
1095             }
1096              
1097             sub _generate_nil {
1098 69     69   153 my($self) = @_;
1099 69         171 return $self->opcode('nil');
1100             }
1101              
1102             sub _generate_vars {
1103 6     6   13 my($self) = @_;
1104 6         15 return $self->opcode('vars');
1105             }
1106              
1107             sub _generate_composer {
1108 131     131   240 my($self, $node) = @_;
1109              
1110 131         361 my $list = $node->first;
1111 131 100       558 my $type = $node->id eq '{' ? 'make_hash' : 'make_array';
1112              
1113             return
1114             $self->opcode( pushmark => undef, comment => $type ),
1115 131         337 (map{ $self->push_expr($_) } @{$list}),
  5265         10420  
  131         384  
1116             $self->opcode($type),
1117             ;
1118             }
1119              
1120             sub _generate_unary {
1121 33     33   59 my($self, $node) = @_;
1122              
1123 33         90 my $id = $node->id;
1124 33 50       104 if(exists $unary{$id}) {
1125 33         137 my @operand = $self->compile_ast($node->first);
1126             my @code = (
1127             @operand,
1128 33         116 $self->opcode( $unary{$id} )
1129             );
1130 33 100 66     187 if( $OPTIMIZE and $self->_code_is_literal(@operand) ) {
1131 17         55 $self->_fold_constants(\@code);
1132             }
1133 33         139 return @code;
1134             }
1135             else {
1136 0         0 $self->_error("Unary operator $id is not implemented", $node);
1137             }
1138             }
1139              
1140             sub _generate_field {
1141 312     312   658 my($self, $node) = @_;
1142              
1143 312         1106 my @lhs = $self->compile_ast($node->first);
1144 304         843 my $field = $node->second;
1145              
1146             # $foo.field
1147             # $foo["field"]
1148 304 100       1001 if($field->arity eq "literal") {
1149             return
1150 250         1670 @lhs,
1151             $self->opcode( fetch_field_s => $field->value );
1152             }
1153             # $foo[expression]
1154             else {
1155 54         134 local $self->{lvar_id} = $self->lvar_use(1);
1156 54         137 my @rhs = $self->compile_ast($field);
1157 54 100 66     234 if($OPTIMIZE and $self->_code_is_literal(@rhs)) {
1158             return
1159 14         44 @lhs,
1160             $self->opcode( fetch_field_s => $rhs[0][1] );
1161             }
1162             return
1163 40         188 @lhs,
1164             $self->opcode( save_to_lvar => $self->lvar_id ),
1165             @rhs,
1166             $self->opcode( load_lvar_to_sb => $self->lvar_id ),
1167             $self->opcode( 'fetch_field' ),
1168             ;
1169             }
1170              
1171             }
1172              
1173             sub _generate_binary {
1174 927     927   1440 my($self, $node) = @_;
1175              
1176 927         2996 my @lhs = $self->compile_ast($node->first);
1177              
1178 925         2388 my $id = $node->id;
1179 925 100       2533 if(exists $binary{$id}) {
    50          
1180 702         1640 local $self->{lvar_id} = $self->lvar_use(1);
1181 702         2210 my @rhs = $self->compile_ast($node->second);
1182             my @code = (
1183             @lhs,
1184             $self->opcode( save_to_lvar => $self->lvar_id ),
1185             @rhs,
1186             $self->opcode( load_lvar_to_sb => $self->lvar_id ),
1187 702         2466 $self->opcode( $binary{$id} ),
1188             );
1189              
1190 702 100       2340 if(any_in($id, qw(min max))) {
1191 26         60 local $self->{lvar_id} = $self->lvar_use(1);
1192 26         88 splice @code, -1, 0,
1193             $self->opcode(save_to_lvar => $self->lvar_id ); # save lhs
1194 26         64 push @code,
1195             $self->opcode( or => +2 , symbol => $node ),
1196             $self->opcode( load_lvar_to_sb => $self->lvar_id ), # on true
1197             # fall through
1198             $self->opcode( 'move_from_sb' ), # on false
1199             }
1200              
1201 702 50       1710 if($OPTIMIZE) {
1202 702 100 100     1764 if( $self->_code_is_literal(@lhs) and $self->_code_is_literal(@rhs) ){
1203 123         331 $self->_fold_constants(\@code);
1204             }
1205             }
1206 702         3595 return @code;
1207             }
1208             elsif(exists $logical_binary{$id}) {
1209 223         720 my @rhs = $self->compile_ast($node->second);
1210             return
1211             @lhs,
1212 223         773 $self->opcode( $logical_binary{$id} => scalar(@rhs) + 1, symbol => $node ),
1213             @rhs;
1214             }
1215              
1216 0         0 $self->_error("Binary operator $id is not implemented", $node);
1217             }
1218              
1219             sub _generate_range {
1220 7     7   16 my($self, $node) = @_;
1221              
1222 7 50       21 $self->can_be_in_list_context
1223             or $self->_error("Range operator must be in list context");
1224              
1225 7         35 my @lhs = $self->compile_ast($node->first);
1226              
1227 7         26 local $self->{lvar_id} = $self->lvar_use(1);
1228 7         30 my @rhs = $self->compile_ast($node->second);
1229             return(
1230 7         32 @lhs,
1231             $self->opcode( save_to_lvar => $self->lvar_id ),
1232             @rhs,
1233             $self->opcode( load_lvar_to_sb => $self->lvar_id ),
1234             $self->opcode( 'range' ),
1235             );
1236             }
1237              
1238             sub _generate_methodcall {
1239 230     230   420 my($self, $node) = @_;
1240              
1241 230         701 my $args = $node->third;
1242 230         1627 my $method = $node->second->value;
1243             return (
1244             $self->opcode( pushmark => undef, comment => $method ),
1245             $self->push_expr($node->first),
1246 230         581 (map { $self->push_expr($_) } @{$args}),
  137         319  
  230         731  
1247             $self->opcode( methodcall_s => $method, line => $node->line ),
1248             );
1249             }
1250              
1251             sub _generate_call {
1252 457     457   1003 my($self, $node) = @_;
1253 457         1100 my $callable = $node->first; # function or macro
1254 457         1103 my $args = $node->second;
1255              
1256 457 100 100     2356 if(my $intern = $builtin{$callable->id} and !$self->overridden_builtin->{$callable->id}) {
1257 54 50       71 if(@{$args} != 1) {
  54         158  
1258 0         0 $self->_error("Wrong number of arguments for $callable", $node);
1259             }
1260              
1261 54         162 return $self->compile_ast($args->[0]),
1262             [ $intern->[0] => undef, $node->line ];
1263             }
1264              
1265             return(
1266             $self->opcode( pushmark => undef, comment => $callable->id ),
1267 403         1436 (map { $self->push_expr($_) } @{$args}),
  273         665  
  403         1104  
1268             $self->compile_ast($callable),
1269             $self->opcode( 'funcall' )
1270             );
1271             }
1272              
1273             # $~iterator
1274             sub _generate_iterator {
1275 43     43   70 my($self, $node) = @_;
1276              
1277 43         98 my $item_var = $node->first;
1278 43         221 my $lvar_id = $self->lvar->{$item_var};
1279 43 50       119 if(!defined($lvar_id)) {
1280 0         0 $self->_error("Refer to iterator $node, but $item_var is not defined",
1281             $node);
1282             }
1283              
1284 43         112 return $self->opcode(
1285             load_lvar => $lvar_id + 1,
1286             symbol => $node,
1287             );
1288             }
1289              
1290             # $~iterator.body
1291             sub _generate_iterator_body {
1292 16     16   34 my($self, $node) = @_;
1293              
1294 16         39 my $item_var = $node->first;
1295 16         77 my $lvar_id = $self->lvar->{$item_var};
1296 16 50       54 if(!defined($lvar_id)) {
1297 0         0 $self->_error("Refer to iterator $node.body, but $item_var is not defined",
1298             $node);
1299             }
1300              
1301 16         58 return $self->opcode(
1302             load_lvar => $lvar_id + 2,
1303             symbol => $node,
1304             );
1305             }
1306              
1307             sub _generate_assign {
1308 59     59   94 my($self, $node) = @_;
1309 59         142 my $lhs = $node->first;
1310 59         163 my $rhs = $node->second;
1311 59         129 my $is_decl = $node->third;
1312              
1313 59         145 my $lvar = $self->lvar;
1314 59         137 my $lvar_name = $lhs->id;
1315              
1316 59 50       212 if($node->id ne "=") {
1317 0         0 $self->_error("Assignment ($node) is not supported", $node);
1318             }
1319              
1320 59         202 my @expr = $self->compile_ast($rhs);
1321              
1322 59 100       454 if($is_decl) {
1323 47         181 $lvar->{$lvar_name} = $self->lvar_id;
1324 47         124 $self->{lvar_id} = $self->lvar_use(1); # don't use local()
1325             }
1326              
1327 59 100 66     443 if(!exists $lvar->{$lvar_name} or $lhs->arity ne "variable") {
1328 1         53 $self->_error("Cannot modify $lhs, which is not a lexical variable", $node);
1329             }
1330              
1331             return
1332             @expr,
1333 58         263 $self->opcode( save_to_lvar => $lvar->{$lvar_name}, symbol => $lhs, comment => $node->id);
1334             }
1335              
1336             sub _generate_constant {
1337 72     72   131 my($self, $node) = @_;
1338 72         182 my $lhs = $node->first;
1339 72         154 my $rhs = $node->second;
1340              
1341 72         189 my @expr = $self->compile_ast($rhs);
1342              
1343 72         188 my $lvar = $self->lvar;
1344 72         179 my $lvar_id = $self->lvar_id;
1345 72         174 my $lvar_name = $lhs->id;
1346 72         151 $lvar->{$lvar_name} = $lvar_id;
1347 72         180 $self->{lvar_id} = $self->lvar_use(1); # don't use local()
1348              
1349 72 50       189 if($OPTIMIZE) {
1350 72 100 100     312 if(@expr == 1
1351             && any_in($expr[0][_OP_NAME], qw(literal load_lvar))) {
1352 33         82 $expr[0][_OP_COMMENT] = "constant $lvar_name";
1353 33         104 $self->const->[$lvar_id] = \@expr;
1354 33         109 return @expr; # no real definition
1355             }
1356             }
1357              
1358             return
1359 39         147 @expr,
1360             $self->opcode( save_to_lvar => $lvar_id, symbol => $lhs, comment => $node->id);
1361             }
1362              
1363             sub _localize_vars {
1364 31     31   65 my($self, $vars) = @_;
1365 31         41 my @localize;
1366 31         42 my @pairs = @{$vars};
  31         71  
1367              
1368 31 100       106 if( (@pairs % 2) != 0 ) {
1369 8 100       24 if(@pairs == 1) {
1370 7         19 return $self->compile_ast(@pairs),
1371             $self->opcode( 'localize_vars' );
1372             }
1373             else {
1374 1         5 $self->_error("You must pass pairs of expressions to include");
1375             }
1376             }
1377              
1378 23         105 while(my($key, $expr) = splice @pairs, 0, 2) {
1379 28 50       129 if(!any_in($key->arity, qw(literal variable))) {
1380 0         0 $self->_error("You must pass a simple name to localize variables", $key);
1381             }
1382 28         80 push @localize,
1383             $self->compile_ast($expr),
1384             $self->opcode( localize_s => $key->value, symbol => $key );
1385             }
1386 23         92 return @localize;
1387             }
1388              
1389             sub _variable_to_value {
1390 2736     2736   5897 my($self, $arg) = @_;
1391              
1392 2736         8086 my $name = $arg->value;
1393 2736         10257 $name =~ s/\$//;
1394 2736         10265 return $name;
1395             }
1396              
1397             sub requires {
1398 102     102 0 241 my($self, @files) = @_;
1399 102         149 push @{ $self->dependencies }, @files;
  102         428  
1400 102         253 return;
1401             }
1402              
1403             sub can_be_in_list_context {
1404 7     7 0 13 my $i = 2;
1405 7         59 while(my $funcname = (caller ++$i)[3]) {
1406 14 100       111 if($funcname =~ /::_generate_(\w+) \z/xms) {
1407 7         31 return any_in($1, qw(
1408             methodcall
1409             call
1410             composer
1411             ));
1412             }
1413             }
1414 0         0 return 0;
1415             }
1416              
1417             # optimizatin stuff
1418              
1419             sub _code_is_literal {
1420 1340     1340   2549 my($self, @code) = @_;
1421 1340   66     9776 return @code == 1
1422             && ( $code[0][_OP_NAME] eq 'literal'
1423             || $code[0][_OP_NAME] eq 'literal_i');
1424             }
1425              
1426             sub _fold_constants {
1427 140     140   225 my($self, $code) = @_;
1428 140 50       501 my $engine = $self->engine or return 0;
1429              
1430 140         385 local $engine->{warn_handler} = \&Carp::croak;
1431 140         322 local $engine->{die_handler} = \&Carp::croak;
1432 140         310 local $engine->{verbose} = 1;
1433              
1434 140         216 my $result = eval {
1435 140         174 my @tmp_code = (@{$code}, $self->opcode('print_raw'), $self->opcode('end'));
  140         369  
1436 140         2490 $engine->_assemble(\@tmp_code, '', undef, undef, undef);
1437 140         2806 $engine->render('');
1438             };
1439 140 50       444 if($@) {
1440 0         0 Carp::carp("[BUG] Constant folding failed (ignored): $@");
1441 0         0 return 0;
1442             }
1443              
1444 140         347 @{$code} = ($self->opcode( literal => $result, comment => "optimized by constant folding"));
  140         525  
1445 140         491 return 1;
1446             }
1447              
1448              
1449             sub _noop {
1450 6216     6216   8506 my($self, $op) = @_;
1451 6216         6959 @{$op} = @{ $self->opcode( noop => undef, comment => "ex-$op->[0]") };
  6216         19538  
  6216         17925  
1452 6216         14131 return;
1453             }
1454              
1455             sub _optimize_vmcode {
1456 10113     10113   20519 my($self, $c) = @_;
1457              
1458             # calculate goto addresses
1459             # eg:
1460             #
1461             # goto +3
1462             # foo
1463             # noop
1464             # bar // goto destination
1465             #
1466             # to be:
1467             #
1468             # goto +2
1469             # foo
1470             # bar // goto destination
1471              
1472 10113         16780 my @goto_addr;
1473 10113         19924 for(my $i = 0; $i < @{$c}; $i++) {
  135194         371128  
1474 125081 100       380598 if(exists $goto_family{ $c->[$i][_OP_NAME] }) {
1475 3459         4961 my $addr = $c->[$i][_OP_ARG]; # relational addr
1476              
1477             # mark ragens that goto family have its effects
1478 3459 100       11009 my @range = $addr > 0
1479             ? ($i .. ($i+$addr-1)) # positive
1480             : (($i+$addr) .. $i); # negative
1481              
1482 3459         6159 foreach my $j(@range) {
1483 21752   100     23978 push @{$goto_addr[$j] ||= []}, $c->[$i];
  21752         82369  
1484             }
1485             }
1486             }
1487              
1488 10113         20737 for(my $i = 0; $i < @{$c}; $i++) {
  135194         382341  
1489 125081         209257 my $name = $c->[$i][_OP_NAME];
1490 125081 100       486034 if($name eq 'print_raw_s') {
    100          
    100          
    100          
1491             # merge a chain of print_raw_s into single command
1492 14421         29934 my $j = $i + 1; # from the next op
1493 14421   66     28324 while($j < @{$c}
  20057   100     158150  
1494             && $c->[$j][_OP_NAME] eq 'print_raw_s'
1495 5806 100       23430 && "@{$goto_addr[$i] || []}" eq "@{$goto_addr[$j] || []}") {
  5806 100       33893  
1496              
1497 5636         13019 $c->[$i][_OP_ARG] .= $c->[$j][_OP_ARG];
1498              
1499 5636         11692 $self->_noop($c->[$j]);
1500 5636         7049 $j++;
1501             }
1502             }
1503             elsif($name eq 'save_to_lvar') {
1504             # use registers, instead of local variables
1505             #
1506             # given:
1507             # save_to_lvar $n
1508             #
1509             # load_lvar_to_sb $n
1510             # convert into:
1511             # move_to_sb
1512             #
1513 1222         1733 my $it = $c->[$i];
1514 1222         2083 my $nn = $c->[$i+2]; # next next
1515 1222 100 66     7858 if(defined($nn)
      66        
1516             && $nn->[_OP_NAME] eq 'load_lvar_to_sb'
1517             && $nn->[_OP_ARG] == $it->[_OP_ARG]) {
1518 580         716 @{$it} = @{$self->opcode( move_to_sb => undef, comment => "ex-$it->[0]" )};
  580         2079  
  580         1951  
1519              
1520 580         2125 $self->_noop($nn);
1521             }
1522             }
1523             elsif($name eq 'literal') {
1524 19333 100       50770 if(is_int($c->[$i][_OP_ARG])) {
1525 916         1760 $c->[$i][_OP_NAME] = 'literal_i';
1526 916         2795 $c->[$i][_OP_ARG] = int($c->[$i][_OP_ARG]); # force int
1527             }
1528             }
1529             elsif($name eq 'fetch_field') {
1530 138         263 my $prev = $c->[$i-1];
1531 138 50       462 if($prev->[_OP_NAME] =~ /^literal/) { # literal or literal_i
1532 0         0 $c->[$i][_OP_NAME] = 'fetch_field_s';
1533 0         0 $c->[$i][_OP_ARG] = $prev->[_OP_ARG]; # arg
1534              
1535 0         0 $self->_noop($prev);
1536             }
1537             }
1538             }
1539              
1540             # remove noop
1541 10113         21123 for(my $i = 0; $i < @{$c}; $i++) {
  129618         350079  
1542 119505 100       345434 if($c->[$i][_OP_NAME] eq 'noop') {
1543 5576 100       10711 if(defined $goto_addr[$i]) {
1544 388         473 foreach my $goto(@{ $goto_addr[$i] }) {
  388         833  
1545             # reduce its absolute value
1546 596 100       1484 $goto->[1] > 0
1547             ? $goto->[1]-- # positive
1548             : $goto->[1]++; # negative
1549             }
1550             }
1551 5576         6151 splice @{$c}, $i, 1;
  5576         11635  
1552             # adjust @goto_addr, but it may be empty
1553 5576 100       18430 splice @goto_addr, $i, 1 if @goto_addr > $i;
1554             }
1555             }
1556 10113         46942 return;
1557             }
1558              
1559             sub as_assembly {
1560 7     7 0 13 my($self, $code_ref, $addix) = @_;
1561              
1562 7         12 my $asm = "";
1563 7         12 foreach my $ix(0 .. (@{$code_ref}-1)) {
  7         18  
1564 47         56 my($name, $arg, $line, $file, $label, $comment) = @{$code_ref->[$ix]};
  47         114  
1565 47 50       98 $asm .= "$ix:" if $addix; # for debugging
1566              
1567             # "$opname $arg #$line:$file *$symbol // $comment"
1568 47 50       95 ref($name) and die "Oops: " . p($code_ref->[$ix]);
1569 47         65 $asm .= $name;
1570 47 100       96 if(defined $arg) {
1571 11         30 $asm .= " " . value_to_literal($arg);
1572             }
1573 47 100       111 if(defined $line) {
1574 28         38 $asm .= " #$line";
1575 28 100       94 if(defined $file) {
1576 7         23 $asm .= ":" . value_to_literal($file);
1577             }
1578             }
1579 47 50       97 if(defined $label) {
1580 0         0 $asm .= " " . value_to_literal($label);
1581             }
1582 47 100       94 if(defined $comment) {
1583 4         8 $asm .= " // $comment";
1584             }
1585 47         86 $asm .= "\n";
1586             }
1587 7         111 return $asm;
1588             }
1589              
1590             sub _error {
1591 18     18   38 my($self, $message, $node) = @_;
1592              
1593 18 100       76 my $line = ref($node) ? $node->line : $node;
1594 18         105 die $self->make_error($message, $self->file, $line);
1595             }
1596              
1597 169     169   1719 no Mouse;
  169         381  
  169         1450  
1598 169     169   23705 no Mouse::Util::TypeConstraints;
  169         351  
  169         1367  
1599              
1600             __PACKAGE__->meta->make_immutable;
1601             __END__