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   26 use strict;
  8         8  
  8         177  
2 8     8   21 use warnings;
  8         6  
  8         215  
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   21 use base 'EJS::Template::Base';
  8         7  
  8         417  
12              
13 8     8   28 use EJS::Template::Runtime;
  8         19  
  8         7881  
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 82     82 1 111 my ($class, $config) = @_;
44 82         187 my $self = $class->SUPER::new($config);
45            
46 82         159 $self->{stack} = [];
47 82         139 $self->{result} = [];
48            
49 82         79 $self->{default_escape} = do {
50 82   100     165 my $name = $self->config('escape') || '';
51            
52 82 100 100     225 if ($name eq '' || $name eq 'raw') {
53 77         153 '';
54             } else {
55 5 50       18 $EJS::Template::Runtime::ESCAPES{$name} || '';
56             }
57             };
58            
59 82         210 $self->_push_state('INIT');
60 82         146 return $self;
61             }
62              
63             =head2 read_line
64              
65             Parses a line.
66              
67             =cut
68              
69             sub read_line {
70 142     142 1 161 my ($self, $line) = @_;
71            
72 142         944 while ($line =~ m{(.*?)((^\s*)?<%(?:(?:\s*:\s*\w+\s*)?=)?|%>(\s*?(?:\n|$))?|\\?["']|/\*|\*/|//|\n|$)}g) {
73 463         939 my ($text, $mark, $left, $right) = ($1, $2, $3, $4);
74 463         348 my $escape;
75            
76 463 100       980 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 44         70 $escape = $self->{default_escape};
81             }
82            
83 463         439 $mark =~ s/\s+(<%=?)/$1/;
84 463         392 $mark =~ s/(%>)\s+/$1/;
85            
86 463         843 my $opt = {
87             left => $left ,
88             right => $right ,
89             escape => $escape,
90             };
91            
92 463 100       837 $self->_handle_token($text) if $text ne '';
93 463 100       1699 $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 82     82 1 83 my ($self) = @_;
105 82         99 $self->_pop_state() until @{$self->{stack}} <= 1;
  92         229  
106 82         355 return $self->{result};
107             }
108              
109             sub _handle_token {
110 543     543   527 my ($self, $token, $opt) = @_;
111 543         573 my $method = $self->{stack}[-1]{state}{method};
112 543         823 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 133     133   112 my ($self) = @_;
122 133   50     431 return $self->{stack}[-1]{opt} || {};
123             }
124              
125             sub _append_result {
126 725     725   625 my ($self, $text) = @_;
127 725         453 push @{$self->{result}}, $text;
  725         2095  
128             }
129              
130             sub _push_state {
131 238     238   328 my ($self, $state_key, $opt) = @_;
132 238         267 my $state = $states->{$state_key};
133 238 100       548 $self->_append_result($state->{js_open}) if $state->{js_open} ne '';
134 238         425 my $entry = {state => $state, opt => $opt};
135 238         198 push @{$self->{stack}}, $entry;
  238         608  
136             }
137              
138             sub _pop_state {
139 156     156   147 my ($self) = @_;
140 156         106 my $entry = pop @{$self->{stack}};
  156         210  
141 156         155 my $state = $entry->{state};
142 156 100       470 $self->_append_result($state->{js_close}) if $state->{js_close} ne '';
143             }
144              
145             sub _in_init {
146 130     130   140 my ($self, $token, $opt) = @_;
147            
148 130 100       332 if ($token eq '<%') {
    100          
    50          
149 31 50       79 if (defined $opt->{left}) {
150 31         44 $opt->{ltrim} = {};
151            
152 31 100       71 if ($opt->{left} ne '') {
153 16         24 $self->_append_result($opt->{left});
154 16         25 $opt->{ltrim}{index} = $#{$self->{result}};
  16         33  
155             }
156             }
157            
158 31         47 $self->_push_state($token, $opt);
159             } elsif ($token eq "<%=") {
160 39 100 66     196 if (defined $opt->{left} && $opt->{left} ne '') {
161 8         14 $self->_push_state('TEXT');
162 8         14 $self->_append_result($opt->{left});
163 8         13 $self->_pop_state();
164             }
165            
166 39         109 $self->_push_state($token, $opt);
167            
168 39 100       366 if (my $escape = $opt->{escape}) {
169 1         4 $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         84 $token =~ s/([\\"])/\\$1/g;
178 60         82 $self->_push_state('TEXT');
179 60         79 $self->_append_result($token);
180             }
181             }
182              
183             sub _in_text {
184 83     83   80 my ($self, $token, $opt) = @_;
185            
186 83 100       200 if ($token eq '<%') {
    100          
    100          
187 3         7 $self->_pop_state();
188 3         6 $self->_push_state($token, $opt);
189             } elsif ($token eq '<%=') {
190 15         39 $self->_pop_state();
191 15         24 $self->_push_state($token, $opt);
192            
193 15 100       86 if (my $escape = $opt->{escape}) {
194 4         10 $self->_append_result(qq{EJS.$escape(});
195             }
196             } elsif ($token eq "\n") {
197 40         56 $self->_append_result("\\n");
198 40         62 $self->_pop_state();
199 40         55 $self->_append_result("\n");
200             } else {
201 25         56 $token =~ s/([\\"])/\\$1/g;
202 25         27 $self->_append_result($token);
203             }
204             }
205              
206             sub _in_script {
207 330     330   305 my ($self, $token, $opt) = @_;
208            
209 330 100       424 if ($token eq '%>') {
210 80 100       145 if (my $ltrim = $self->_top_opt->{ltrim}) {
211 27 100       47 if (defined $opt->{right}) {
212 25 100       55 if ($opt->{right} ne '') {
213 15         25 $self->_append_result($opt->{right});
214             }
215             } else {
216 2 50       5 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         45 $self->_pop_state();
223             } else {
224 53 100       85 if ($self->_top_opt->{escape}) {
225 5         8 $self->_append_result(qq{)});
226             }
227            
228 53         90 $self->_pop_state();
229            
230 53         68 my $right = $opt->{right};
231            
232 53 100 100     431 if (defined $right && $right ne '') {
233 17 100       67 if ($right =~ s/\n$//) {
234 15         39 $self->_append_result(qq{print("$right\\n");\n});
235             } else {
236 2         6 $self->_append_result(qq{print("$right");});
237             }
238             }
239             }
240             } else {
241 250         268 $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;