File Coverage

blib/lib/EJS/Template/Parser/Context.pm
Criterion Covered Total %
statement 110 116 94.8
branch 50 54 92.5
condition 12 15 80.0
subroutine 15 16 93.7
pod 3 3 100.0
total 190 204 93.1


line stmt bran cond sub pod time code
1 8     8   24 use strict;
  8         7  
  8         161  
2 8     8   20 use warnings;
  8         7  
  8         213  
3              
4             =head1 NAME
5              
6             EJS::Template::Parser::Context - Implementation of EJS::Template::Parser
7              
8             =cut
9              
10             package EJS::Template::Parser::Context;
11 8     8   23 use base 'EJS::Template::Base';
  8         6  
  8         441  
12              
13 8     8   31 use EJS::Template::Runtime;
  8         4  
  8         7984  
14              
15             my $states = {
16             'INIT' => {
17             key => 'INIT', method => '_in_init',
18             js_open => '', js_close => '',
19             },
20             'TEXT' => {
21             key => 'TEXT', method => '_in_text',
22             js_open => 'print("', js_close => '");',
23             },
24             '<%' => {
25             key => '<%' , method => '_in_script',
26             js_open => '', js_close => '',
27             },
28             '<%=' => {
29             key => '<%=' , method => '_in_script',
30             js_open => 'print(' , js_close => ');',
31             },
32             };
33              
34             =head1 Methods
35              
36             =head2 new
37              
38             Creates a new parser context.
39              
40             =cut
41              
42             sub new {
43 83     83 1 102 my ($class, $config) = @_;
44 83         201 my $self = $class->SUPER::new($config);
45            
46 83         163 $self->{stack} = [];
47 83         127 $self->{result} = [];
48            
49 83         85 $self->{default_escape} = do {
50 83   100     182 my $name = $self->config('escape') || '';
51            
52 83 100 100     316 if ($name eq '' || $name eq 'raw') {
53 78         163 '';
54             } else {
55 5 50       18 $EJS::Template::Runtime::ESCAPES{$name} || '';
56             }
57             };
58            
59 83         174 $self->_push_state('INIT');
60 83         149 return $self;
61             }
62              
63             =head2 read_line
64              
65             Parses a line.
66              
67             =cut
68              
69             sub read_line {
70 143     143 1 160 my ($self, $line) = @_;
71            
72 143         908 while ($line =~ m{(.*?)((^\s*)?<%(?:(?:\s*:\s*\w+\s*)?=)?|%>(\s*?(?:\n|$))?|\\?["']|/\*|\*/|//|\n|$)}g) {
73 468         888 my ($text, $mark, $left, $right) = ($1, $2, $3, $4);
74 468         306 my $escape;
75            
76 468 100       946 if ($mark =~ s/<%\s*:\s*(\w+)\s*=/<%=/) {
    100          
77 3         4 my $name = $1;
78 3   50     9 $escape = $EJS::Template::Runtime::ESCAPES{$name} || '';
79             } elsif ($mark eq '<%=') {
80 45         69 $escape = $self->{default_escape};
81             }
82            
83 468         461 $mark =~ s/\s+(<%=?)/$1/;
84 468         376 $mark =~ s/(%>)\s+/$1/;
85            
86 468         854 my $opt = {
87             left => $left ,
88             right => $right ,
89             escape => $escape,
90             };
91            
92 468 100       832 $self->_handle_token($text) if $text ne '';
93 468 100       1669 $self->_handle_token($mark, $opt) if $mark ne '';
94             }
95             }
96              
97             =head2 result
98              
99             Retrieves the result (an array ref of the generated JavaScript texts).
100              
101             =cut
102              
103             sub result {
104 83     83 1 86 my ($self) = @_;
105 83         69 $self->_pop_state() until @{$self->{stack}} <= 1;
  93         235  
106 83         367 return $self->{result};
107             }
108              
109             sub _handle_token {
110 550     550   519 my ($self, $token, $opt) = @_;
111 550         589 my $method = $self->{stack}[-1]{state}{method};
112 550         800 return $self->$method($token, $opt);
113             }
114              
115             sub _top_key {
116 0     0   0 my ($self) = @_;
117 0         0 return $self->{stack}[-1]{state}{key};
118             }
119              
120             sub _top_opt {
121 135     135   115 my ($self) = @_;
122 135   50     433 return $self->{stack}[-1]{opt} || {};
123             }
124              
125             sub _append_result {
126 732     732   570 my ($self, $text) = @_;
127 732         465 push @{$self->{result}}, $text;
  732         2076  
128             }
129              
130             sub _push_state {
131 240     240   265 my ($self, $state_key, $opt) = @_;
132 240         259 my $state = $states->{$state_key};
133 240 100       516 $self->_append_result($state->{js_open}) if $state->{js_open} ne '';
134 240         400 my $entry = {state => $state, opt => $opt};
135 240         184 push @{$self->{stack}}, $entry;
  240         570  
136             }
137              
138             sub _pop_state {
139 157     157   156 my ($self) = @_;
140 157         111 my $entry = pop @{$self->{stack}};
  157         205  
141 157         147 my $state = $entry->{state};
142 157 100       496 $self->_append_result($state->{js_close}) if $state->{js_close} ne '';
143             }
144              
145             sub _in_init {
146 131     131   131 my ($self, $token, $opt) = @_;
147            
148 131 100       328 if ($token eq '<%') {
    100          
    50          
149 31 50       59 if (defined $opt->{left}) {
150 31         41 $opt->{ltrim} = {};
151            
152 31 100       61 if ($opt->{left} ne '') {
153 16         25 $self->_append_result($opt->{left});
154 16         15 $opt->{ltrim}{index} = $#{$self->{result}};
  16         34  
155             }
156             }
157            
158 31         42 $self->_push_state($token, $opt);
159             } elsif ($token eq "<%=") {
160 40 100 66     178 if (defined $opt->{left} && $opt->{left} ne '') {
161 8         13 $self->_push_state('TEXT');
162 8         14 $self->_append_result($opt->{left});
163 8         11 $self->_pop_state();
164             }
165            
166 40         53 $self->_push_state($token, $opt);
167            
168 40 100       352 if (my $escape = $opt->{escape}) {
169 1         3 $self->_append_result(qq{EJS.$escape(});
170             }
171             } elsif ($token eq "\n") {
172 0         0 $self->_push_state('TEXT');
173 0         0 $self->_append_result("\\n");
174 0         0 $self->_pop_state();
175 0         0 $self->_append_result("\n");
176             } else {
177 60         91 $token =~ s/([\\"])/\\$1/g;
178 60         111 $self->_push_state('TEXT');
179 60         105 $self->_append_result($token);
180             }
181             }
182              
183             sub _in_text {
184 83     83   81 my ($self, $token, $opt) = @_;
185            
186 83 100       204 if ($token eq '<%') {
    100          
    100          
187 3         7 $self->_pop_state();
188 3         4 $self->_push_state($token, $opt);
189             } elsif ($token eq '<%=') {
190 15         27 $self->_pop_state();
191 15         22 $self->_push_state($token, $opt);
192            
193 15 100       82 if (my $escape = $opt->{escape}) {
194 4         11 $self->_append_result(qq{EJS.$escape(});
195             }
196             } elsif ($token eq "\n") {
197 40         58 $self->_append_result("\\n");
198 40         52 $self->_pop_state();
199 40         51 $self->_append_result("\n");
200             } else {
201 25         50 $token =~ s/([\\"])/\\$1/g;
202 25         31 $self->_append_result($token);
203             }
204             }
205              
206             sub _in_script {
207 336     336   317 my ($self, $token, $opt) = @_;
208            
209 336 100       434 if ($token eq '%>') {
210 81 100       123 if (my $ltrim = $self->_top_opt->{ltrim}) {
211 27 100       50 if (defined $opt->{right}) {
212 25 100       53 if ($opt->{right} ne '') {
213 15         31 $self->_append_result($opt->{right});
214             }
215             } else {
216 2 50       6 if (defined(my $idx = $ltrim->{index})) {
217 2         3 my $left = $self->{result}[$idx];
218 2         5 $self->{result}[$idx] = qq{print("$left");};
219             }
220             }
221            
222 27         46 $self->_pop_state();
223             } else {
224 54 100       75 if ($self->_top_opt->{escape}) {
225 5         9 $self->_append_result(qq{)});
226             }
227            
228 54         91 $self->_pop_state();
229            
230 54         62 my $right = $opt->{right};
231            
232 54 100 100     375 if (defined $right && $right ne '') {
233 17 100       62 if ($right =~ s/\n$//) {
234 15         47 $self->_append_result(qq{print("$right\\n");\n});
235             } else {
236 2         5 $self->_append_result(qq{print("$right");});
237             }
238             }
239             }
240             } else {
241 255         293 $self->_append_result($token);
242             }
243             }
244              
245             =head1 SEE ALSO
246              
247             =over 4
248              
249             =item * L
250              
251             =item * L
252              
253             =back
254              
255             =cut
256              
257             1;