File Coverage

blib/lib/Text/Clevery/Parser.pm
Criterion Covered Total %
statement 122 126 96.8
branch 15 20 75.0
condition 6 6 100.0
subroutine 14 15 93.3
pod 0 9 0.0
total 157 176 89.2


line stmt bran cond sub pod time code
1             package Text::Clevery::Parser;
2 13     13   57513 use Any::Moose;
  13         31  
  13         114  
3             extends 'Text::Xslate::Parser';
4              
5 13     13   8328 use Text::Xslate::Util qw(p any_in);
  13         25  
  13         32819  
6              
7             my $SIMPLE_IDENT = qr/(?: [a-zA-Z_][a-zA-Z0-9_]* )/xms;
8              
9             sub _build_identity_pattern {
10 14     14   275859 return qr{ (?: [/\$]? $SIMPLE_IDENT ) }xmso;
11             }
12              
13 14     14   472 sub _build_line_start { undef }
14              
15             # preprocess code sections
16             around trim_code => sub {
17             my($super, $parser, $code) = @_;
18              
19             # comment {* ... *}
20             if($code =~ /\A \* .* \* \z/xms) {
21             return '';
22             }
23              
24             # config variable
25             $code =~ s{ \# \s* (\S+) \s* \# }
26             { '$clevery.config.' . $1 }xmsgeo;
27              
28             return $parser->$super($code);
29             };
30              
31             around split => sub {
32             my($super, $parser, @args) = @_;
33              
34              
35             my $tokens_ref = $parser->$super(@args);
36             for(my $i = 0; $i < @{$tokens_ref}; $i++) {
37             my $t = $tokens_ref->[$i];
38             # process {literal} ... {/literal}
39             if($t->[0] eq 'code' && $t->[1] =~ m{\A \s* literal \s* \z}xms) {
40             my $text = '';
41              
42             for(my $j = $i + 1; $j < @{$tokens_ref}; $j++) {
43             my $u = $tokens_ref->[$j];
44             if($u->[0] eq 'code' && $u->[1] =~ m{\A \s* /literal \s* \z}xms) {
45             splice @{$tokens_ref}, $i+1, $j - $i;
46             last;
47             }
48             elsif( $u->[0] eq 'code' ) {
49             $text .= $parser->tag_start . $u->[1];
50              
51             my $n = $tokens_ref->[$j+1];
52             if($n && $n->[0] eq 'postchomp') {
53             $text .= $n->[1];
54             $j++;
55             }
56             $text .= $parser->tag_end;
57             }
58             else {
59             $text .= $u->[1];
60             }
61             }
62             $t->[0] = 'text';
63             $t->[1] = $text;
64             }
65             }
66             return $tokens_ref;
67             };
68              
69             sub init_symbols {
70 14     14 0 13486 my($parser) = @_;
71              
72 14         131 $parser->init_basic_operators();
73              
74             # special symbols
75 14         70824 $parser->symbol('`')->set_nud(\&nud_backquote);
76 14         407 $parser->symbol('(name)')->set_std(\&std_name);
77              
78             # operators
79 14         833 $parser->symbol('|') ->set_led(\&led_pipe); # reset
80 14         177 $parser->symbol('.') ->set_led(\&led_dot); # reset
81 14         288 $parser->make_alias('.' => '->');
82              
83             # special variables
84 14         602 $parser->symbol('$clevery') ->set_nud(\&nud_clevery_context);
85 14         405 $parser->symbol('$smarty')->set_nud(\&nud_clevery_context);
86              
87 14         484 $parser->define_literal(ldelim => $parser->tag_start);
88 14         1083 $parser->define_literal(rdelim => $parser->tag_end);
89              
90             # statement tokens
91 14         1028 $parser->symbol('if') ->set_std(\&std_if);
92 14         318 $parser->symbol('elseif')->is_block_end(1);
93 14         302 $parser->symbol('else') ->is_block_end(1);
94 14         311 $parser->symbol('/if') ->is_block_end(1);
95              
96 14         291 $parser->symbol('foreach') ->set_std(\&std_foreach);
97 14         321 $parser->symbol('foreachelse')->is_block_end(1);
98 14         313 $parser->symbol('/foreach') ->is_block_end(1);
99              
100 14         338 $parser->symbol('include')->set_std(\&std_include);
101              
102 14         317 return;
103             }
104              
105             sub nud_backquote { # the same as parens
106 1     1 0 7 my($parser, $symbol) = @_;
107 1         5 my $expr = $parser->expression(0);
108 1         3 $parser->advance('`');
109 1         3 return $expr;
110             }
111              
112             sub nud_clevery_context {
113 31     31 0 6108 my($parser, $symbol) = @_;
114 31         81 return $parser->call('@clevery_context');
115             }
116              
117             around nud_literal => sub {
118             my($super, $parser, $symbol) = @_;
119              
120             my $value = $symbol->value;
121             if(defined($value) and !Scalar::Util::looks_like_number($value)) {
122             # XXX: string literals in Clevery are "raw" string
123             return $parser->call('mark_raw', $parser->$super($symbol));
124             }
125              
126             return $parser->$super($symbol);
127             };
128              
129             around led_dot => sub {
130             my($super, $parser, $symbol, $left) = @_;
131              
132             # special case: foo.$field
133             if($parser->token->id =~ /\A \$/xms) {
134             return $symbol->clone(
135             arity => "field",
136             first => $left,
137             second => $parser->expression( $symbol->lbp ),
138             );
139             }
140              
141             return $parser->$super($symbol, $left);
142             };
143              
144             # variable modifiers
145             # expr | modifier : param1 : param2 ...
146             around led_pipe => sub {
147             my($super, $parser, $symbol, $left) = @_;
148              
149             my $bar = $parser->$super($symbol, $left);
150              
151             my @args;
152             while($parser->token->id eq ':') {
153             $parser->advance();
154             my $modifier = $parser->expression(0);
155             push @args, $modifier;
156             }
157             push @{$bar->second}, @args;
158             return $bar;
159             };
160              
161             sub attr_list {
162 79     79 0 139 my($parser) = @_;
163 79         110 my @args;
164 79         107 while(1) {
165 286         5549 my $key = $parser->token;
166 286 100 100     1408 if(!($key->arity eq "name"
167             and $parser->next_token_is('='))) {
168 79         170 last;
169             }
170 207         2026 $parser->advance();
171 207         12329 $parser->advance("=");
172              
173 207         29054 my $value;
174 207         525 my $t = $parser->token;
175 207 100 100     1078 if($t->arity eq "name" && !$t->is_defined) {
176 29         92 $value = $t->clone(arity => 'literal');
177 29         624 $parser->advance();
178             }
179             else {
180 178         609 $value = $parser->expression(0);
181             }
182              
183 207         17795 push @args, $key->clone(arity => 'literal') => $value;
184             }
185              
186 79         560 return @args;
187             }
188              
189             sub std_name { # simple names are assumed as commands
190 58     58 0 20592 my($parser, $symbol) = @_;
191              
192 58         227 my @args = $parser->attr_list();
193 58         257 return $parser->print( $parser->call($symbol, @args) );
194             }
195              
196             sub define_function {
197 14     14 0 644 my($parser, @names) = @_;
198              
199 14         46 foreach my $name(@names) {
200 980         5019 my $s = $parser->symbol($name);
201 980         20394 $s->set_std(\&std_name);
202             }
203 14         137 return;
204             }
205              
206              
207             sub std_if {
208 8     8 0 749 my($parser, $symbol) = @_;
209              
210 8         32 my $if = $symbol->clone(arity => 'if');
211              
212 8         193 $if->first( $parser->expression(0) );
213 8         408 $if->second( $parser->statements() );
214              
215 8         594 my $t = $parser->token;
216              
217 8         12 my $top_if = $if;
218              
219 8         43 while($t->id eq 'elseif') {
220 1         4 $parser->reserve($t);
221 1         4 $parser->advance();
222              
223 1         4 my $elsif = $t->clone(arity => "if");
224 1         24 $elsif->first( $parser->expression(0) );
225 1         34 $elsif->second( $parser->statements() );
226 1         5 $if->third([$elsif]);
227 1         3 $if = $elsif;
228 1         8 $t = $parser->token;
229             }
230              
231 8 100       35 if($t->id eq 'else') {
232 4         12 $parser->reserve($t);
233 4         27 $parser->advance();
234              
235 4         74 $if->third( $parser->statements() );
236             }
237              
238 8         238 $parser->advance('/if');
239              
240 8         263 return $top_if;
241             }
242              
243             sub std_foreach {
244 15     15 0 1295 my($parser, $symbol) = @_;
245              
246 15         52 my $for = $symbol->clone( arity => 'for' );
247              
248 15         352 my %args = $parser->attr_list();
249              
250 15 50       293 my $from = $args{from} or $parser->_error("You must specify 'from' attribute for {foreach}");
251 15 50       140 my $item = $args{item} or $parser->_error("You must specify 'item' attribute for {foreach}");
252 15         26 my $key = $args{key};
253 15         25 my $name = $args{name};
254              
255 15         75 $item->id( '$' . $item->id );
256 15         41 $item->arity('variable');
257              
258 15         41 $for->first($from);
259 15         59 $for->second([$item]);
260              
261 15         81 $parser->new_scope();
262 15         165 my $iterator = $parser->define_iterator($item);
263 15         1423 my $body = $parser->statements();
264 15         532 $parser->pop_scope();
265              
266             # set_foreach_property(name, $~iter.index, $~iter.body)
267 15 100       172 if($name) {
268 9         11 unshift @{$body}, $parser->call(
  9         45  
269             '@clevery_set_foreach_property',
270             $name,
271             $iterator,
272             $parser->iterator_body($iterator),
273             );
274             }
275 15         846 $for->third($body);
276              
277 15 100       79 if($parser->token->id eq 'foreachelse') {
278 3         11 $parser->advance();
279              
280             # if array_is_not_empty(my $array = expr) {
281             # foreach $array -> ...
282             # }
283             # else {
284             # foreachelse ...
285             # }
286              
287 3         12 my $else = $parser->statements();
288              
289 3         15 my $tmpname = $parser->symbol('($foreach)')->clone(arity => 'name');
290 3         137 my $tmpinit = $symbol->clone(
291             arity => 'constant',
292             first => $tmpname,
293             second => $from,
294             );
295 3         77 $for->first($tmpname);
296              
297 3         11 my $array_is_not_empty = $parser->call(
298             '@clevery_array_is_not_empty', $tmpinit);
299              
300 3         200 my $if = $symbol->clone(
301             arity => 'if',
302             first => $array_is_not_empty,
303             second => [$for],
304             third => $else,
305             );
306              
307 3         68 $for = $if;
308             }
309              
310 15         50 $parser->advance('/foreach');
311              
312 15 50       188 if(defined $key) {
313 0         0 $for = $parser->_not_implemented($symbol,
314             "'key' attribute for {$symbol}");
315             }
316              
317 15         166 return $for;
318             }
319              
320             sub std_include {
321 6     6 0 3353 my($parser, $symbol) = @_;
322              
323 6         18 my @args = $parser->attr_list();
324              
325 6         11 my $file;
326 6         16 for(my $i = 0; $i < @args; $i += 2) {
327 6         17 my $key = $args[$i]->id;
328              
329 6 50       29 if($key eq 'assign') {
    50          
330 0         0 return $parser->_not_implemented($symbol, "'assign' attribute for {$symbol}");
331             }
332             elsif($key eq 'file') {
333 6         10 $file = $args[$i+1];
334 6         39 splice @args, $i, 2; # delete
335             }
336             }
337              
338 6         22 return $symbol->clone(
339             arity => 'include',
340             first => $file,
341             second => \@args,
342             );
343             }
344              
345             sub _not_implemented {
346 0     0     my($self, $proto, $name) = @_;
347 0           return $self->call('@clevery_not_implemented',
348             $proto->clone(arity => 'literal', value => $name));
349             }
350              
351 13     13   106 no Any::Moose;
  13         27  
  13         174  
352             1;
353             __END__