1package Text::Xslate::PP::State; # implement tx_state_t 2use Mouse; 3 4use Text::Xslate::Util qw(neat p $DEBUG); 5use Text::Xslate::PP; 6use Text::Xslate::PP::Const qw( 7 TXframe_NAME TXframe_RETADDR TXframe_OUTPUT 8 TX_VERBOSE_DEFAULT); 9 10if(!Text::Xslate::PP::_PP_ERROR_VERBOSE()) { 11 our @CARP_NOT = qw( 12 Text::Xslate::PP::Opcode 13 Text::Xslate::PP::Booter 14 Text::Xslate::PP::Method 15 ); 16} 17 18has vars => ( 19 is => 'rw', 20); 21 22has tmpl => ( 23 is => 'rw', 24); 25 26has engine => ( 27 is => 'rw', 28 weak_ref => 1, 29); 30 31has frame => ( 32 is => 'rw', 33); 34 35has current_frame => ( 36 is => 'rw', 37); 38 39# opinfo is integrated into code 40#has info => ( 41# is => 'rw', 42#); 43 44has code => ( 45 is => 'rw', 46); 47 48has code_len => ( 49 is => 'rw', 50); 51 52has symbol => ( 53 is => 'rw', 54); 55 56has local_stack => ( 57 is => 'rw', 58); 59 60has encoding => ( 61 is => 'ro', 62 init_arg => undef, 63 lazy => 1, 64 default => sub { 65 require Encode; 66 return Encode::find_encoding('UTF-8'); 67 }, 68); 69 70sub fetch { 71 # my ( $st, $var, $key, $frame, $line ) = @_; 72 my $ret; 73 74 if ( Scalar::Util::blessed($_[1]) ) { 75 my $key = $_[2]; 76 $ret = eval { $_[1]->$key() }; 77 $_[0]->error( [ $_[3], $_[4] ], "%s", $@ ) if $@; 78 } 79 elsif ( ref $_[1] eq 'HASH' ) { 80 if ( defined $_[2] ) { 81 $ret = $_[1]->{ $_[2] }; 82 } 83 else { 84 $_[0]->warn( [ $_[3], $_[4] ], "Use of nil as a field key" ); 85 } 86 } 87 elsif ( ref $_[1] eq 'ARRAY' ) { 88 if ( Scalar::Util::looks_like_number($_[2]) ) { 89 $ret = $_[1]->[ $_[2] ]; 90 } 91 else { 92 $_[0]->warn( [ $_[3], $_[4] ], "Use of %s as an array index", neat( $_[2] ) ); 93 } 94 } 95 elsif ( $_[1] ) { 96 $_[0]->error( [ $_[3], $_[4] ], "Cannot access %s (%s is not a container)", neat($_[2]), neat($_[1]) ); 97 } 98 else { 99 $_[0]->warn( [ $_[3], $_[4] ], "Use of nil to access %s", neat( $_[2] ) ); 100 } 101 102 return $ret; 103} 104 105sub fetch_symbol { 106 my ( $st, $name, $context ) = @_; 107 108 my $symbol_table = $st->symbol; 109 if ( !exists $symbol_table->{ $name } ) { 110 if(defined $context) { 111 my($frame, $line) = @{$context}; 112 if ( defined $line ) { 113 $st->{ pc } = $line; 114 $st->frame->[ $st->current_frame ]->[ TXframe_NAME ] = $frame; 115 } 116 } 117 Carp::croak( sprintf( "Undefined symbol %s", $name ) ); 118 } 119 120 return $symbol_table->{ $name }; 121} 122 123sub localize { 124 my($st, $key, $newval) = @_; 125 my $vars = $st->vars; 126 my $preeminent = exists $vars->{$key}; 127 my $oldval = delete $vars->{$key}; 128 129 my $cleanup = $preeminent 130 ? sub { $vars->{$key} = $oldval; return } 131 : sub { delete $vars->{$key}; return }; 132 133 push @{ $st->{local_stack} ||= [] }, 134 bless($cleanup, 'Text::Xslate::PP::Guard'); 135 136 $vars->{$key} = $newval; 137 return; 138} 139 140sub push_frame { 141 my ( $st, $name, $retaddr ) = @_; 142 143 if ( $st->current_frame > 100 ) { 144 Carp::croak("Macro call is too deep (> 100)"); 145 } 146 147 my $new = $st->frame->[ $st->current_frame( $st->current_frame + 1 ) ] 148 ||= []; 149 $new->[ TXframe_NAME ] = $name; 150 $new->[ TXframe_RETADDR ] = $retaddr; 151 return $new; 152} 153 154sub pop_frame { 155 my( $st, $replace_output ) = @_; 156 $st->current_frame( $st->current_frame - 1 ); 157 if($replace_output) { 158 my $top = $st->frame->[ $st->current_frame + 1]; 159 ($st->{output}, $top->[ TXframe_OUTPUT ]) 160 = ($top->[ TXframe_OUTPUT ], $st->{output}); 161 } 162 163 return; 164} 165 166sub pad { 167 return $_[0]->{frame}->[ $_[0]->{current_frame} ]; 168} 169 170sub op_arg { 171 $_[0]->{ code }->[ $_[0]->{ pc } ]->{ arg }; 172} 173 174sub print { 175 my($st, $sv, $frame_and_line) = @_; 176 if ( ref( $sv ) eq Text::Xslate::PP::TXt_RAW ) { 177 if(defined ${$sv}) { 178 $st->{output} .= 179 (utf8::is_utf8($st->{output}) && !utf8::is_utf8(${$sv})) 180 ? eval {$st->encoding->decode(${$sv}, Encode::FB_CROAK())} || ${$sv} 181 : ${$sv}; 182 } 183 else { 184 $st->warn($frame_and_line, "Use of nil to print" ); 185 } 186 } 187 elsif ( defined $sv ) { 188 $sv =~ s/($Text::Xslate::PP::html_metachars)/$Text::Xslate::PP::html_escape{$1}/xmsgeo; 189 $st->{output} .= 190 (utf8::is_utf8($st->{output}) && !utf8::is_utf8($sv)) 191 ? eval {$st->encoding->decode($sv, Encode::FB_CROAK())} || $sv 192 : $sv; 193 } 194 else { 195 $st->warn( $frame_and_line, "Use of nil to print" ); 196 } 197 return; 198} 199 200sub _doerror { 201 my ( $st, $context, $fmt, @args ) = @_; 202 if(defined $context) { # hack to share it with PP::Booster and PP::Opcode 203 my($frame, $line) = @{$context}; 204 if ( defined $line ) { 205 $st->{ pc } = $line; 206 $st->frame->[ $st->current_frame ]->[ TXframe_NAME ] = $frame; 207 } 208 } 209 Carp::carp( sprintf( $fmt, @args ) ); 210 return; 211} 212 213sub warn :method { 214 my $st = shift; 215 if( $st->engine->{verbose} > TX_VERBOSE_DEFAULT ) { 216 $st->_doerror(@_); 217 } 218 return; 219} 220 221 222sub error :method { 223 my $st = shift; 224 if( $st->engine->{verbose} >= TX_VERBOSE_DEFAULT ) { 225 $st->_doerror(@_); 226 } 227 return; 228} 229 230sub bad_arg { 231 my $st = shift; 232 unshift @_, undef if @_ == 1; # hack to share it with PP::Booster and PP::Opcode 233 my($context, $name) = @_; 234 return $st->error($context, "Wrong number of arguments for %s", $name); 235} 236 237no Mouse; 238__PACKAGE__->meta->make_immutable; 2391; 240__END__ 241 242 243=head1 NAME 244 245Text::Xslate::PP::State - Text::Xslate pure-Perl virtual machine state 246 247=head1 DESCRIPTION 248 249This module is used by Text::Xslate::PP internally. 250 251=head1 SEE ALSO 252 253L<Text::Xslate> 254 255L<Text::Xslate::PP> 256 257=head1 AUTHOR 258 259Makamaka Hannyaharamitu E<lt>makamaka at cpan.orgE<gt> 260 261Text::Xslate was written by Fuji, Goro (gfx). 262 263=head1 LICENSE AND COPYRIGHT 264 265Copyright (c) 2010 by Makamaka Hannyaharamitu (makamaka). 266 267This library is free software; you can redistribute it and/or modify 268it under the same terms as Perl itself. 269 270=cut 271