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