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   56 use strict;
  8         15  
  8         221  
2 8     8   39 use warnings;
  8         16  
  8         303  
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   59 use base 'EJS::Template::Base';
  8         13  
  8         540  
12              
13 8     8   62 use EJS::Template::Runtime;
  8         17  
  8         12213  
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 258 my ($class, $config) = @_;
44 83         308 my $self = $class->SUPER::new($config);
45            
46 83         298 $self->{stack} = [];
47 83         240 $self->{result} = [];
48            
49 83         150 $self->{default_escape} = do {
50 83   100     230 my $name = $self->config('escape') || '';
51            
52 83 100 100     316 if ($name eq '' || $name eq 'raw') {
53 78         213 '';
54             } else {
55 5 50       22 $EJS::Template::Runtime::ESCAPES{$name} || '';
56             }
57             };
58            
59 83         354 $self->_push_state('INIT');
60 83         219 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 353 my ($self, $line) = @_;
71            
72 143         1618 while ($line =~ m{(.*?)((^\s*)?<%(?:(?:\s*:\s*\w+\s*)?=)?|%>(\s*?(?:\n|$))?|\\?["']|/\*|\*/|//|\n|$)}g) {
73 468         1726 my ($text, $mark, $left, $right) = ($1, $2, $3, $4);
74 468         714 my $escape;
75            
76 468 100       1265 if ($mark =~ s/<%\s*:\s*(\w+)\s*=/<%=/) {
    100          
77 3         9 my $name = $1;
78 3   50     9 $escape = $EJS::Template::Runtime::ESCAPES{$name} || '';
79             } elsif ($mark eq '<%=') {
80 45         130 $escape = $self->{default_escape};
81             }
82            
83 468         806 $mark =~ s/\s+(<%=?)/$1/;
84 468         776 $mark =~ s/(%>)\s+/$1/;
85            
86 468         1350 my $opt = {
87             left => $left ,
88             right => $right ,
89             escape => $escape,
90             };
91            
92 468 100       1314 $self->_handle_token($text) if $text ne '';
93 468 100       2186 $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 184 my ($self) = @_;
105 83         143 $self->_pop_state() until @{$self->{stack}} <= 1;
  93         341  
106 83         458 return $self->{result};
107             }
108              
109             sub _handle_token {
110 550     550   1095 my ($self, $token, $opt) = @_;
111 550         1054 my $method = $self->{stack}[-1]{state}{method};
112 550         1354 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   256 my ($self) = @_;
122 135   50     558 return $self->{stack}[-1]{opt} || {};
123             }
124              
125             sub _append_result {
126 732     732   1324 my ($self, $text) = @_;
127 732         1274 push @{$self->{result}}, $text;
  732         3137  
128             }
129              
130             sub _push_state {
131 240     240   540 my ($self, $state_key, $opt) = @_;
132 240         558 my $state = $states->{$state_key};
133 240 100       809 $self->_append_result($state->{js_open}) if $state->{js_open} ne '';
134 240         679 my $entry = {state => $state, opt => $opt};
135 240         415 push @{$self->{stack}}, $entry;
  240         872  
136             }
137              
138             sub _pop_state {
139 157     157   318 my ($self) = @_;
140 157         239 my $entry = pop @{$self->{stack}};
  157         330  
141 157         274 my $state = $entry->{state};
142 157 100       579 $self->_append_result($state->{js_close}) if $state->{js_close} ne '';
143             }
144              
145             sub _in_init {
146 131     131   324 my ($self, $token, $opt) = @_;
147            
148 131 100       440 if ($token eq '<%') {
    100          
    50          
149 31 50       89 if (defined $opt->{left}) {
150 31         67 $opt->{ltrim} = {};
151            
152 31 100       85 if ($opt->{left} ne '') {
153 16         53 $self->_append_result($opt->{left});
154 16         25 $opt->{ltrim}{index} = $#{$self->{result}};
  16         46  
155             }
156             }
157            
158 31         70 $self->_push_state($token, $opt);
159             } elsif ($token eq "<%=") {
160 40 100 66     265 if (defined $opt->{left} && $opt->{left} ne '') {
161 8         23 $self->_push_state('TEXT');
162 8         26 $self->_append_result($opt->{left});
163 8         20 $self->_pop_state();
164             }
165            
166 40         136 $self->_push_state($token, $opt);
167            
168 40 100       510 if (my $escape = $opt->{escape}) {
169 1         5 $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         175 $token =~ s/([\\"])/\\$1/g;
178 60         162 $self->_push_state('TEXT');
179 60         138 $self->_append_result($token);
180             }
181             }
182              
183             sub _in_text {
184 83     83   167 my ($self, $token, $opt) = @_;
185            
186 83 100       273 if ($token eq '<%') {
    100          
    100          
187 3         11 $self->_pop_state();
188 3         8 $self->_push_state($token, $opt);
189             } elsif ($token eq '<%=') {
190 15         47 $self->_pop_state();
191 15         60 $self->_push_state($token, $opt);
192            
193 15 100       111 if (my $escape = $opt->{escape}) {
194 4         15 $self->_append_result(qq{EJS.$escape(});
195             }
196             } elsif ($token eq "\n") {
197 40         104 $self->_append_result("\\n");
198 40         125 $self->_pop_state();
199 40         86 $self->_append_result("\n");
200             } else {
201 25         85 $token =~ s/([\\"])/\\$1/g;
202 25         56 $self->_append_result($token);
203             }
204             }
205              
206             sub _in_script {
207 336     336   607 my ($self, $token, $opt) = @_;
208            
209 336 100       657 if ($token eq '%>') {
210 81 100       231 if (my $ltrim = $self->_top_opt->{ltrim}) {
211 27 100       70 if (defined $opt->{right}) {
212 25 100       68 if ($opt->{right} ne '') {
213 15         32 $self->_append_result($opt->{right});
214             }
215             } else {
216 2 50       7 if (defined(my $idx = $ltrim->{index})) {
217 2         5 my $left = $self->{result}[$idx];
218 2         10 $self->{result}[$idx] = qq{print("$left");};
219             }
220             }
221            
222 27         59 $self->_pop_state();
223             } else {
224 54 100       154 if ($self->_top_opt->{escape}) {
225 5         10 $self->_append_result(qq{)});
226             }
227            
228 54         201 $self->_pop_state();
229            
230 54         127 my $right = $opt->{right};
231            
232 54 100 100     488 if (defined $right && $right ne '') {
233 17 100       86 if ($right =~ s/\n$//) {
234 15         62 $self->_append_result(qq{print("$right\\n");\n});
235             } else {
236 2         8 $self->_append_result(qq{print("$right");});
237             }
238             }
239             }
240             } else {
241 255         511 $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;