| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Text::Xslate::Compiler; | 
| 2 | 169 |  |  | 169 |  | 176077 | use Mouse; | 
|  | 169 |  |  |  |  | 347002 |  | 
|  | 169 |  |  |  |  | 1142 |  | 
| 3 | 169 |  |  | 169 |  | 60101 | use Mouse::Util::TypeConstraints; | 
|  | 169 |  |  |  |  | 383 |  | 
|  | 169 |  |  |  |  | 1217 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 169 |  |  | 169 |  | 14624 | use Scalar::Util (); | 
|  | 169 |  |  |  |  | 346 |  | 
|  | 169 |  |  |  |  | 2792 |  | 
| 6 | 169 |  |  | 169 |  | 910 | use Carp         (); | 
|  | 169 |  |  |  |  | 338 |  | 
|  | 169 |  |  |  |  | 3006 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 169 |  |  | 169 |  | 108705 | use Text::Xslate::Parser; | 
|  | 169 |  |  |  |  | 15970 |  | 
|  | 169 |  |  |  |  | 7164 |  | 
| 9 | 169 |  |  |  |  | 40655 | use Text::Xslate::Util qw( | 
| 10 |  |  |  |  |  |  | $DEBUG | 
| 11 |  |  |  |  |  |  | value_to_literal | 
| 12 |  |  |  |  |  |  | is_int any_in | 
| 13 |  |  |  |  |  |  | make_error | 
| 14 |  |  |  |  |  |  | p | 
| 15 | 169 |  |  | 169 |  | 1079 | ); | 
|  | 169 |  |  |  |  | 312 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | #use constant _VERBOSE  => scalar($DEBUG =~ /\b verbose \b/xms); | 
| 18 |  |  |  |  |  |  | use constant { | 
| 19 | 169 |  |  |  |  | 1767190 | _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 |  | 1057 | }; | 
|  | 169 |  |  |  |  | 342 |  | 
| 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 |  | 8410 | my($self) = @_; | 
| 206 | 238 |  |  |  |  | 2481 | my $syntax = $self->syntax; | 
| 207 | 238 | 100 |  |  |  | 2660 | if(ref($syntax)) { | 
| 208 | 1 |  |  |  |  | 5 | return $syntax; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  | else { | 
| 211 | 237 |  |  |  |  | 3312 | my $parser_class = Mouse::Util::load_first_existing_class( | 
| 212 |  |  |  |  |  |  | "Text::Xslate::Syntax::" . $syntax, | 
| 213 |  |  |  |  |  |  | $syntax, | 
| 214 |  |  |  |  |  |  | ); | 
| 215 |  |  |  |  |  |  | return $parser_class->new( | 
| 216 | 237 |  |  |  |  | 34957 | %{$self->parser_option}, | 
|  | 237 |  |  |  |  | 8199 |  | 
| 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 | 2249 | my($self, $n) = @_; | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 1459 |  |  |  |  | 5399 | return $self->lvar_id + $n; | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | sub filename { | 
| 259 | 48120 |  |  | 48120 | 0 | 70645 | my($self) = @_; | 
| 260 | 48120 |  |  |  |  | 109492 | my $file = $self->file; | 
| 261 | 48120 | 100 |  |  |  | 135597 | return ref($file) ? '' : $file; | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | sub compile { | 
| 265 | 3439 |  |  | 3439 | 0 | 20303 | my($self, $input, %args) = @_; | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | # each compiling process is independent | 
| 268 | 3439 |  |  |  |  | 11697 | local $self->{macro_table}  = {}; | 
| 269 | 3439 |  |  |  |  | 9146 | local $self->{lvar_id     } = 0; | 
| 270 | 3439 |  |  |  |  | 8759 | local $self->{lvar}         = {}; | 
| 271 | 3439 |  |  |  |  | 9665 | local $self->{const}        = []; | 
| 272 | 3439 |  |  |  |  | 8249 | local $self->{in_loop}      = 0; | 
| 273 | 3439 |  |  |  |  | 8406 | local $self->{dependencies} = []; | 
| 274 | 3439 |  |  |  |  | 7632 | local $self->{cascade}; | 
| 275 | 3439 |  |  |  |  | 8382 | local $self->{header}       = $self->{header}; | 
| 276 | 3439 |  |  |  |  | 8177 | local $self->{footer}       = $self->{footer}; | 
| 277 | 3439 |  |  |  |  | 7819 | local $self->{macro}        = $self->{macro}; | 
| 278 | 3439 |  |  |  |  | 8478 | local $self->{current_file} = ''; # for opinfo | 
| 279 | 3439 |  | 100 |  |  | 15678 | local $self->{file}         = $args{file} || \$input; | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 3439 | 100 |  |  |  | 16939 | if(my $engine = $self->engine) { | 
| 282 | 3434 |  |  |  |  | 9973 | my $ob = $self->overridden_builtin; | 
| 283 | 3434 |  |  |  |  | 9142 | Internals::SvREADONLY($ob, 0); | 
| 284 | 3434 |  |  |  |  | 13541 | foreach my $name(keys %builtin) { | 
| 285 | 30906 |  |  |  |  | 63058 | my $f = $engine->{function}{$name}; | 
| 286 | 30906 |  |  |  |  | 81846 | $ob->{$name} = ( $builtin{$name}[1] != $f ) + 0; | 
| 287 |  |  |  |  |  |  | } | 
| 288 | 3434 |  |  |  |  | 13233 | Internals::SvREADONLY($ob, 1); | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 | 3439 |  |  |  |  | 11089 | my $parser = $self->parser; | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 3439 |  |  |  |  | 7864 | my $header = delete $self->{header}; | 
| 294 | 3439 |  |  |  |  | 7376 | my $footer = delete $self->{footer}; | 
| 295 | 3439 |  |  |  |  | 7157 | my $macro = delete $self->{macro}; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 3439 | 100 |  |  |  | 10364 | if(!$args{omit_augment}) { | 
| 298 | 2212 | 100 |  |  |  | 6584 | if($header) { | 
| 299 | 9 |  |  |  |  | 28 | substr $input, 0, 0, $self->_cat_files($header); | 
| 300 |  |  |  |  |  |  | } | 
| 301 | 2212 | 100 |  |  |  | 8282 | if($footer) { | 
| 302 | 9 |  |  |  |  | 25 | $input .= $self->_cat_files($footer); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | } | 
| 305 | 3439 | 100 |  |  |  | 8375 | if($macro) { | 
| 306 | 2 | 50 |  |  |  | 4 | if(!grep { $_ eq $self->current_file } @$macro) { | 
|  | 2 |  |  |  |  | 15 |  | 
| 307 | 2 |  |  |  |  | 13 | substr $input, 0, 0, $self->_cat_files($macro); | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 3439 |  |  |  |  | 5850 | my @code; # main code | 
| 312 |  |  |  |  |  |  | { | 
| 313 | 3439 |  |  |  |  | 5900 | my $ast = $parser->parse($input, %args); | 
|  | 3439 |  |  |  |  | 16246 |  | 
| 314 | 3390 |  |  |  |  | 7699 | print STDERR p($ast) if _DUMP_AST; | 
| 315 | 3390 |  |  |  |  | 15247 | @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 | 3375 |  |  |  |  | 13237 | my $cascade = $self->cascade; | 
| 323 | 3375 | 100 |  |  |  | 9244 | if(defined $cascade) { | 
| 324 | 73 |  |  |  |  | 265 | $self->_process_cascade($cascade, \%args, \@code); | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 3370 | 50 |  |  |  | 16882 | push @code, $self->_flush_macro_table() if $self->has_macro_table; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 3370 | 50 |  |  |  | 9181 | if($OPTIMIZE) { | 
| 330 | 3370 |  |  |  |  | 15298 | $self->_optimize_vmcode(\@code) for 1 .. 3; | 
| 331 |  |  |  |  |  |  | } | 
| 332 |  |  |  |  |  |  |  | 
| 333 | 3370 |  |  |  |  | 5948 | print STDERR "// ", $self->filename, "\n", | 
| 334 |  |  |  |  |  |  | $self->as_assembly(\@code, scalar($DEBUG =~ /\b ix \b/xms)) | 
| 335 |  |  |  |  |  |  | if _DUMP_ASM; | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | { | 
| 338 | 3370 |  |  |  |  | 6262 | my %uniq; | 
|  | 3370 |  |  |  |  | 6013 |  | 
| 339 |  |  |  |  |  |  | push @code, | 
| 340 | 84 |  |  |  |  | 339 | map  { [ depend => $_ ] } | 
| 341 | 3370 |  | 66 |  |  | 6709 | grep { !ref($_) and !$uniq{$_}++ } @{$self->dependencies}; | 
|  | 100 |  |  |  |  | 679 |  | 
|  | 3370 |  |  |  |  | 16218 |  | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  |  | 
| 344 | 3370 |  |  |  |  | 36436 | return \@code; | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | sub opcode { # build an opcode | 
| 348 | 51768 |  |  | 51768 | 0 | 122743 | my($self, $name, $arg, %args) = @_; | 
| 349 | 51768 |  |  |  |  | 82225 | my $symbol = $args{symbol}; | 
| 350 | 51768 |  |  |  |  | 77379 | my $file   = $args{file}; | 
| 351 | 51768 |  |  |  |  | 74611 | my $label  = $args{label}; | 
| 352 | 51768 | 100 |  |  |  | 115224 | if(not defined $file) { | 
| 353 | 47809 |  |  |  |  | 103701 | $file = $self->filename; | 
| 354 | 47809 | 100 | 66 |  |  | 243873 | if(defined $file and $file ne $self->current_file) { | 
| 355 | 1957 |  |  |  |  | 10268 | $self->current_file($file); | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  | else { | 
| 358 | 45852 |  |  |  |  | 86015 | $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 | 51768 |  | 66 |  |  | 417164 | ]; | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | sub push_expr { | 
| 371 | 5905 |  |  | 5905 | 0 | 8288 | my($self, $node) = @_; | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 5905 |  |  |  |  | 15935 | my $list_op = $node->arity eq 'range'; | 
| 374 | 5905 |  |  |  |  | 11543 | my @code = ($self->compile_ast($node)); | 
| 375 | 5905 | 100 |  |  |  | 12508 | if(not $list_op) { | 
| 376 | 5898 |  |  |  |  | 11021 | push @code, $self->opcode('push'); | 
| 377 |  |  |  |  |  |  | } | 
| 378 | 5905 |  |  |  |  | 15889 | return @code; | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | sub _cat_files { | 
| 383 | 20 |  |  | 20 |  | 36 | my($self, $files) = @_; | 
| 384 | 20 |  | 33 |  |  | 82 | my $engine = $self->engine || $self->_error("No Xslate engine which header/footer requires"); | 
| 385 | 20 |  |  |  |  | 35 | my $s = ''; | 
| 386 | 20 |  |  |  |  | 30 | foreach my $file(@{$files}) { | 
|  | 20 |  |  |  |  | 45 |  | 
| 387 | 26 |  |  |  |  | 104 | my $fullpath = $engine->find_file($file)->{fullpath}; | 
| 388 | 26 |  |  |  |  | 160 | $s .= $engine->slurp_template( $self->input_layer, $fullpath ); | 
| 389 | 26 |  |  |  |  | 84 | $self->requires($fullpath); | 
| 390 |  |  |  |  |  |  | } | 
| 391 | 20 |  |  |  |  | 79 | return $s; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | our $_lv = -1; | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | sub compile_ast { | 
| 397 | 17849 |  |  | 17849 | 0 | 29001 | my($self, $ast) = @_; | 
| 398 | 17849 | 100 |  |  |  | 39098 | return if not defined $ast; | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 17705 |  |  |  |  | 20945 | local $_lv = $_lv + 1 if _DUMP_GEN; | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 17705 |  |  |  |  | 22609 | my @code; | 
| 403 | 17705 | 100 |  |  |  | 46340 | foreach my $node(ref($ast) eq 'ARRAY' ? @{$ast} : $ast) { | 
|  | 4585 |  |  |  |  | 13300 |  | 
| 404 | 28252 | 50 |  |  |  | 97935 | Scalar::Util::blessed($node) or Carp::confess("[BUG] Not a node object: " . p($node)); | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 28252 |  |  |  |  | 42787 | printf STDERR "%s"."generate %s (%s)\n", "." x $_lv, $node->arity, $node->id if _DUMP_GEN; | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 28252 |  | 33 |  |  | 151449 | my $generator = $self->can('_generate_' . $node->arity) | 
| 409 |  |  |  |  |  |  | || Carp::confess("[BUG] Unexpected node:  " . p($node)); | 
| 410 |  |  |  |  |  |  |  | 
| 411 | 28252 |  |  |  |  | 69132 | push @code, $self->$generator($node); | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 | 17671 |  |  |  |  | 65009 | return @code; | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | sub _process_cascade { | 
| 418 | 73 |  |  | 73 |  | 135 | my($self, $cascade, $args, $main_code) = @_; | 
| 419 | 73 |  |  |  |  | 107 | printf STDERR "# cascade %s %s", $self->file, $cascade->dump if _DUMP_CAS; | 
| 420 | 73 |  | 33 |  |  | 318 | my $engine = $self->engine | 
| 421 |  |  |  |  |  |  | || $self->_error("Cannot cascade templates without Xslate engine", $cascade); | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 73 |  |  |  |  | 116 | my($base_file, $base_code); | 
| 424 | 73 |  |  |  |  | 182 | my $base       = $cascade->first; | 
| 425 |  |  |  |  |  |  | my @components = $cascade->second | 
| 426 | 73 | 100 |  |  |  | 272 | ? (map{ $self->_bare_to_file($_) } @{$cascade->second}) | 
|  | 13 |  |  |  |  | 34 |  | 
|  | 11 |  |  |  |  | 33 |  | 
| 427 |  |  |  |  |  |  | : (); | 
| 428 | 73 |  |  |  |  | 194 | my $vars       = $cascade->third; | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 73 | 100 |  |  |  | 179 | if(defined $base) { # pure cascade | 
| 431 | 66 |  |  |  |  | 200 | $base_file = $self->_bare_to_file($base); | 
| 432 | 65 |  |  |  |  | 425 | $base_code = $engine->load_file($base_file); | 
| 433 | 63 |  |  |  |  | 262 | $self->requires( $engine->find_file($base_file)->{fullpath} ); | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  | else { # overlay | 
| 436 | 7 |  |  |  |  | 13 | $base_file = $args->{file}; # only for error messages | 
| 437 | 7 |  |  |  |  | 146 | $base_code = $main_code; | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 7 | 50 |  |  |  | 19 | if(defined $args->{fullpath}) { | 
| 440 | 0 |  |  |  |  | 0 | $self->requires( $args->{fullpath} ); | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 7 |  |  |  |  | 9 | push @{$main_code}, $self->_flush_macro_table(); | 
|  | 7 |  |  |  |  | 23 |  | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  |  | 
| 446 | 70 |  |  |  |  | 236 | foreach my $cfile(@components) { | 
| 447 | 13 |  |  |  |  | 52 | my $code     = $engine->load_file($cfile); | 
| 448 | 13 |  |  |  |  | 46 | my $fullpath = $engine->find_file($cfile)->{fullpath}; | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 13 |  |  |  |  | 83 | my $mtable   = $self->macro_table; | 
| 451 | 13 |  |  |  |  | 16 | my $macro; | 
| 452 | 13 |  |  |  |  | 19 | foreach my $c(@{$code}) { | 
|  | 13 |  |  |  |  | 35 |  | 
| 453 |  |  |  |  |  |  | # $c = [name, arg, line, file, symbol ] | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | # retrieve macros from assembly code | 
| 456 | 158 | 100 |  |  |  | 392 | if($c->[_OP_NAME] eq 'macro_begin' .. $c->[_OP_NAME] eq 'macro_end') { | 
|  |  | 50 |  |  |  |  |  | 
| 457 | 100 | 100 |  |  |  | 387 | if($c->[_OP_NAME] eq 'macro_begin') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 458 | 23 |  |  |  |  | 36 | $macro = []; | 
| 459 | 23 |  |  |  |  | 95 | $macro = { | 
| 460 |  |  |  |  |  |  | name  => $c->[_OP_ARG], | 
| 461 |  |  |  |  |  |  | line  => $c->[_OP_LINE], | 
| 462 |  |  |  |  |  |  | file  => $c->[_OP_FILE], | 
| 463 |  |  |  |  |  |  | body  => [], | 
| 464 |  |  |  |  |  |  | }; | 
| 465 | 23 |  | 50 |  |  | 40 | push @{ $mtable->{$c->[_OP_ARG]} ||= [] }, $macro; | 
|  | 23 |  |  |  |  | 142 |  | 
| 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 |  |  |  |  | 55 | push @{$macro->{body}}, $c; | 
|  | 54 |  |  |  |  | 125 |  | 
| 478 |  |  |  |  |  |  | } | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  | elsif($c->[_OP_NAME] eq 'depend') { | 
| 481 | 0 |  |  |  |  | 0 | $self->requires($c->[_OP_ARG]); | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  | } | 
| 484 | 13 |  |  |  |  | 36 | $self->requires($fullpath); | 
| 485 | 13 |  |  |  |  | 34 | $self->_process_cascade_file($cfile, $base_code); | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 70 | 100 |  |  |  | 193 | if(defined $base) { # pure cascade | 
| 489 | 63 |  |  |  |  | 200 | $self->_process_cascade_file($base_file, $base_code); | 
| 490 | 61 | 100 |  |  |  | 150 | if(defined $vars) { | 
| 491 | 13 |  |  |  |  | 19 | unshift @{$base_code}, $self->_localize_vars($vars); | 
|  | 13 |  |  |  |  | 47 |  | 
| 492 |  |  |  |  |  |  | } | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 61 |  |  |  |  | 104 | foreach my $c(@{$main_code}) { | 
|  | 61 |  |  |  |  | 131 |  | 
| 495 | 166 | 50 | 66 |  |  | 703 | 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 |  |  |  |  | 100 | @{$main_code} = @{$base_code}; | 
|  | 61 |  |  |  |  | 457 |  | 
|  | 61 |  |  |  |  | 111 |  | 
| 501 |  |  |  |  |  |  | } | 
| 502 |  |  |  |  |  |  | else { # overlay | 
| 503 | 7 |  |  |  |  | 17 | return; | 
| 504 |  |  |  |  |  |  | } | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | sub _process_cascade_file { | 
| 508 | 76 |  |  | 76 |  | 154 | my($self, $file, $base_code) = @_; | 
| 509 | 76 |  |  |  |  | 94 | printf STDERR "# cascade file %s\n", p($file) if _DUMP_CAS; | 
| 510 | 76 |  |  |  |  | 222 | my $mtable = $self->macro_table; | 
| 511 |  |  |  |  |  |  |  | 
| 512 | 76 |  |  |  |  | 136 | for(my $i = 0; $i < @{$base_code}; $i++) { | 
|  | 803 |  |  |  |  | 1807 |  | 
| 513 | 729 |  |  |  |  | 962 | my $c = $base_code->[$i]; | 
| 514 | 729 | 100 |  |  |  | 1633 | if($c->[_OP_NAME] ne 'macro_begin') { | 
| 515 | 661 |  |  |  |  | 986 | next; | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | # macro | 
| 519 | 68 |  |  |  |  | 123 | my $name = $c->[_OP_ARG]; | 
| 520 | 68 |  |  |  |  | 180 | $name =~ s/\@.+$//; | 
| 521 | 68 |  |  |  |  | 89 | printf STDERR "# macro %s\n", $name if _DUMP_CAS; | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 68 | 100 |  |  |  | 236 | if(exists $mtable->{$name}) { | 
| 524 | 2 |  |  |  |  | 3 | my $m = $mtable->{$name}; | 
| 525 | 2 | 50 |  |  |  | 7 | 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 |  |  |  |  | 13 | ); | 
| 535 |  |  |  |  |  |  | } | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 66 |  |  |  |  | 236 | my $before = delete $mtable->{$name . '@before'}; | 
| 538 | 66 |  |  |  |  | 182 | my $around = delete $mtable->{$name . '@around'}; | 
| 539 | 66 |  |  |  |  | 168 | my $after  = delete $mtable->{$name . '@after'}; | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 66 | 100 |  |  |  | 192 | if(defined $before) { | 
| 542 | 25 |  |  |  |  | 36 | my $n = scalar @{$base_code}; | 
|  | 25 |  |  |  |  | 50 |  | 
| 543 | 25 |  |  |  |  | 38 | foreach my $m(@{$before}) { | 
|  | 25 |  |  |  |  | 58 |  | 
| 544 | 25 |  |  |  |  | 40 | splice @{$base_code}, $i+1, 0, @{$m->{body}}; | 
|  | 25 |  |  |  |  | 48 |  | 
|  | 25 |  |  |  |  | 113 |  | 
| 545 |  |  |  |  |  |  | } | 
| 546 | 25 |  |  |  |  | 36 | $i += scalar(@{$base_code}) - $n; | 
|  | 25 |  |  |  |  | 57 |  | 
| 547 |  |  |  |  |  |  | } | 
| 548 |  |  |  |  |  |  |  | 
| 549 | 66 |  |  |  |  | 113 | my $macro_start = $i+1; | 
| 550 | 66 |  |  |  |  | 602 | $i++ while($base_code->[$i][_OP_NAME] ne 'macro_end'); # move to the end | 
| 551 |  |  |  |  |  |  |  | 
| 552 | 66 | 100 |  |  |  | 166 | if(defined $around) { | 
| 553 | 21 |  |  |  |  | 33 | my @original = splice @{$base_code}, $macro_start, ($i - $macro_start); | 
|  | 21 |  |  |  |  | 87 |  | 
| 554 | 21 |  |  |  |  | 38 | $i = $macro_start; | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 21 |  |  |  |  | 32 | my @body; | 
| 557 | 21 |  |  |  |  | 32 | foreach my $m(@{$around}) { | 
|  | 21 |  |  |  |  | 49 |  | 
| 558 | 21 |  |  |  |  | 33 | push @body, @{$m->{body}}; | 
|  | 21 |  |  |  |  | 80 |  | 
| 559 |  |  |  |  |  |  | } | 
| 560 | 21 |  |  |  |  | 83 | for(my $j = 0; $j < @body; $j++) { | 
| 561 | 142 | 100 |  |  |  | 494 | if($body[$j][_OP_NAME] eq 'super') { | 
| 562 | 7 |  |  |  |  | 33 | splice @body, $j, 1, @original; | 
| 563 |  |  |  |  |  |  | } | 
| 564 |  |  |  |  |  |  | } | 
| 565 | 21 |  |  |  |  | 44 | splice @{$base_code}, $macro_start, 0, @body; | 
|  | 21 |  |  |  |  | 61 |  | 
| 566 |  |  |  |  |  |  |  | 
| 567 | 21 |  |  |  |  | 69 | $i += scalar(@body); | 
| 568 |  |  |  |  |  |  | } | 
| 569 |  |  |  |  |  |  |  | 
| 570 | 66 | 100 |  |  |  | 240 | if(defined $after) { | 
| 571 | 24 |  |  |  |  | 34 | foreach my $m(@{$after}) { | 
|  | 24 |  |  |  |  | 70 |  | 
| 572 | 24 |  |  |  |  | 35 | splice @{$base_code}, $i, 0, @{$m->{body}}; | 
|  | 24 |  |  |  |  | 44 |  | 
|  | 24 |  |  |  |  | 180 |  | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  | } | 
| 576 | 74 |  |  |  |  | 219 | return; | 
| 577 |  |  |  |  |  |  | } | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | sub _flush_macro_table { | 
| 581 | 3377 |  |  | 3377 |  | 6900 | my($self) = @_; | 
| 582 | 3377 |  |  |  |  | 9561 | my $mtable = $self->macro_table; | 
| 583 | 3377 |  |  |  |  | 5765 | my @code; | 
| 584 | 3377 |  |  |  |  | 6212 | foreach my $macros(values %{$mtable}) { | 
|  | 3377 |  |  |  |  | 13567 |  | 
| 585 | 258 | 100 |  |  |  | 772 | foreach my $macro(ref($macros) eq 'ARRAY' ? @{$macros} : $macros) { | 
|  | 29 |  |  |  |  | 51 |  | 
| 586 |  |  |  |  |  |  | push @code, | 
| 587 |  |  |  |  |  |  | $self->opcode( macro_begin => $macro->{name}, | 
| 588 |  |  |  |  |  |  | file => $macro->{file}, | 
| 589 | 258 |  |  |  |  | 845 | line => $macro->{line} ); | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | push @code, $self->opcode( macro_nargs => $macro->{nargs} ) | 
| 592 | 258 | 100 |  |  |  | 962 | if $macro->{nargs}; | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | push @code, $self->opcode( macro_outer => $macro->{outer} ) | 
| 595 | 258 | 100 |  |  |  | 726 | if $macro->{outer}; | 
| 596 |  |  |  |  |  |  |  | 
| 597 | 258 |  |  |  |  | 364 | push @code, @{ $macro->{body} }, $self->opcode('macro_end'); | 
|  | 258 |  |  |  |  | 727 |  | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  | } | 
| 600 | 3377 |  |  |  |  | 6434 | %{$mtable} = (); | 
|  | 3377 |  |  |  |  | 10227 |  | 
| 601 | 3377 |  |  |  |  | 10350 | return @code; | 
| 602 |  |  |  |  |  |  | } | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | sub _generate_name { | 
| 605 | 449 |  |  | 449 |  | 1033 | my($self, $node) = @_; | 
| 606 |  |  |  |  |  |  |  | 
| 607 | 449 |  |  |  |  | 2891 | my $id = $node->value; # may be aliased | 
| 608 | 449 | 100 |  |  |  | 1874 | if(defined(my $lvar_id = $self->lvar->{$id})) { # constants | 
| 609 | 71 |  |  |  |  | 188 | my $code = $self->const->[$lvar_id]; | 
| 610 | 71 | 100 |  |  |  | 160 | if(defined $code) { | 
| 611 |  |  |  |  |  |  | # because the constant value is very simple, | 
| 612 |  |  |  |  |  |  | # its definition is optimized away. | 
| 613 |  |  |  |  |  |  | # only its value remains. | 
| 614 | 23 |  |  |  |  | 32 | return @{$code}; | 
|  | 23 |  |  |  |  | 92 |  | 
| 615 |  |  |  |  |  |  | } | 
| 616 |  |  |  |  |  |  | else { | 
| 617 | 48 |  |  |  |  | 121 | return $self->opcode( load_lvar => $lvar_id, symbol => $node ); | 
| 618 |  |  |  |  |  |  | } | 
| 619 |  |  |  |  |  |  | } | 
| 620 |  |  |  |  |  |  |  | 
| 621 | 378 |  |  |  |  | 1293 | 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 |  |  |  |  | 3 | $self->_error("Invalid expression", $node); | 
| 629 |  |  |  |  |  |  | } | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | sub _can_optimize_print { | 
| 632 | 2381 |  |  | 2381 |  | 5950 | my($self, $name, $node) = @_; | 
| 633 |  |  |  |  |  |  |  | 
| 634 | 2381 | 50 |  |  |  | 6783 | return 0 if !$OPTIMIZE; | 
| 635 | 2381 | 50 | 66 |  |  | 8032 | return 0 if !($name eq 'print' or $name eq 'print_raw'); | 
| 636 |  |  |  |  |  |  |  | 
| 637 | 2381 |  |  |  |  | 7146 | 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 | 2381 |  | 100 |  |  | 16618 | && !$self->overridden_builtin->{$maybe_name->id}; | 
| 643 |  |  |  |  |  |  | } | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | # also deal with smart escaping | 
| 646 |  |  |  |  |  |  | sub _generate_print { | 
| 647 | 12644 |  |  | 12644 |  | 23170 | my($self, $node) = @_; | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 12644 |  |  |  |  | 19198 | my @code; | 
| 650 |  |  |  |  |  |  |  | 
| 651 | 12644 |  |  |  |  | 34041 | my $proc = $node->id; | 
| 652 | 12644 | 100 | 100 |  |  | 44240 | if($proc eq 'print' and $self->type eq 'text') { | 
| 653 | 28 |  |  |  |  | 43 | $proc = 'print_raw'; | 
| 654 |  |  |  |  |  |  | } | 
| 655 |  |  |  |  |  |  |  | 
| 656 | 12644 |  |  |  |  | 19873 | foreach my $arg(@{ $node->first }){ | 
|  | 12644 |  |  |  |  | 44382 |  | 
| 657 | 12675 | 100 | 66 |  |  | 97299 | if( $proc eq 'print' && $self->overridden_builtin->{html_escape} ) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | # default behaviour of print() is overridden | 
| 659 | 5 |  |  |  |  | 14 | 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 | 10289 |  |  |  |  | 49795 | push @code, | 
| 670 |  |  |  |  |  |  | $self->opcode( $proc . '_s' => $arg->value, | 
| 671 |  |  |  |  |  |  | line         => $arg->line ); | 
| 672 |  |  |  |  |  |  | } | 
| 673 |  |  |  |  |  |  | elsif($self->_can_optimize_print($proc, $arg)){ | 
| 674 | 27 |  |  |  |  | 118 | my $filter      = $arg->first; | 
| 675 | 27 |  |  |  |  | 76 | my $filter_name = $filter->id; | 
| 676 | 27 | 100 |  |  |  | 89 | my $command = $builtin{ $filter_name }[0] eq 'builtin_mark_raw' | 
| 677 |  |  |  |  |  |  | ? 'print_raw'  # mark_raw, raw | 
| 678 |  |  |  |  |  |  | : 'print';     # html | 
| 679 |  |  |  |  |  |  |  | 
| 680 | 27 |  |  |  |  | 134 | push @code, | 
| 681 |  |  |  |  |  |  | $self->compile_ast($arg->second->[0]), | 
| 682 |  |  |  |  |  |  | $self->opcode( | 
| 683 |  |  |  |  |  |  | $command => undef, | 
| 684 |  |  |  |  |  |  | symbol   => $filter ); | 
| 685 |  |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | } | 
| 687 |  |  |  |  |  |  | else { | 
| 688 | 2354 |  |  |  |  | 7967 | push @code, | 
| 689 |  |  |  |  |  |  | $self->compile_ast($arg), | 
| 690 |  |  |  |  |  |  | $self->opcode( $proc => undef, line => $node->line ); | 
| 691 |  |  |  |  |  |  | } | 
| 692 |  |  |  |  |  |  | } | 
| 693 |  |  |  |  |  |  |  | 
| 694 | 12635 | 50 |  |  |  | 32543 | if(!@code) { | 
| 695 | 0 |  |  |  |  | 0 | $self->_error("$node requires at least one argument", $node); | 
| 696 |  |  |  |  |  |  | } | 
| 697 | 12635 |  |  |  |  | 44415 | return @code; | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | sub _generate_include { | 
| 701 | 1253 |  |  | 1253 |  | 1998 | my($self, $node) = @_; | 
| 702 |  |  |  |  |  |  |  | 
| 703 | 1253 |  |  |  |  | 2809 | my $file = $node->first; | 
| 704 | 1253 | 100 |  |  |  | 3906 | 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 |  |  |  | 4326 | 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 |  |  |  |  | 3762 | return @code; | 
| 719 |  |  |  |  |  |  | } | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | sub _bare_to_file { | 
| 722 | 82 |  |  | 82 |  | 148 | my($self, $file) = @_; | 
| 723 | 82 | 100 |  |  |  | 267 | if(ref($file) eq 'ARRAY') { # myapp::foo | 
|  |  | 100 |  |  |  |  |  | 
| 724 | 68 |  |  |  |  | 109 | return join('/', map { $_->value } @{$file}) . $self->{engine}->{suffix}; | 
|  | 120 |  |  |  |  | 1022 |  | 
|  | 68 |  |  |  |  | 134 |  | 
| 725 |  |  |  |  |  |  | } | 
| 726 |  |  |  |  |  |  | elsif($file->arity eq 'literal') { | 
| 727 | 13 |  |  |  |  | 48 | 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 |  | 131 | my($self, $node) = @_; | 
| 736 | 73 | 50 |  |  |  | 297 | if(defined $self->cascade) { | 
| 737 | 0 |  |  |  |  | 0 | $self->_error("Cannot cascade twice in a template", $node); | 
| 738 |  |  |  |  |  |  | } | 
| 739 | 73 |  |  |  |  | 185 | $self->cascade( $node ); | 
| 740 | 73 |  |  |  |  | 161 | return; | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | # XXX: need more consideration | 
| 744 |  |  |  |  |  |  | sub _compile_loop_block { | 
| 745 | 195 |  |  | 195 |  | 326 | my($self, $block) = @_; | 
| 746 | 195 |  |  |  |  | 481 | my @block_code = $self->compile_ast($block); | 
| 747 |  |  |  |  |  |  |  | 
| 748 | 195 |  |  |  |  | 411 | foreach my $op(@block_code) { | 
| 749 | 1620 | 100 |  |  |  | 4212 | 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 |  |  |  |  | 68 | push    @block_code, $self->opcode('leave'); | 
| 754 | 25 |  |  |  |  | 56 | last; | 
| 755 |  |  |  |  |  |  | } | 
| 756 |  |  |  |  |  |  | } | 
| 757 |  |  |  |  |  |  |  | 
| 758 | 195 |  |  |  |  | 648 | foreach my $i(1 .. (@block_code-1)) { | 
| 759 | 1640 |  |  |  |  | 2069 | my $op = $block_code[$i]; | 
| 760 | 1640 | 100 |  |  |  | 3759 | if($op->[_OP_NAME] eq 'loop_control') { | 
| 761 | 10 |  |  |  |  | 17 | my $type = $op->[_OP_ARG]; | 
| 762 | 10 |  |  |  |  | 19 | $op->[_OP_NAME] = 'goto'; | 
| 763 |  |  |  |  |  |  |  | 
| 764 | 10 |  |  |  |  | 17 | $op->[_OP_ARG] = (@block_code - $i); | 
| 765 |  |  |  |  |  |  |  | 
| 766 | 10 | 100 |  |  |  | 29 | $op->[_OP_ARG] += 1 if $type eq 'last'; | 
| 767 |  |  |  |  |  |  | } | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  |  | 
| 770 | 195 |  |  |  |  | 777 | return @block_code; | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | sub _generate_for { | 
| 774 | 170 |  |  | 170 |  | 290 | my($self, $node) = @_; | 
| 775 | 170 |  |  |  |  | 439 | my $expr  = $node->first; | 
| 776 | 170 |  |  |  |  | 388 | my $vars  = $node->second; | 
| 777 | 170 |  |  |  |  | 383 | my $block = $node->third; | 
| 778 |  |  |  |  |  |  |  | 
| 779 | 170 | 50 |  |  |  | 210 | if(@{$vars} != 1) { | 
|  | 170 |  |  |  |  | 522 |  | 
| 780 | 0 |  |  |  |  | 0 | $self->_error("A for-loop requires single variable for each item", $node); | 
| 781 |  |  |  |  |  |  | } | 
| 782 | 170 |  |  |  |  | 250 | local $self->{lvar}  = { %{$self->lvar} };  # new scope | 
|  | 170 |  |  |  |  | 868 |  | 
| 783 | 170 |  |  |  |  | 254 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 170 |  |  |  |  | 623 |  | 
| 784 | 170 |  |  |  |  | 382 | local $self->{in_loop} = _FOR_LOOP; | 
| 785 |  |  |  |  |  |  |  | 
| 786 | 170 |  |  |  |  | 449 | my @code = $self->compile_ast($expr); | 
| 787 |  |  |  |  |  |  |  | 
| 788 | 170 |  |  |  |  | 280 | my($iter_var) = @{$vars}; | 
|  | 170 |  |  |  |  | 369 |  | 
| 789 | 170 |  |  |  |  | 480 | my $lvar_id   = $self->lvar_id; | 
| 790 | 170 |  |  |  |  | 618 | my $lvar_name = $iter_var->id; | 
| 791 |  |  |  |  |  |  |  | 
| 792 | 170 |  |  |  |  | 703 | $self->lvar->{$lvar_name} = $lvar_id; | 
| 793 | 170 |  |  |  |  | 470 | $self->lvar->{'($_)'}     = $lvar_id; | 
| 794 |  |  |  |  |  |  |  | 
| 795 | 170 |  |  |  |  | 457 | 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 |  |  |  |  | 452 | local $self->{lvar_id} = $self->lvar_use(3); | 
| 799 |  |  |  |  |  |  |  | 
| 800 | 170 |  |  |  |  | 538 | my @block_code = $self->_compile_loop_block($block); | 
| 801 | 170 |  |  |  |  | 541 | 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 |  |  |  |  | 1680 | return @code; | 
| 808 |  |  |  |  |  |  | } | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | sub _generate_for_else { | 
| 811 | 8 |  |  | 8 |  | 16 | my($self, $node) = @_; | 
| 812 |  |  |  |  |  |  |  | 
| 813 | 8 |  |  |  |  | 24 | my $for_block  = $node->first; | 
| 814 | 8 |  |  |  |  | 19 | my $else_block = $node->second; | 
| 815 |  |  |  |  |  |  |  | 
| 816 | 8 |  |  |  |  | 29 | my @code = ( | 
| 817 |  |  |  |  |  |  | $self->compile_ast($for_block), | 
| 818 |  |  |  |  |  |  | ); | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | # 'for' block sets __a with true if the loop count > 0 | 
| 821 | 8 |  |  |  |  | 22 | my @else = $self->compile_ast($else_block); | 
| 822 | 8 |  |  |  |  | 25 | push @code, ( | 
| 823 |  |  |  |  |  |  | $self->opcode( or => scalar(@else) + 1, comment => 'for-else' ), | 
| 824 |  |  |  |  |  |  | @else, | 
| 825 |  |  |  |  |  |  | ); | 
| 826 |  |  |  |  |  |  |  | 
| 827 | 8 |  |  |  |  | 54 | return @code; | 
| 828 |  |  |  |  |  |  | } | 
| 829 |  |  |  |  |  |  |  | 
| 830 |  |  |  |  |  |  | sub _generate_while { | 
| 831 | 25 |  |  | 25 |  | 43 | my($self, $node) = @_; | 
| 832 | 25 |  |  |  |  | 59 | my $expr  = $node->first; | 
| 833 | 25 |  |  |  |  | 55 | my $vars  = $node->second; | 
| 834 | 25 |  |  |  |  | 58 | my $block = $node->third; | 
| 835 |  |  |  |  |  |  |  | 
| 836 | 25 | 50 |  |  |  | 33 | if(@{$vars} > 1) { | 
|  | 25 |  |  |  |  | 78 |  | 
| 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 |  |  |  |  | 41 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 25 |  |  |  |  | 123 |  | 
| 844 | 25 |  |  |  |  | 38 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 25 |  |  |  |  | 87 |  | 
| 845 | 25 |  |  |  |  | 57 | local $self->{in_loop} = _WHILE_LOOP; | 
| 846 |  |  |  |  |  |  |  | 
| 847 | 25 |  |  |  |  | 73 | my @code = $self->compile_ast($expr); | 
| 848 |  |  |  |  |  |  |  | 
| 849 | 25 |  |  |  |  | 36 | my($iter_var) = @{$vars}; | 
|  | 25 |  |  |  |  | 47 |  | 
| 850 | 25 |  |  |  |  | 33 | my($lvar_id, $lvar_name); | 
| 851 |  |  |  |  |  |  |  | 
| 852 | 25 | 100 |  |  |  | 155 | if(@{$vars}) { | 
|  | 25 |  |  |  |  | 82 |  | 
| 853 | 10 |  |  |  |  | 59 | $lvar_id                  = $self->lvar_id; | 
| 854 | 10 |  |  |  |  | 23 | $lvar_name                = $iter_var->id; | 
| 855 | 10 |  |  |  |  | 31 | $self->lvar->{$lvar_name} = $lvar_id; | 
| 856 | 10 |  |  |  |  | 23 | push @code, $self->opcode( save_to_lvar => $lvar_id, symbol => $iter_var ); | 
| 857 |  |  |  |  |  |  | } | 
| 858 |  |  |  |  |  |  |  | 
| 859 | 25 |  |  |  |  | 39 | local $self->{lvar_id} = $self->lvar_use(scalar @{$vars}); | 
|  | 25 |  |  |  |  | 60 |  | 
| 860 | 25 |  |  |  |  | 82 | my @block_code = $self->_compile_loop_block($block); | 
| 861 | 25 |  |  |  |  | 95 | 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 |  | 20 | my($self, $node) = @_; | 
| 871 | 12 |  |  |  |  | 29 | my $type = $node->id; | 
| 872 |  |  |  |  |  |  |  | 
| 873 | 12 | 50 |  |  |  | 33 | any_in($type, qw(last next)) | 
| 874 |  |  |  |  |  |  | or $self->_error("[BUG] Unknown loop control statement '$type'"); | 
| 875 |  |  |  |  |  |  |  | 
| 876 | 12 | 100 |  |  |  | 30 | if(not $self->{in_loop}) { | 
| 877 | 2 |  |  |  |  | 11 | $self->_error("Use of loop control statement ($type) outside of loops"); | 
| 878 |  |  |  |  |  |  | } | 
| 879 |  |  |  |  |  |  |  | 
| 880 | 10 |  |  |  |  | 13 | my @cleanup; | 
| 881 | 10 | 100 | 100 |  |  | 46 | if( $self->{in_loop} == _FOR_LOOP && $type eq 'last' ) { | 
| 882 | 2 |  |  |  |  | 8 | my $lvar_id = $self->lvar->{'($_)'}; | 
| 883 | 2 | 50 |  |  |  | 7 | 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 |  |  |  |  | 24 | 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 |  | 516 | my($self, $node) = @_; | 
| 903 | 311 |  |  |  |  | 817 | my $type   = $node->id; | 
| 904 | 311 |  |  |  |  | 1040 | my $name   = $node->first->id; | 
| 905 | 311 |  |  |  |  | 459 | my @args   = map{ $_->id } @{$node->second}; | 
|  | 114 |  |  |  |  | 418 |  | 
|  | 311 |  |  |  |  | 932 |  | 
| 906 | 311 |  |  |  |  | 811 | my $block  = $node->third; | 
| 907 |  |  |  |  |  |  |  | 
| 908 | 311 |  |  |  |  | 396 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 311 |  |  |  |  | 1489 |  | 
| 909 | 311 |  |  |  |  | 469 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 311 |  |  |  |  | 1150 |  | 
| 910 |  |  |  |  |  |  |  | 
| 911 | 311 |  |  |  |  | 798 | my $lvar_used = $self->lvar_id; | 
| 912 | 311 |  |  |  |  | 428 | my $arg_ix    = 0; | 
| 913 | 311 |  |  |  |  | 609 | foreach my $arg(@args) { | 
| 914 |  |  |  |  |  |  | # to fetch ST(ix) | 
| 915 |  |  |  |  |  |  | # Note that arg_ix must be start from 1 | 
| 916 | 114 |  |  |  |  | 449 | $self->lvar->{$arg} = $lvar_used + $arg_ix++; | 
| 917 |  |  |  |  |  |  | } | 
| 918 |  |  |  |  |  |  |  | 
| 919 | 311 |  |  |  |  | 845 | local $self->{lvar_id} = $self->lvar_use($arg_ix); | 
| 920 |  |  |  |  |  |  |  | 
| 921 | 311 |  |  |  |  | 819 | my $opinfo = $self->opcode(set_opinfo => undef, file => $self->filename, line => $node->line); | 
| 922 | 311 |  |  |  |  | 997 | 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 |  |  |  | 1416 | if(any_in($type, qw(macro block))) { | 
| 932 | 235 | 100 |  |  |  | 1034 | if(exists $self->macro_table->{$name}) { | 
| 933 | 2 |  |  |  |  | 7 | my $m = $self->macro_table->{$name}; | 
| 934 | 2 | 50 |  |  |  | 7 | if(p(\%macro) ne p($m)) { | 
| 935 | 2 |  |  |  |  | 330 | $self->_error("Redefinition of $type $name is forbidden", $node); | 
| 936 |  |  |  |  |  |  | } | 
| 937 |  |  |  |  |  |  | } | 
| 938 | 233 |  |  |  |  | 940 | $self->macro_table->{$name} = \%macro; | 
| 939 |  |  |  |  |  |  | } | 
| 940 |  |  |  |  |  |  | else { | 
| 941 | 76 |  |  |  |  | 370 | my $fq_name = sprintf '%s@%s', $name, $type; | 
| 942 | 76 |  |  |  |  | 148 | $macro{name} = $fq_name; | 
| 943 | 76 |  | 50 |  |  | 108 | push @{ $self->macro_table->{ $fq_name } ||= [] }, \%macro; | 
|  | 76 |  |  |  |  | 602 |  | 
| 944 |  |  |  |  |  |  | } | 
| 945 | 309 |  |  |  |  | 1498 | return; | 
| 946 |  |  |  |  |  |  | } | 
| 947 |  |  |  |  |  |  |  | 
| 948 |  |  |  |  |  |  | sub _generate_lambda { | 
| 949 | 39 |  |  | 39 |  | 60 | my($self, $node) = @_; | 
| 950 |  |  |  |  |  |  |  | 
| 951 | 39 |  |  |  |  | 86 | my $macro = $node->first; | 
| 952 | 39 |  |  |  |  | 84 | $self->compile_ast($macro); | 
| 953 | 39 |  |  |  |  | 190 | return $self->opcode( fetch_symbol => $macro->first->id, line => $node->line ); | 
| 954 |  |  |  |  |  |  | } | 
| 955 |  |  |  |  |  |  |  | 
| 956 |  |  |  |  |  |  | sub _prepare_cond_expr { | 
| 957 | 418 |  |  | 418 |  | 629 | my($self, $expr) = @_; | 
| 958 | 418 |  |  |  |  | 576 | my $t = "and"; | 
| 959 | 418 |  |  |  |  | 588 | my $f = "or"; | 
| 960 |  |  |  |  |  |  |  | 
| 961 | 418 |  |  |  |  | 1577 | while($expr->id eq '!') { | 
| 962 | 31 |  |  |  |  | 83 | $expr    = $expr->first; | 
| 963 | 31 |  |  |  |  | 141 | ($t, $f) = ($f, $t); | 
| 964 |  |  |  |  |  |  | } | 
| 965 |  |  |  |  |  |  |  | 
| 966 | 418 | 100 | 100 |  |  | 2206 | if($expr->is_logical and any_in($expr->id, qw(== !=))) { | 
| 967 | 167 |  |  |  |  | 444 | my $rhs = $expr->second; | 
| 968 | 167 | 100 |  |  |  | 623 | if($rhs->arity eq "nil") { | 
| 969 |  |  |  |  |  |  | # add prefix 'd' (i.e. "and" to "dand", "or" to "dor") | 
| 970 | 39 |  |  |  |  | 93 | substr $t, 0, 0, 'd'; | 
| 971 | 39 |  |  |  |  | 67 | substr $f, 0, 0, 'd'; | 
| 972 |  |  |  |  |  |  |  | 
| 973 | 39 | 100 |  |  |  | 181 | if($expr->id eq "==") { | 
| 974 | 18 |  |  |  |  | 50 | ($t, $f) = ($f, $t); | 
| 975 |  |  |  |  |  |  | } | 
| 976 | 39 |  |  |  |  | 192 | $expr = $expr->first; | 
| 977 |  |  |  |  |  |  | } | 
| 978 |  |  |  |  |  |  | } | 
| 979 |  |  |  |  |  |  |  | 
| 980 | 418 |  |  |  |  | 1185 | return($t, $f, $expr); | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  |  | 
| 983 |  |  |  |  |  |  | sub _generate_if { | 
| 984 | 393 |  |  | 393 |  | 638 | my($self, $node) = @_; | 
| 985 | 393 |  |  |  |  | 917 | my $first  = $node->first; | 
| 986 | 393 |  |  |  |  | 845 | my $second = $node->second; | 
| 987 | 393 |  |  |  |  | 836 | my $third  = $node->third; | 
| 988 |  |  |  |  |  |  |  | 
| 989 | 393 |  |  |  |  | 947 | my($cond_true, $cond_false, $expr) = $self->_prepare_cond_expr($first); | 
| 990 |  |  |  |  |  |  |  | 
| 991 | 393 |  |  |  |  | 624 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 393 |  |  |  |  | 1848 |  | 
| 992 | 393 |  |  |  |  | 609 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 393 |  |  |  |  | 1384 |  | 
| 993 | 393 |  |  |  |  | 1106 | my @cond  = $self->compile_ast($expr); | 
| 994 |  |  |  |  |  |  |  | 
| 995 | 393 |  |  |  |  | 753 | my @then = do { | 
| 996 | 393 |  |  |  |  | 871 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 393 |  |  |  |  | 1824 |  | 
| 997 | 393 |  |  |  |  | 740 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 393 |  |  |  |  | 1287 |  | 
| 998 | 393 |  |  |  |  | 1053 | $self->compile_ast($second); | 
| 999 |  |  |  |  |  |  | }; | 
| 1000 |  |  |  |  |  |  |  | 
| 1001 | 393 |  |  |  |  | 611 | my @else = do { | 
| 1002 | 393 |  |  |  |  | 509 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 393 |  |  |  |  | 1550 |  | 
| 1003 | 393 |  |  |  |  | 581 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 393 |  |  |  |  | 1281 |  | 
| 1004 | 393 |  |  |  |  | 913 | $self->compile_ast($third); | 
| 1005 |  |  |  |  |  |  | }; | 
| 1006 |  |  |  |  |  |  |  | 
| 1007 | 393 | 50 |  |  |  | 999 | if($OPTIMIZE) { | 
| 1008 | 393 | 100 |  |  |  | 977 | if($self->_code_is_literal(@cond)) { | 
| 1009 | 100 |  |  |  |  | 177 | my $value = $cond[0][_OP_ARG]; | 
| 1010 | 100 | 100 |  |  |  | 292 | if($cond_true eq 'and' ? $value : !$value) { | 
|  |  | 100 |  |  |  |  |  | 
| 1011 | 75 |  |  |  |  | 476 | return @then; | 
| 1012 |  |  |  |  |  |  | } | 
| 1013 |  |  |  |  |  |  | else { | 
| 1014 | 25 |  |  |  |  | 172 | return @else; | 
| 1015 |  |  |  |  |  |  | } | 
| 1016 |  |  |  |  |  |  | } | 
| 1017 |  |  |  |  |  |  | } | 
| 1018 |  |  |  |  |  |  |  | 
| 1019 | 293 | 100 | 100 |  |  | 1966 | if( (@then and @else) or !$OPTIMIZE) { | 
|  |  | 100 | 66 |  |  |  |  | 
| 1020 |  |  |  |  |  |  | return( | 
| 1021 | 217 |  |  |  |  | 1027 | @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 |  |  |  |  | 368 | @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 |  |  |  |  | 22 | @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 |  | 61 | my($self, $node) = @_; | 
| 1046 | 39 |  |  |  |  | 86 | my $expr  = $node->first; | 
| 1047 | 39 |  |  |  |  | 76 | my $vars  = $node->second; | 
| 1048 | 39 |  |  |  |  | 91 | my $block = $node->third; | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 | 39 | 50 |  |  |  | 43 | if(@{$vars} > 1) { | 
|  | 39 |  |  |  |  | 107 |  | 
| 1051 | 0 |  |  |  |  | 0 | $self->_error("A given block requires one or zero variables", $node); | 
| 1052 |  |  |  |  |  |  | } | 
| 1053 | 39 |  |  |  |  | 50 | local $self->{lvar}  = { %{$self->lvar}  }; # new scope | 
|  | 39 |  |  |  |  | 192 |  | 
| 1054 | 39 |  |  |  |  | 55 | local $self->{const} = [ @{$self->const} ]; # new scope | 
|  | 39 |  |  |  |  | 136 |  | 
| 1055 |  |  |  |  |  |  |  | 
| 1056 | 39 |  |  |  |  | 101 | my @code = $self->compile_ast($expr); | 
| 1057 |  |  |  |  |  |  |  | 
| 1058 | 39 |  |  |  |  | 60 | my($lvar)     = @{$vars}; | 
|  | 39 |  |  |  |  | 72 |  | 
| 1059 | 39 |  |  |  |  | 102 | my $lvar_id   = $self->lvar_id; | 
| 1060 | 39 |  |  |  |  | 88 | my $lvar_name = $lvar->id; | 
| 1061 |  |  |  |  |  |  |  | 
| 1062 | 39 |  |  |  |  | 125 | $self->lvar->{$lvar_name} = $lvar_id; | 
| 1063 |  |  |  |  |  |  |  | 
| 1064 | 39 |  |  |  |  | 86 | local $self->{lvar_id} = $self->lvar_use(1); # topic variable | 
| 1065 | 39 |  |  |  |  | 91 | push @code, $self->opcode( save_to_lvar => $lvar_id, symbol => $lvar ), | 
| 1066 |  |  |  |  |  |  | $self->compile_ast($block); | 
| 1067 |  |  |  |  |  |  |  | 
| 1068 | 39 |  |  |  |  | 293 | return @code; | 
| 1069 |  |  |  |  |  |  | } | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 |  |  |  |  |  |  | sub _generate_variable { | 
| 1072 | 3155 |  |  | 3155 |  | 6398 | my($self, $node) = @_; | 
| 1073 |  |  |  |  |  |  |  | 
| 1074 | 3155 | 100 |  |  |  | 29270 | if(defined(my $lvar_id = $self->lvar->{$node->value})) { | 
| 1075 | 420 |  |  |  |  | 1014 | return $self->opcode( load_lvar => $lvar_id, symbol => $node ); | 
| 1076 |  |  |  |  |  |  | } | 
| 1077 |  |  |  |  |  |  | else { | 
| 1078 | 2735 |  |  |  |  | 8257 | my $name = $self->_variable_to_value($node); | 
| 1079 | 2735 | 100 |  |  |  | 9096 | if($name =~ /~/) { | 
| 1080 | 8 |  |  |  |  | 32 | $self->_error("Undefined iterator variable $node", $node); | 
| 1081 |  |  |  |  |  |  | } | 
| 1082 | 2727 |  |  |  |  | 10556 | return $self->opcode( fetch_s => $name, line => $node->line ); | 
| 1083 |  |  |  |  |  |  | } | 
| 1084 |  |  |  |  |  |  | } | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | sub _generate_super { | 
| 1087 | 7 |  |  | 7 |  | 12 | my($self, $node) = @_; | 
| 1088 |  |  |  |  |  |  |  | 
| 1089 | 7 |  |  |  |  | 21 | return return $self->opcode( super => undef, symbol => $node ); | 
| 1090 |  |  |  |  |  |  | } | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | sub _generate_literal { | 
| 1093 | 7299 |  |  | 7299 |  | 9646 | my($self, $node) = @_; | 
| 1094 | 7299 |  |  |  |  | 20971 | return $self->opcode( literal => $node->value ); | 
| 1095 |  |  |  |  |  |  | } | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 |  |  |  |  |  |  | sub _generate_nil { | 
| 1098 | 69 |  |  | 69 |  | 121 | my($self) = @_; | 
| 1099 | 69 |  |  |  |  | 155 | return $self->opcode('nil'); | 
| 1100 |  |  |  |  |  |  | } | 
| 1101 |  |  |  |  |  |  |  | 
| 1102 |  |  |  |  |  |  | sub _generate_vars { | 
| 1103 | 6 |  |  | 6 |  | 11 | my($self) = @_; | 
| 1104 | 6 |  |  |  |  | 16 | return $self->opcode('vars'); | 
| 1105 |  |  |  |  |  |  | } | 
| 1106 |  |  |  |  |  |  |  | 
| 1107 |  |  |  |  |  |  | sub _generate_composer { | 
| 1108 | 131 |  |  | 131 |  | 270 | my($self, $node) = @_; | 
| 1109 |  |  |  |  |  |  |  | 
| 1110 | 131 |  |  |  |  | 334 | my $list = $node->first; | 
| 1111 | 131 | 100 |  |  |  | 521 | my $type = $node->id eq '{' ? 'make_hash' : 'make_array'; | 
| 1112 |  |  |  |  |  |  |  | 
| 1113 |  |  |  |  |  |  | return | 
| 1114 |  |  |  |  |  |  | $self->opcode( pushmark => undef, comment => $type ), | 
| 1115 | 131 |  |  |  |  | 350 | (map{ $self->push_expr($_) } @{$list}), | 
|  | 5265 |  |  |  |  | 9764 |  | 
|  | 131 |  |  |  |  | 365 |  | 
| 1116 |  |  |  |  |  |  | $self->opcode($type), | 
| 1117 |  |  |  |  |  |  | ; | 
| 1118 |  |  |  |  |  |  | } | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  | sub _generate_unary { | 
| 1121 | 33 |  |  | 33 |  | 59 | my($self, $node) = @_; | 
| 1122 |  |  |  |  |  |  |  | 
| 1123 | 33 |  |  |  |  | 91 | my $id = $node->id; | 
| 1124 | 33 | 50 |  |  |  | 95 | if(exists $unary{$id}) { | 
| 1125 | 33 |  |  |  |  | 147 | my @operand = $self->compile_ast($node->first); | 
| 1126 |  |  |  |  |  |  | my @code = ( | 
| 1127 |  |  |  |  |  |  | @operand, | 
| 1128 | 33 |  |  |  |  | 117 | $self->opcode( $unary{$id} ) | 
| 1129 |  |  |  |  |  |  | ); | 
| 1130 | 33 | 100 | 66 |  |  | 185 | if( $OPTIMIZE and $self->_code_is_literal(@operand) ) { | 
| 1131 | 17 |  |  |  |  | 50 | $self->_fold_constants(\@code); | 
| 1132 |  |  |  |  |  |  | } | 
| 1133 | 33 |  |  |  |  | 140 | 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 |  | 625 | my($self, $node) = @_; | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 | 312 |  |  |  |  | 1062 | my @lhs   = $self->compile_ast($node->first); | 
| 1144 | 304 |  |  |  |  | 866 | my $field = $node->second; | 
| 1145 |  |  |  |  |  |  |  | 
| 1146 |  |  |  |  |  |  | # $foo.field | 
| 1147 |  |  |  |  |  |  | # $foo["field"] | 
| 1148 | 304 | 100 |  |  |  | 981 | if($field->arity eq "literal") { | 
| 1149 |  |  |  |  |  |  | return | 
| 1150 | 250 |  |  |  |  | 1695 | @lhs, | 
| 1151 |  |  |  |  |  |  | $self->opcode( fetch_field_s => $field->value ); | 
| 1152 |  |  |  |  |  |  | } | 
| 1153 |  |  |  |  |  |  | # $foo[expression] | 
| 1154 |  |  |  |  |  |  | else { | 
| 1155 | 54 |  |  |  |  | 133 | local $self->{lvar_id} = $self->lvar_use(1); | 
| 1156 | 54 |  |  |  |  | 141 | my @rhs = $self->compile_ast($field); | 
| 1157 | 54 | 100 | 66 |  |  | 231 | if($OPTIMIZE and $self->_code_is_literal(@rhs)) { | 
| 1158 |  |  |  |  |  |  | return | 
| 1159 | 14 |  |  |  |  | 40 | @lhs, | 
| 1160 |  |  |  |  |  |  | $self->opcode( fetch_field_s => $rhs[0][1] ); | 
| 1161 |  |  |  |  |  |  | } | 
| 1162 |  |  |  |  |  |  | return | 
| 1163 | 40 |  |  |  |  | 158 | @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 |  | 1490 | my($self, $node) = @_; | 
| 1175 |  |  |  |  |  |  |  | 
| 1176 | 927 |  |  |  |  | 2976 | my @lhs = $self->compile_ast($node->first); | 
| 1177 |  |  |  |  |  |  |  | 
| 1178 | 925 |  |  |  |  | 2377 | my $id = $node->id; | 
| 1179 | 925 | 100 |  |  |  | 2483 | if(exists $binary{$id}) { | 
|  |  | 50 |  |  |  |  |  | 
| 1180 | 702 |  |  |  |  | 1574 | local $self->{lvar_id} = $self->lvar_use(1); | 
| 1181 | 702 |  |  |  |  | 2204 | 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 |  |  |  |  | 2513 | $self->opcode( $binary{$id} ), | 
| 1188 |  |  |  |  |  |  | ); | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 | 702 | 100 |  |  |  | 2432 | if(any_in($id, qw(min max))) { | 
| 1191 | 26 |  |  |  |  | 58 | local $self->{lvar_id} = $self->lvar_use(1); | 
| 1192 | 26 |  |  |  |  | 86 | splice @code, -1, 0, | 
| 1193 |  |  |  |  |  |  | $self->opcode(save_to_lvar => $self->lvar_id ); # save lhs | 
| 1194 | 26 |  |  |  |  | 61 | 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 |  |  |  | 1735 | if($OPTIMIZE) { | 
| 1202 | 702 | 100 | 100 |  |  | 1740 | if( $self->_code_is_literal(@lhs) and $self->_code_is_literal(@rhs) ){ | 
| 1203 | 123 |  |  |  |  | 332 | $self->_fold_constants(\@code); | 
| 1204 |  |  |  |  |  |  | } | 
| 1205 |  |  |  |  |  |  | } | 
| 1206 | 702 |  |  |  |  | 3622 | return @code; | 
| 1207 |  |  |  |  |  |  | } | 
| 1208 |  |  |  |  |  |  | elsif(exists $logical_binary{$id}) { | 
| 1209 | 223 |  |  |  |  | 736 | my @rhs = $self->compile_ast($node->second); | 
| 1210 |  |  |  |  |  |  | return | 
| 1211 |  |  |  |  |  |  | @lhs, | 
| 1212 | 223 |  |  |  |  | 842 | $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 |  | 15 | my($self, $node) = @_; | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 | 7 | 50 |  |  |  | 18 | $self->can_be_in_list_context | 
| 1223 |  |  |  |  |  |  | or $self->_error("Range operator must be in list context"); | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 | 7 |  |  |  |  | 31 | my @lhs  = $self->compile_ast($node->first); | 
| 1226 |  |  |  |  |  |  |  | 
| 1227 | 7 |  |  |  |  | 18 | local $self->{lvar_id} = $self->lvar_use(1); | 
| 1228 | 7 |  |  |  |  | 26 | my @rhs = $self->compile_ast($node->second); | 
| 1229 |  |  |  |  |  |  | return( | 
| 1230 | 7 |  |  |  |  | 26 | @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 |  | 366 | my($self, $node) = @_; | 
| 1240 |  |  |  |  |  |  |  | 
| 1241 | 230 |  |  |  |  | 650 | my $args   = $node->third; | 
| 1242 | 230 |  |  |  |  | 1508 | my $method = $node->second->value; | 
| 1243 |  |  |  |  |  |  | return ( | 
| 1244 |  |  |  |  |  |  | $self->opcode( pushmark => undef, comment => $method ), | 
| 1245 |  |  |  |  |  |  | $self->push_expr($node->first), | 
| 1246 | 230 |  |  |  |  | 575 | (map { $self->push_expr($_) } @{$args}), | 
|  | 137 |  |  |  |  | 304 |  | 
|  | 230 |  |  |  |  | 682 |  | 
| 1247 |  |  |  |  |  |  | $self->opcode( methodcall_s => $method, line => $node->line ), | 
| 1248 |  |  |  |  |  |  | ); | 
| 1249 |  |  |  |  |  |  | } | 
| 1250 |  |  |  |  |  |  |  | 
| 1251 |  |  |  |  |  |  | sub _generate_call { | 
| 1252 | 457 |  |  | 457 |  | 986 | my($self, $node) = @_; | 
| 1253 | 457 |  |  |  |  | 1116 | my $callable = $node->first; # function or macro | 
| 1254 | 457 |  |  |  |  | 1048 | my $args     = $node->second; | 
| 1255 |  |  |  |  |  |  |  | 
| 1256 | 457 | 100 | 100 |  |  | 2243 | if(my $intern = $builtin{$callable->id} and !$self->overridden_builtin->{$callable->id}) { | 
| 1257 | 54 | 50 |  |  |  | 67 | if(@{$args} != 1) { | 
|  | 54 |  |  |  |  | 153 |  | 
| 1258 | 0 |  |  |  |  | 0 | $self->_error("Wrong number of arguments for $callable", $node); | 
| 1259 |  |  |  |  |  |  | } | 
| 1260 |  |  |  |  |  |  |  | 
| 1261 | 54 |  |  |  |  | 163 | 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 |  |  |  |  | 1396 | (map { $self->push_expr($_) } @{$args}), | 
|  | 273 |  |  |  |  | 662 |  | 
|  | 403 |  |  |  |  | 1101 |  | 
| 1268 |  |  |  |  |  |  | $self->compile_ast($callable), | 
| 1269 |  |  |  |  |  |  | $self->opcode( 'funcall' ) | 
| 1270 |  |  |  |  |  |  | ); | 
| 1271 |  |  |  |  |  |  | } | 
| 1272 |  |  |  |  |  |  |  | 
| 1273 |  |  |  |  |  |  | # $~iterator | 
| 1274 |  |  |  |  |  |  | sub _generate_iterator { | 
| 1275 | 43 |  |  | 43 |  | 65 | my($self, $node) = @_; | 
| 1276 |  |  |  |  |  |  |  | 
| 1277 | 43 |  |  |  |  | 94 | my $item_var = $node->first; | 
| 1278 | 43 |  |  |  |  | 210 | my $lvar_id  = $self->lvar->{$item_var}; | 
| 1279 | 43 | 50 |  |  |  | 106 | 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 |  |  |  |  | 113 | 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 |  | 23 | my($self, $node) = @_; | 
| 1293 |  |  |  |  |  |  |  | 
| 1294 | 16 |  |  |  |  | 39 | my $item_var = $node->first; | 
| 1295 | 16 |  |  |  |  | 70 | my $lvar_id  = $self->lvar->{$item_var}; | 
| 1296 | 16 | 50 |  |  |  | 47 | 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 |  |  |  |  | 56 | return $self->opcode( | 
| 1302 |  |  |  |  |  |  | load_lvar => $lvar_id + 2, | 
| 1303 |  |  |  |  |  |  | symbol    => $node, | 
| 1304 |  |  |  |  |  |  | ); | 
| 1305 |  |  |  |  |  |  | } | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  | sub _generate_assign { | 
| 1308 | 59 |  |  | 59 |  | 95 | my($self, $node) = @_; | 
| 1309 | 59 |  |  |  |  | 135 | my $lhs     = $node->first; | 
| 1310 | 59 |  |  |  |  | 195 | my $rhs     = $node->second; | 
| 1311 | 59 |  |  |  |  | 136 | my $is_decl = $node->third; | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 | 59 |  |  |  |  | 144 | my $lvar      = $self->lvar; | 
| 1314 | 59 |  |  |  |  | 142 | my $lvar_name = $lhs->id; | 
| 1315 |  |  |  |  |  |  |  | 
| 1316 | 59 | 50 |  |  |  | 215 | if($node->id ne "=") { | 
| 1317 | 0 |  |  |  |  | 0 | $self->_error("Assignment ($node) is not supported", $node); | 
| 1318 |  |  |  |  |  |  | } | 
| 1319 |  |  |  |  |  |  |  | 
| 1320 | 59 |  |  |  |  | 189 | my @expr = $self->compile_ast($rhs); | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 | 59 | 100 |  |  |  | 488 | if($is_decl) { | 
| 1323 | 47 |  |  |  |  | 202 | $lvar->{$lvar_name} = $self->lvar_id; | 
| 1324 | 47 |  |  |  |  | 119 | $self->{lvar_id}    = $self->lvar_use(1); # don't use local() | 
| 1325 |  |  |  |  |  |  | } | 
| 1326 |  |  |  |  |  |  |  | 
| 1327 | 59 | 100 | 66 |  |  | 426 | if(!exists $lvar->{$lvar_name} or $lhs->arity ne "variable") { | 
| 1328 | 1 |  |  |  |  | 48 | $self->_error("Cannot modify $lhs, which is not a lexical variable", $node); | 
| 1329 |  |  |  |  |  |  | } | 
| 1330 |  |  |  |  |  |  |  | 
| 1331 |  |  |  |  |  |  | return | 
| 1332 |  |  |  |  |  |  | @expr, | 
| 1333 | 58 |  |  |  |  | 249 | $self->opcode( save_to_lvar => $lvar->{$lvar_name}, symbol => $lhs, comment => $node->id); | 
| 1334 |  |  |  |  |  |  | } | 
| 1335 |  |  |  |  |  |  |  | 
| 1336 |  |  |  |  |  |  | sub _generate_constant { | 
| 1337 | 72 |  |  | 72 |  | 138 | my($self, $node) = @_; | 
| 1338 | 72 |  |  |  |  | 222 | my $lhs     = $node->first; | 
| 1339 | 72 |  |  |  |  | 164 | my $rhs     = $node->second; | 
| 1340 |  |  |  |  |  |  |  | 
| 1341 | 72 |  |  |  |  | 198 | my @expr = $self->compile_ast($rhs); | 
| 1342 |  |  |  |  |  |  |  | 
| 1343 | 72 |  |  |  |  | 216 | my $lvar            = $self->lvar; | 
| 1344 | 72 |  |  |  |  | 184 | my $lvar_id         = $self->lvar_id; | 
| 1345 | 72 |  |  |  |  | 202 | my $lvar_name       = $lhs->id; | 
| 1346 | 72 |  |  |  |  | 172 | $lvar->{$lvar_name} = $lvar_id; | 
| 1347 | 72 |  |  |  |  | 176 | $self->{lvar_id}    = $self->lvar_use(1); # don't use local() | 
| 1348 |  |  |  |  |  |  |  | 
| 1349 | 72 | 50 |  |  |  | 195 | if($OPTIMIZE) { | 
| 1350 | 72 | 100 | 100 |  |  | 311 | if(@expr == 1 | 
| 1351 |  |  |  |  |  |  | && any_in($expr[0][_OP_NAME], qw(literal load_lvar))) { | 
| 1352 | 33 |  |  |  |  | 84 | $expr[0][_OP_COMMENT] = "constant $lvar_name"; | 
| 1353 | 33 |  |  |  |  | 106 | $self->const->[$lvar_id] = \@expr; | 
| 1354 | 33 |  |  |  |  | 117 | return @expr; # no real definition | 
| 1355 |  |  |  |  |  |  | } | 
| 1356 |  |  |  |  |  |  | } | 
| 1357 |  |  |  |  |  |  |  | 
| 1358 |  |  |  |  |  |  | return | 
| 1359 | 39 |  |  |  |  | 155 | @expr, | 
| 1360 |  |  |  |  |  |  | $self->opcode( save_to_lvar => $lvar_id, symbol => $lhs, comment => $node->id); | 
| 1361 |  |  |  |  |  |  | } | 
| 1362 |  |  |  |  |  |  |  | 
| 1363 |  |  |  |  |  |  | sub _localize_vars { | 
| 1364 | 31 |  |  | 31 |  | 63 | my($self, $vars) = @_; | 
| 1365 | 31 |  |  |  |  | 37 | my @localize; | 
| 1366 | 31 |  |  |  |  | 44 | my @pairs = @{$vars}; | 
|  | 31 |  |  |  |  | 72 |  | 
| 1367 |  |  |  |  |  |  |  | 
| 1368 | 31 | 100 |  |  |  | 108 | if( (@pairs % 2) != 0 ) { | 
| 1369 | 8 | 100 |  |  |  | 24 | if(@pairs == 1) { | 
| 1370 | 7 |  |  |  |  | 18 | return $self->compile_ast(@pairs), | 
| 1371 |  |  |  |  |  |  | $self->opcode( 'localize_vars' ); | 
| 1372 |  |  |  |  |  |  | } | 
| 1373 |  |  |  |  |  |  | else { | 
| 1374 | 1 |  |  |  |  | 4 | $self->_error("You must pass pairs of expressions to include"); | 
| 1375 |  |  |  |  |  |  | } | 
| 1376 |  |  |  |  |  |  | } | 
| 1377 |  |  |  |  |  |  |  | 
| 1378 | 23 |  |  |  |  | 104 | while(my($key, $expr) = splice @pairs, 0, 2) { | 
| 1379 | 28 | 50 |  |  |  | 125 | 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 |  |  |  |  | 82 | push @localize, | 
| 1383 |  |  |  |  |  |  | $self->compile_ast($expr), | 
| 1384 |  |  |  |  |  |  | $self->opcode( localize_s => $key->value, symbol => $key ); | 
| 1385 |  |  |  |  |  |  | } | 
| 1386 | 23 |  |  |  |  | 85 | return @localize; | 
| 1387 |  |  |  |  |  |  | } | 
| 1388 |  |  |  |  |  |  |  | 
| 1389 |  |  |  |  |  |  | sub _variable_to_value { | 
| 1390 | 2735 |  |  | 2735 |  | 5707 | my($self, $arg) = @_; | 
| 1391 |  |  |  |  |  |  |  | 
| 1392 | 2735 |  |  |  |  | 7814 | my $name = $arg->value; | 
| 1393 | 2735 |  |  |  |  | 10152 | $name =~ s/\$//; | 
| 1394 | 2735 |  |  |  |  | 10348 | return $name; | 
| 1395 |  |  |  |  |  |  | } | 
| 1396 |  |  |  |  |  |  |  | 
| 1397 |  |  |  |  |  |  | sub requires { | 
| 1398 | 102 |  |  | 102 | 0 | 248 | my($self, @files) = @_; | 
| 1399 | 102 |  |  |  |  | 155 | push @{ $self->dependencies }, @files; | 
|  | 102 |  |  |  |  | 366 |  | 
| 1400 | 102 |  |  |  |  | 253 | return; | 
| 1401 |  |  |  |  |  |  | } | 
| 1402 |  |  |  |  |  |  |  | 
| 1403 |  |  |  |  |  |  | sub can_be_in_list_context { | 
| 1404 | 7 |  |  | 7 | 0 | 14 | my $i = 2; | 
| 1405 | 7 |  |  |  |  | 49 | while(my $funcname = (caller ++$i)[3]) { | 
| 1406 | 14 | 100 |  |  |  | 91 | if($funcname =~ /::_generate_(\w+) \z/xms) { | 
| 1407 | 7 |  |  |  |  | 25 | 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 |  | 2615 | my($self, @code) = @_; | 
| 1421 | 1340 |  | 66 |  |  | 9913 | 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 |  | 223 | my($self, $code) = @_; | 
| 1428 | 140 | 50 |  |  |  | 523 | my $engine = $self->engine or return 0; | 
| 1429 |  |  |  |  |  |  |  | 
| 1430 | 140 |  |  |  |  | 374 | local $engine->{warn_handler} = \&Carp::croak; | 
| 1431 | 140 |  |  |  |  | 314 | local $engine->{die_handler}  = \&Carp::croak; | 
| 1432 | 140 |  |  |  |  | 308 | local $engine->{verbose}      = 1; | 
| 1433 |  |  |  |  |  |  |  | 
| 1434 | 140 |  |  |  |  | 220 | my $result = eval { | 
| 1435 | 140 |  |  |  |  | 177 | my @tmp_code = (@{$code}, $self->opcode('print_raw'), $self->opcode('end')); | 
|  | 140 |  |  |  |  | 371 |  | 
| 1436 | 140 |  |  |  |  | 2546 | $engine->_assemble(\@tmp_code, '', undef, undef, undef); | 
| 1437 | 140 |  |  |  |  | 2800 | $engine->render(''); | 
| 1438 |  |  |  |  |  |  | }; | 
| 1439 | 140 | 50 |  |  |  | 424 | if($@) { | 
| 1440 | 0 |  |  |  |  | 0 | Carp::carp("[BUG] Constant folding failed (ignored): $@"); | 
| 1441 | 0 |  |  |  |  | 0 | return 0; | 
| 1442 |  |  |  |  |  |  | } | 
| 1443 |  |  |  |  |  |  |  | 
| 1444 | 140 |  |  |  |  | 360 | @{$code} = ($self->opcode( literal => $result, comment => "optimized by constant folding")); | 
|  | 140 |  |  |  |  | 504 |  | 
| 1445 | 140 |  |  |  |  | 495 | return 1; | 
| 1446 |  |  |  |  |  |  | } | 
| 1447 |  |  |  |  |  |  |  | 
| 1448 |  |  |  |  |  |  |  | 
| 1449 |  |  |  |  |  |  | sub _noop { | 
| 1450 | 6216 |  |  | 6216 |  | 9224 | my($self, $op) = @_; | 
| 1451 | 6216 |  |  |  |  | 7042 | @{$op} = @{ $self->opcode( noop => undef, comment => "ex-$op->[0]") }; | 
|  | 6216 |  |  |  |  | 19552 |  | 
|  | 6216 |  |  |  |  | 17471 |  | 
| 1452 | 6216 |  |  |  |  | 14118 | return; | 
| 1453 |  |  |  |  |  |  | } | 
| 1454 |  |  |  |  |  |  |  | 
| 1455 |  |  |  |  |  |  | sub _optimize_vmcode { | 
| 1456 | 10110 |  |  | 10110 |  | 20921 | 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 | 10110 |  |  |  |  | 16985 | my @goto_addr; | 
| 1473 | 10110 |  |  |  |  | 20174 | for(my $i = 0; $i < @{$c}; $i++) { | 
|  | 135173 |  |  |  |  | 364181 |  | 
| 1474 | 125063 | 100 |  |  |  | 372966 | if(exists $goto_family{ $c->[$i][_OP_NAME] }) { | 
| 1475 | 3459 |  |  |  |  | 5082 | my $addr = $c->[$i][_OP_ARG]; # relational addr | 
| 1476 |  |  |  |  |  |  |  | 
| 1477 |  |  |  |  |  |  | # mark ragens that goto family have its effects | 
| 1478 | 3459 | 100 |  |  |  | 10859 | my @range = $addr > 0 | 
| 1479 |  |  |  |  |  |  | ? ($i .. ($i+$addr-1))  # positive | 
| 1480 |  |  |  |  |  |  | : (($i+$addr) .. $i); # negative | 
| 1481 |  |  |  |  |  |  |  | 
| 1482 | 3459 |  |  |  |  | 5952 | foreach my $j(@range) { | 
| 1483 | 21752 |  | 100 |  |  | 24227 | push @{$goto_addr[$j] ||= []}, $c->[$i]; | 
|  | 21752 |  |  |  |  | 80469 |  | 
| 1484 |  |  |  |  |  |  | } | 
| 1485 |  |  |  |  |  |  | } | 
| 1486 |  |  |  |  |  |  | } | 
| 1487 |  |  |  |  |  |  |  | 
| 1488 | 10110 |  |  |  |  | 20128 | for(my $i = 0; $i < @{$c}; $i++) { | 
|  | 135173 |  |  |  |  | 372502 |  | 
| 1489 | 125063 |  |  |  |  | 213041 | my $name = $c->[$i][_OP_NAME]; | 
| 1490 | 125063 | 100 |  |  |  | 479376 | if($name eq 'print_raw_s') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1491 |  |  |  |  |  |  | # merge a chain of print_raw_s into single command | 
| 1492 | 14415 |  |  |  |  | 28614 | my $j = $i + 1; # from the next op | 
| 1493 | 14415 |  | 66 |  |  | 27856 | while($j < @{$c} | 
|  | 20051 |  | 100 |  |  | 151446 |  | 
| 1494 |  |  |  |  |  |  | && $c->[$j][_OP_NAME] eq 'print_raw_s' | 
| 1495 | 5806 | 100 |  |  |  | 21987 | && "@{$goto_addr[$i] || []}" eq "@{$goto_addr[$j] || []}") { | 
|  | 5806 | 100 |  |  |  | 30965 |  | 
| 1496 |  |  |  |  |  |  |  | 
| 1497 | 5636 |  |  |  |  | 13369 | $c->[$i][_OP_ARG] .= $c->[$j][_OP_ARG]; | 
| 1498 |  |  |  |  |  |  |  | 
| 1499 | 5636 |  |  |  |  | 11641 | $self->_noop($c->[$j]); | 
| 1500 | 5636 |  |  |  |  | 7418 | $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 |  |  |  |  | 1817 | my $it = $c->[$i]; | 
| 1514 | 1222 |  |  |  |  | 2094 | my $nn = $c->[$i+2]; # next next | 
| 1515 | 1222 | 100 | 66 |  |  | 7761 | if(defined($nn) | 
|  |  |  | 66 |  |  |  |  | 
| 1516 |  |  |  |  |  |  | && $nn->[_OP_NAME] eq 'load_lvar_to_sb' | 
| 1517 |  |  |  |  |  |  | && $nn->[_OP_ARG] == $it->[_OP_ARG]) { | 
| 1518 | 580 |  |  |  |  | 735 | @{$it} = @{$self->opcode( move_to_sb => undef, comment => "ex-$it->[0]" )}; | 
|  | 580 |  |  |  |  | 2161 |  | 
|  | 580 |  |  |  |  | 1996 |  | 
| 1519 |  |  |  |  |  |  |  | 
| 1520 | 580 |  |  |  |  | 2109 | $self->_noop($nn); | 
| 1521 |  |  |  |  |  |  | } | 
| 1522 |  |  |  |  |  |  | } | 
| 1523 |  |  |  |  |  |  | elsif($name eq 'literal') { | 
| 1524 | 19333 | 100 |  |  |  | 48760 | if(is_int($c->[$i][_OP_ARG])) { | 
| 1525 | 916 |  |  |  |  | 1822 | $c->[$i][_OP_NAME] = 'literal_i'; | 
| 1526 | 916 |  |  |  |  | 2788 | $c->[$i][_OP_ARG]  = int($c->[$i][_OP_ARG]); # force int | 
| 1527 |  |  |  |  |  |  | } | 
| 1528 |  |  |  |  |  |  | } | 
| 1529 |  |  |  |  |  |  | elsif($name eq 'fetch_field') { | 
| 1530 | 138 |  |  |  |  | 227 | my $prev = $c->[$i-1]; | 
| 1531 | 138 | 50 |  |  |  | 433 | 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 | 10110 |  |  |  |  | 20644 | for(my $i = 0; $i < @{$c}; $i++) { | 
|  | 129597 |  |  |  |  | 346864 |  | 
| 1542 | 119487 | 100 |  |  |  | 336352 | if($c->[$i][_OP_NAME] eq 'noop') { | 
| 1543 | 5576 | 100 |  |  |  | 10889 | if(defined $goto_addr[$i]) { | 
| 1544 | 388 |  |  |  |  | 472 | foreach my $goto(@{ $goto_addr[$i] }) { | 
|  | 388 |  |  |  |  | 745 |  | 
| 1545 |  |  |  |  |  |  | # reduce its absolute value | 
| 1546 | 596 | 100 |  |  |  | 1456 | $goto->[1] > 0 | 
| 1547 |  |  |  |  |  |  | ? $goto->[1]--  # positive | 
| 1548 |  |  |  |  |  |  | : $goto->[1]++; # negative | 
| 1549 |  |  |  |  |  |  | } | 
| 1550 |  |  |  |  |  |  | } | 
| 1551 | 5576 |  |  |  |  | 6727 | splice @{$c}, $i, 1; | 
|  | 5576 |  |  |  |  | 11520 |  | 
| 1552 |  |  |  |  |  |  | # adjust @goto_addr, but it may be empty | 
| 1553 | 5576 | 100 |  |  |  | 18656 | splice @goto_addr, $i, 1 if @goto_addr > $i; | 
| 1554 |  |  |  |  |  |  | } | 
| 1555 |  |  |  |  |  |  | } | 
| 1556 | 10110 |  |  |  |  | 45135 | return; | 
| 1557 |  |  |  |  |  |  | } | 
| 1558 |  |  |  |  |  |  |  | 
| 1559 |  |  |  |  |  |  | sub as_assembly { | 
| 1560 | 7 |  |  | 7 | 0 | 12 | my($self, $code_ref, $addix) = @_; | 
| 1561 |  |  |  |  |  |  |  | 
| 1562 | 7 |  |  |  |  | 11 | my $asm = ""; | 
| 1563 | 7 |  |  |  |  | 11 | foreach my $ix(0 .. (@{$code_ref}-1)) { | 
|  | 7 |  |  |  |  | 58 |  | 
| 1564 | 47 |  |  |  |  | 57 | my($name, $arg, $line, $file, $label, $comment) = @{$code_ref->[$ix]}; | 
|  | 47 |  |  |  |  | 105 |  | 
| 1565 | 47 | 50 |  |  |  | 97 | $asm .= "$ix:" if $addix; # for debugging | 
| 1566 |  |  |  |  |  |  |  | 
| 1567 |  |  |  |  |  |  | # "$opname $arg #$line:$file *$symbol // $comment" | 
| 1568 | 47 | 50 |  |  |  | 90 | ref($name) and die "Oops: " . p($code_ref->[$ix]); | 
| 1569 | 47 |  |  |  |  | 66 | $asm .= $name; | 
| 1570 | 47 | 100 |  |  |  | 100 | if(defined $arg) { | 
| 1571 | 11 |  |  |  |  | 28 | $asm .= " " . value_to_literal($arg); | 
| 1572 |  |  |  |  |  |  | } | 
| 1573 | 47 | 100 |  |  |  | 98 | if(defined $line) { | 
| 1574 | 28 |  |  |  |  | 40 | $asm .= " #$line"; | 
| 1575 | 28 | 100 |  |  |  | 65 | if(defined $file) { | 
| 1576 | 7 |  |  |  |  | 21 | $asm .= ":" . value_to_literal($file); | 
| 1577 |  |  |  |  |  |  | } | 
| 1578 |  |  |  |  |  |  | } | 
| 1579 | 47 | 50 |  |  |  | 92 | if(defined $label) { | 
| 1580 | 0 |  |  |  |  | 0 | $asm .= " " . value_to_literal($label); | 
| 1581 |  |  |  |  |  |  | } | 
| 1582 | 47 | 100 |  |  |  | 92 | if(defined $comment) { | 
| 1583 | 4 |  |  |  |  | 9 | $asm .= " // $comment"; | 
| 1584 |  |  |  |  |  |  | } | 
| 1585 | 47 |  |  |  |  | 79 | $asm .= "\n"; | 
| 1586 |  |  |  |  |  |  | } | 
| 1587 | 7 |  |  |  |  | 103 | return $asm; | 
| 1588 |  |  |  |  |  |  | } | 
| 1589 |  |  |  |  |  |  |  | 
| 1590 |  |  |  |  |  |  | sub _error { | 
| 1591 | 18 |  |  | 18 |  | 35 | my($self, $message, $node) = @_; | 
| 1592 |  |  |  |  |  |  |  | 
| 1593 | 18 | 100 |  |  |  | 71 | my $line = ref($node) ? $node->line : $node; | 
| 1594 | 18 |  |  |  |  | 120 | die $self->make_error($message, $self->file, $line); | 
| 1595 |  |  |  |  |  |  | } | 
| 1596 |  |  |  |  |  |  |  | 
| 1597 | 169 |  |  | 169 |  | 1509 | no Mouse; | 
|  | 169 |  |  |  |  | 398 |  | 
|  | 169 |  |  |  |  | 1312 |  | 
| 1598 | 169 |  |  | 169 |  | 21995 | no Mouse::Util::TypeConstraints; | 
|  | 169 |  |  |  |  | 345 |  | 
|  | 169 |  |  |  |  | 1289 |  | 
| 1599 |  |  |  |  |  |  |  | 
| 1600 |  |  |  |  |  |  | __PACKAGE__->meta->make_immutable; | 
| 1601 |  |  |  |  |  |  | __END__ |