File Coverage

blib/lib/Template/Alloy/Parse.pm
Criterion Covered Total %
statement 483 547 88.3
branch 282 348 81.0
condition 133 190 70.0
subroutine 38 45 84.4
pod 5 38 13.1
total 941 1168 80.5


line stmt bran cond sub pod time code
1             package Template::Alloy::Parse;
2              
3             =head1 NAME
4              
5             Template::Alloy::Parse - Common parsing role for creating AST from templates
6              
7             =cut
8              
9 10     10   77 use strict;
  10         21  
  10         352  
10 10     10   55 use warnings;
  10         20  
  10         361  
11 10     10   58 use base qw(Exporter);
  10         91  
  10         1328  
12 10     10   76 use Template::Alloy;
  10         25  
  10         97  
13 10         103499 use Template::Alloy::Operator qw($QR_OP $QR_OP_ASSIGN $QR_OP_PREFIX
14 10     10   66 $OP $OP_ASSIGN $OP_PREFIX $OP_POSTFIX);
  10         21  
15              
16             our $VERSION = $Template::Alloy::VERSION;
17             our @EXPORT_OK = qw(define_directive define_syntax
18             $ALIASES $DIRECTIVES $TAGS $QR_DIRECTIVE $QR_COMMENTS);
19              
20 0     0 0 0 sub new { die "This class is a role for use by packages such as Template::Alloy" }
21              
22             ###----------------------------------------------------------------###
23              
24             our $TAGS = {
25             asp => ['<%', '%>' ], # ASP
26             default => ['\[%', '%\]' ], # default
27             html => ['' ], # HTML comments
28             mason => ['<%', '>' ], # HTML::Mason
29             metatext => ['%%', '%%' ], # Text::MetaText
30             php => ['<\?', '\?>' ], # PHP
31             star => ['\[\*', '\*\]' ], # TT alternate
32             template => ['\[%', '%\]' ], # Normal Template Toolkit
33             template1 => ['[\[%]%', '%[%\]]'], # TT1
34             tt2 => ['\[%', '%\]' ], # TT2
35             };
36              
37             our $SYNTAX = {
38             alloy => sub { shift->parse_tree_tt3(@_) },
39             js => sub { shift->parse_tree_js(@_) },
40             jsr => sub { shift->parse_tree_jsr(@_) },
41             ht => sub { my $self = shift; local $self->{'V2EQUALS'} = 0; local $self->{'EXPR'} = 0; $self->parse_tree_hte(@_) },
42             hte => sub { my $self = shift; local $self->{'V2EQUALS'} = 0; $self->parse_tree_hte(@_) },
43             tt3 => sub { shift->parse_tree_tt3(@_) },
44             tt2 => sub { my $self = shift; local $self->{'V2PIPE'} = 1; $self->parse_tree_tt3(@_) },
45             tt1 => sub { my $self = shift; local $self->{'V2PIPE'} = 1; local $self->{'V1DOLLAR'} = 1; $self->parse_tree_tt3(@_) },
46             tmpl => sub { shift->parse_tree_tmpl(@_) },
47             velocity => sub { shift->parse_tree_velocity(@_) },
48             };
49              
50             our $DIRECTIVES = {
51             #name parse_sub play_sub block postdir continue no_interp
52             BLOCK => [\&parse_BLOCK, \&play_BLOCK, 1],
53             BREAK => [sub {}, \&play_control],
54             CALL => [\&parse_CALL, \&play_CALL],
55             CASE => [\&parse_CASE, undef, 0, 0, {SWITCH => 1, CASE => 1}],
56             CATCH => [\&parse_CATCH, undef, 0, 0, {TRY => 1, CATCH => 1}],
57             CLEAR => [sub {}, \&play_CLEAR],
58             '#' => [sub {}, sub {}],
59             COMMENT => [sub {}, sub {}, 1],
60             CONFIG => [\&parse_CONFIG, \&play_CONFIG],
61             DEBUG => [\&parse_DEBUG, \&play_DEBUG],
62             DEFAULT => [\&parse_DEFAULT, \&play_DEFAULT],
63             DUMP => [\&parse_DUMP, \&play_DUMP],
64             ELSE => [sub {}, undef, 0, 0, {IF => 1, ELSIF => 1, UNLESS => 1}],
65             ELSIF => [\&parse_IF, undef, 0, 0, {IF => 1, ELSIF => 1, UNLESS => 1}],
66             END => [sub {}, sub {}],
67             EVAL => [\&parse_EVAL, \&play_EVAL],
68             FILTER => [\&parse_FILTER, \&play_FILTER, 1, 1],
69             '|' => [\&parse_FILTER, \&play_FILTER, 1, 1],
70             FINAL => [sub {}, undef, 0, 0, {TRY => 1, CATCH => 1}],
71             FOR => [\&parse_FOR, \&play_FOR, 1, 1],
72             FOREACH => [\&parse_FOR, \&play_FOR, 1, 1],
73             GET => [\&parse_GET, \&play_GET],
74             IF => [\&parse_IF, \&play_IF, 1, 1],
75             INCLUDE => [\&parse_INCLUDE, \&play_INCLUDE],
76             INSERT => [\&parse_INSERT, \&play_INSERT],
77             JS => [sub {}, \&play_JS, 1, 0, 0, 1],
78             LAST => [sub {}, \&play_control],
79             LOOP => [\&parse_LOOP, \&play_LOOP, 1, 1],
80             MACRO => [\&parse_MACRO, \&play_MACRO],
81             META => [\&parse_META, \&play_META],
82             NEXT => [sub {}, \&play_control],
83             PERL => [sub {}, \&play_PERL, 1, 0, 0, 1],
84             PROCESS => [\&parse_PROCESS, \&play_PROCESS],
85             RAWPERL => [sub {}, \&play_RAWPERL, 1, 0, 0, 1],
86             RETURN => [\&parse_RETURN, \&play_control],
87             SET => [\&parse_SET, \&play_SET],
88             STOP => [sub {}, \&play_control],
89             SWITCH => [\&parse_SWITCH, \&play_SWITCH, 1],
90             TAGS => [\&parse_TAGS, sub {}],
91             THROW => [\&parse_THROW, \&play_THROW],
92             TRY => [sub {}, \&play_TRY, 1],
93             UNLESS => [\&parse_UNLESS, \&play_UNLESS, 1, 1],
94             USE => [\&parse_USE, \&play_USE],
95             VIEW => [\&parse_VIEW, \&play_VIEW, 1],
96             WHILE => [\&parse_WHILE, \&play_WHILE, 1, 1],
97             WRAPPER => [\&parse_WRAPPER, \&play_WRAPPER, 1, 1],
98             #name parse_sub play_sub block postdir continue no_interp
99             };
100             our $ALIASES = {
101             EVALUATE => 'EVAL',
102             };
103              
104              
105             our $QR_DIRECTIVE = '( [a-zA-Z]+\b | \| )';
106             our $QR_COMMENTS = '(?-s: \# .* \s*)*';
107             our $QR_FILENAME = '([a-zA-Z]]:/|/)? [\w\.][\w\-\.]* (?:/[\w\-\.]+)*';
108             our $QR_BLOCK = '\w+\b (?: :\w+\b)* )';
109             our $QR_NUM = '(?:\d*\.\d+ | \d+) (?: [eE][+-]?\d+ )?';
110             our $QR_AQ_SPACE = '(?: \\s+ | \$ | (?=;) )';
111              
112             our $_escapes = {n => "\n", r => "\r", t => "\t", '"' => '"', '\\' => '\\', '$' => '$'};
113             our $QR_ESCAPES = qr{[nrt\"\$\\]};
114              
115             sub define_directive {
116 0     0 0 0 my ($self, $name, $args) = @_;
117 0         0 $DIRECTIVES->{$name} = [@{ $args }{qw(parse_sub play_sub is_block is_postop continues no_interp)}];
  0         0  
118 0         0 return 1;
119             }
120              
121             sub define_syntax {
122 0     0 0 0 my ($self, $name, $sub) = @_;
123 0         0 $SYNTAX->{$name} = $sub;
124 0         0 return 1;
125             }
126              
127             ###----------------------------------------------------------------###
128              
129             sub parse_tree {
130 4852   100 4852 1 18149 my $syntax = $_[0]->{'SYNTAX'} || 'alloy';
131 4852   66     14023 my $meth = $SYNTAX->{$syntax} || $_[0]->throw('config', "Unknown SYNTAX \"$syntax\"");
132 4849         11435 return $meth->(@_);
133             }
134              
135             ###----------------------------------------------------------------###
136              
137             sub parse_expr {
138 17654     17654 1 29935 my $self = shift;
139 17654         25058 my $str_ref = shift;
140 17654   100     51072 my $ARGS = shift || {};
141 17654 100       38884 my $is_aq = $ARGS->{'auto_quote'} ? 1 : 0;
142 17654         26944 my $mark = pos $$str_ref;
143              
144             ### allow for custom auto_quoting (such as hash constructors)
145 17654 100       34178 if ($is_aq) {
146 2183 100       82450 if ($$str_ref =~ m{ \G \s* $QR_COMMENTS $ARGS->{'auto_quote'} }gcx) {
    100          
    100          
147 1485         9283 return $1;
148              
149             ### allow for ${foo.bar} type constructs
150             } elsif ($$str_ref =~ m{ \G \$\{ }gcx) {
151 12         45 my $var = $self->parse_expr($str_ref);
152 12 50       176 $$str_ref =~ m{ \G \s* $QR_COMMENTS \} }gcxo
153             || $self->throw('parse', 'Missing close "}" from "${"', undef, pos($$str_ref));
154 12         50 return $var;
155              
156             ### allow for auto-quoted $foo
157             } elsif ($$str_ref =~ m{ \G \$ }gcx) {
158 45   33     222 return $self->parse_expr($str_ref)
159             || $self->throw('parse', "Missing variable", undef, pos($$str_ref));
160             }
161             }
162              
163 16112         48122 $$str_ref =~ m{ \G \s* $QR_COMMENTS }gcxo;
164              
165             ### allow for macro definer
166 16112 100       39491 if ($$str_ref =~ m{ \G -> \s* }gcxo) { # longest token would be nice - until then this comes before prefix
167 27         87 local $self->{'_operator_precedence'} = 0; # reset presedence
168 27         48 my $args;
169 27 100       92 if ($$str_ref =~ m{ \G \( \s* }gcx) {
170 18         92 $args = $self->parse_args($str_ref, {positional_only => 1});
171 18 50       101 $$str_ref =~ m{ \G \) \s* }gcx || $self->throw('parse.missing', "Missing close ')'", undef, pos($$str_ref));
172             }
173 27 50       569 $$str_ref =~ m{ \G \{ $QR_COMMENTS }gcx || $self->throw('parse.missing', "Missing open '{'", undef, pos($$str_ref));
174 27         159 local $self->{'END_TAG'} = qr{ \} }x;
175 27         152 my $tree = $self->parse_tree_tt3($str_ref, 'one_tag_only');
176 27   100     244 return [[undef, '->', $args || [['this',0]], $tree]];
177             }
178              
179             ### test for leading prefix operators
180 16085         23027 my $has_prefix;
181 16085   100     69071 while (! $is_aq && $$str_ref =~ m{ \G ($QR_OP_PREFIX) }gcxo) {
182 102         222 push @{ $has_prefix }, $1;
  102         358  
183 102         697 $$str_ref =~ m{ \G \s* $QR_COMMENTS }gcxo;
184             }
185              
186 16085         48049 my @var;
187             my $is_literal;
188 16085         0 my $is_namespace;
189 16085         0 my $already_parsed_args;
190              
191             ### allow hex
192 16085 50 100     166625 if ($$str_ref =~ m{ \G 0x ( [a-fA-F0-9]+ ) }gcx) {
    100 100        
    100 100        
    100 100        
    100 100        
    100          
    100          
    100          
    100          
    100          
    100          
    100          
193 0   0     0 my $number = eval { hex $1 } || 0;
194 0         0 push @var, \ $number;
195 0         0 $is_literal = 1;
196              
197             ### allow for numbers
198             } elsif ($$str_ref =~ m{ \G ( $QR_NUM ) }gcx) {
199 2845         9636 my $number = 0 + $1;
200 2845         6430 push @var, \ $number;
201 2845         5563 $is_literal = 1;
202              
203             ### allow for quoted array constructor
204             } elsif (! $is_aq && $$str_ref =~ m{ \G qw ([^\w\s]) \s* }gcxo) {
205 18         56 my $quote = $1;
206 18         55 $quote =~ y|([{<|)]}>|;
207 18 50       554 $$str_ref =~ m{ \G (.*?) (?
208             || $self->throw('parse.missing.array_close', "Missing close \"$quote\"", undef, pos($$str_ref));
209 18         65 my $str = $1;
210 18         53 $str =~ s{ ^ \s+ }{}x;
211 18         72 $str =~ s{ \s+ $ }{}x;
212 18         215 $str =~ s{ \\ \Q$quote\E }{$quote}gx;
213 18         151 push @var, [undef, '[]', split /\s+/, $str];
214              
215             ### looks like a normal variable start
216             } elsif ($$str_ref =~ m{ \G (\w+) }gcx) {
217 6978         21334 push @var, $1;
218 6978 100 100     16550 $is_namespace = 1 if $self->{'NAMESPACE'} && $self->{'NAMESPACE'}->{$1};
219              
220             ### allow for regex constructor
221             } elsif (! $is_aq && $$str_ref =~ m{ \G / }gcx) {
222 47 100       321 $$str_ref =~ m{ \G (.*?) (?
223             || $self->throw('parse', 'Unclosed regex tag "/"', undef, pos($$str_ref));
224 39         143 my ($str, $opts) = ($1, $2);
225 39 100       115 $self->throw('parse', 'e option not allowed on regex', undef, pos($$str_ref)) if $opts =~ /e/;
226 36 100       115 $self->throw('parse', 'g option not supported on regex', undef, pos($$str_ref)) if $opts =~ /g/;
227 33         68 $str =~ s|\\n|\n|g;
228 33         65 $str =~ s|\\t|\t|g;
229 33         54 $str =~ s|\\r|\r|g;
230 33         62 $str =~ s|\\\/|\/|g;
231 33         49 $str =~ s|\\\$|\$|g;
232 33 100       60 $self->throw('parse', "Invalid regex: $@", undef, pos($$str_ref)) if ! eval { "" =~ /$str/; 1 };
  33         539  
  27         102  
233 27         101 push @var, [undef, 'qr', $str, $opts];
234              
235             ### allow for single quoted strings
236             } elsif ($$str_ref =~ m{ \G \' (.*?) (?
237 1400         4129 my $str = $1;
238 1400         2890 $str =~ s{ \\\' }{\'}xg;
239 1400 100       3041 return $str if $is_aq;
240 1375         2913 push @var, \ $str;
241 1375         3037 $is_literal = 1;
242              
243             ### allow for double quoted strings
244             } elsif ($$str_ref =~ m{ \G \" }gcx) {
245 724         1442 my @pieces;
246 724         2773 while ($$str_ref =~ m{ \G (.*?) ([\"\$\\]) }gcxs) {
247 864         2914 my ($str, $item) = ($1, $2);
248 864 100       2342 if (length $str) {
249 735 100 100     2080 if (defined($pieces[-1]) && ! ref($pieces[-1])) { $pieces[-1] .= $str; } else { push @pieces, $str }
  40         115  
  695         1776  
250             }
251 864 100       2457 if ($item eq '\\') {
    100          
    100          
252 21 50       139 my $chr = ($$str_ref =~ m{ \G ($QR_ESCAPES) }gcxo) ? $_escapes->{$1} : '\\';
253 21 50 33     95 if (defined($pieces[-1]) && ! ref($pieces[-1])) { $pieces[-1] .= $chr; } else { push @pieces, $chr }
  21         46  
  0         0  
254 21         83 next;
255             } elsif ($item eq '"') {
256 721         1281 last;
257             } elsif ($self->{'AUTO_EVAL'}) {
258 16 50 33     66 if (defined($pieces[-1]) && ! ref($pieces[-1])) { $pieces[-1] .= '$'; } else { push @pieces, '$' }
  16         38  
  0         0  
259 16         62 next;
260             }
261              
262 106         266 my $not = $$str_ref =~ m{ \G ! }gcx;
263 106         194 my $mark = pos($$str_ref);
264 106         192 my $ref;
265 106 100       291 if ($$str_ref =~ m{ \G \{ }gcx) {
266 42         129 local $self->{'_operator_precedence'} = 0; # allow operators
267 42         120 $ref = $self->parse_expr($str_ref);
268 42 50       361 $$str_ref =~ m{ \G \s* $QR_COMMENTS \} }gcxo
269             || $self->throw('parse', 'Missing close }', undef, pos($$str_ref));
270             } else {
271 64         259 local $self->{'_operator_precedence'} = 1; # no operators
272 64   33     208 $ref = $self->parse_expr($str_ref)
273             || $self->throw('parse', "Error while parsing for interpolated string", undef, pos($$str_ref));
274             }
275 106 100 100     546 if (! $not && $self->{'SHOW_UNDEFINED_INTERP'}) {
276 8         43 $ref = [[undef, '//', $ref, '$'.substr($$str_ref, $mark, pos($$str_ref)-$mark)], 0];
277             }
278 106 50       667 push @pieces, $ref if defined $ref;
279             }
280 724 100 100     4298 if (! @pieces) { # [% "" %]
    100          
    100          
    100          
281 3 50       10 return '' if $is_aq;
282 3         9 push @var, \ '';
283 3         6 $is_literal = 1;
284             } elsif (@pieces == 1 && ref $pieces[0]) { # [% "$foo" %] or [% "${ 1 + 2 }" %]
285 51 100       161 return $pieces[0] if $is_aq;
286 39         76 push @var, @{ $pieces[0] };
  39         109  
287 39         106 $already_parsed_args = 1;
288             } elsif ($self->{'AUTO_EVAL'}) {
289 44         167 push @var, [undef, '~', @pieces], 0, '|', 'eval', 0;
290 44 100       141 return \@var if $is_aq;
291 30         62 $already_parsed_args = 1;
292             } elsif (@pieces == 1) { # [% "foo" %]
293 573 100       1339 return $pieces[0] if $is_aq;
294 565         1098 push @var, \ $pieces[0];
295 565         1128 $is_literal = 1;
296             } else { # [% "foo $bar baz" %]
297 53         177 push @var, [undef, '~', @pieces];
298 53 100       187 return [$var[0], 0] if $is_aq;
299             }
300              
301             ### allow for leading $foo type constructs
302             } elsif ($$str_ref =~ m{ \G \$ (\w+) \b }gcx) {
303 302 100       792 if ($self->{'V1DOLLAR'}) {
304 203         551 push @var, $1;
305 203 50 66     499 $is_namespace = 1 if $self->{'NAMESPACE'} && $self->{'NAMESPACE'}->{$1};
306             } else {
307 99         389 push @var, [$1, 0];
308             }
309              
310             ### allow for ${foo.bar} type constructs
311             } elsif ($$str_ref =~ m{ \G \$\{ }gcx) {
312 126         384 push @var, $self->parse_expr($str_ref);
313 126 50       723 $$str_ref =~ m{ \G \s* $QR_COMMENTS \} }gcxo
314             || $self->throw('parse', 'Missing close "}" from "${"', undef, pos($$str_ref));
315              
316             ### looks like an array constructor
317             } elsif (! $is_aq && $$str_ref =~ m{ \G \[ }gcx) {
318 315         1008 local $self->{'_operator_precedence'} = 0; # reset presedence
319 315         789 my $arrayref = [undef, '[]'];
320 315         883 while (defined(my $var = $self->parse_expr($str_ref))) {
321 400         920 push @$arrayref, $var;
322 400         1801 $$str_ref =~ m{ \G \s* $QR_COMMENTS , }gcxo;
323             }
324 312 100       1698 $$str_ref =~ m{ \G \s* $QR_COMMENTS \] }gcxo
325             || $self->throw('parse.missing.square_bracket', "Missing close \]", undef, pos($$str_ref));
326 309         1042 push @var, $arrayref;
327              
328             ### looks like a hash constructor
329             } elsif (! $is_aq && $$str_ref =~ m{ \G \{ }gcx) {
330 197         629 local $self->{'_operator_precedence'} = 0; # reset precedence
331 197         570 my $hashref = [undef, '{}'];
332 197         987 while (defined(my $key = $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b) (?! \\.) \\s* $QR_COMMENTS"}))) {
333 226         1056 $$str_ref =~ m{ \G \s* $QR_COMMENTS (?: = >? | [:,]) }gcxo;
334 226         617 my $val = $self->parse_expr($str_ref);
335 226         728 push @$hashref, $key, $val;
336 226         1649 $$str_ref =~ m{ \G \s* $QR_COMMENTS , }gcxo;
337             }
338 197 50       1223 $$str_ref =~ m{ \G \s* $QR_COMMENTS \} }gcxo
339             || $self->throw('parse.missing.curly_bracket', "Missing close \}", undef, pos($$str_ref));
340 197         668 push @var, $hashref;
341              
342             ### looks like a paren grouper or a context specifier
343             } elsif (! $is_aq && $$str_ref =~ m{ \G ([\$\@]?) \( }gcx) {
344 228         694 local $self->{'_operator_precedence'} = 0; # reset precedence
345 228         539 my $ctx = $1;
346 228         904 my $var = $self->parse_expr($str_ref, {allow_parened_ops => 1});
347              
348 228 50       1364 $$str_ref =~ m{ \G \s* $QR_COMMENTS \) }gcxo
349             || $self->throw('parse.missing.paren', "Missing close \) in group", undef, pos($$str_ref));
350              
351 228 100       649 $self->throw('parse', 'Paren group cannot be followed by an open paren', undef, pos($$str_ref))
352             if $$str_ref =~ m{ \G \( }gcx;
353 225         419 $already_parsed_args = 1;
354              
355 225 100       880 if (! ref $var) {
    50          
356 3         9 push @var, \$var, 0;
357 3         78 $is_literal = 1;
358             } elsif (! defined $var->[0]) {
359 0         0 push @var, $var, 0;
360             } else {
361 222         603 push @var, @$var;
362             }
363 225 100       684 if ($ctx) {
364 90         276 my $copy = [@var];
365 90         539 @var = ([undef, "$ctx()", $copy], 0);
366             }
367              
368             ### nothing to find - return failure
369             } else {
370 2905 100 100     10340 pos($$str_ref) = $mark if $is_aq || $has_prefix;
371 2905         9988 return;
372             }
373              
374             # auto_quoted thing was too complicated
375 13083 100       26613 if ($is_aq) {
376 101         284 pos($$str_ref) = $mark;
377 101         360 return;
378             }
379              
380             ### looks for args for the initial
381 12982 100       32905 if ($already_parsed_args) {
    100          
382             # do nothing
383             } elsif ($$str_ref =~ m{ \G \( }gcxo) {
384 185         538 local $self->{'_operator_precedence'} = 0; # reset precedence
385 185         698 my $args = $self->parse_args($str_ref, {is_parened => 1});
386 185 50       1320 $$str_ref =~ m{ \G \s* $QR_COMMENTS \) }gcxo
387             || $self->throw('parse.missing.paren', "Missing close \) in args", undef, pos($$str_ref));
388 185         571 push @var, $args;
389             } else {
390 12503         21685 push @var, 0;
391             }
392              
393             ### allow for nested items
394 12982         89226 while ($$str_ref =~ m{ \G \s* $QR_COMMENTS ( \.(?!\.) | \|(?!\|) ) }gcx) {
395 3265 100 100     9788 if ($1 eq '|' && $self->{'V2PIPE'}) {
396 15         53 pos($$str_ref) -= 1;
397 15         38 last;
398             }
399              
400 3250 100       9852 push(@var, $1) if ! $ARGS->{'no_dots'};
401              
402 3250         9918 $$str_ref =~ m{ \G \s* $QR_COMMENTS }gcxo;
403              
404             ### allow for interpolated variables in the middle - one.$foo.two
405 3250 100       13838 if ($$str_ref =~ m{ \G \$ (\w+) \b }gcxo) {
    100          
    50          
406 54 100       262 push @var, $self->{'V1DOLLAR'} ? $1 : [$1, 0];
407              
408             ### or one.${foo.bar}.two
409             } elsif ($$str_ref =~ m{ \G \$\{ }gcx) {
410 29         104 push @var, $self->parse_expr($str_ref);
411 29 50       254 $$str_ref =~ m{ \G \s* $QR_COMMENTS \} }gcxo
412             || $self->throw('parse', 'Missing close "}" from "${"', undef, pos($$str_ref));
413              
414             ### allow for names (foo.bar or foo.0 or foo.-1)
415             } elsif ($$str_ref =~ m{ \G (-? \w+) }gcx) {
416 3167         7533 push @var, $1;
417              
418             } else {
419 0         0 $self->throw('parse', "Not sure how to continue parsing", undef, pos($$str_ref));
420             }
421              
422             ### looks for args for the nested item
423 3250 100       7394 if ($$str_ref =~ m{ \G \( }gcx) {
424 713         1931 local $self->{'_operator_precedence'} = 0; # reset precedence
425 713         2660 my $args = $self->parse_args($str_ref, {is_parened => 1});
426 698 50       4238 $$str_ref =~ m{ \G \s* $QR_COMMENTS \) }gcxo
427             || $self->throw('parse.missing.paren', "Missing close \) in args of nested item", undef, pos($$str_ref));
428 698         5622 push @var, $args;
429             } else {
430 2537         15079 push @var, 0;
431             }
432              
433             }
434              
435             ### flatten literals and constants as much as possible
436 12967         23182 my $var;
437 12967 100       26808 if ($is_literal) {
    100          
438 4776         6734 $var = ${ $var[0] };
  4776         9432  
439 4776 100       11816 if ($#var != 1) {
440 114         399 $var[0] = [undef, '~', $var];
441 114         240 $var = \@var;
442             }
443             } elsif ($is_namespace) {
444 48         92 my $name = $var[0];
445 48         519 local $self->{'_vars'}->{$name} = $self->{'NAMESPACE'}->{$name};
446 48         228 $var = $self->play_expr(\@var, {is_namespace_during_compile => 1});
447             } else {
448 8143         14925 $var = \@var;
449             }
450              
451             ### allow for all "operators"
452 12967 100       31272 if (! $self->{'_operator_precedence'}) {
453 11586         19938 my $tree;
454             my $found;
455 11586         16150 while (1) {
456 12670         21092 my $mark = pos $$str_ref;
457              
458 12670         36250 $$str_ref =~ m{ \G \s* $QR_COMMENTS }gcxo;
459              
460 12670 100 100     97144 if ($self->{'_end_tag'} && $$str_ref =~ m{ \G [+=~-]? $self->{'_end_tag'} }gcx) {
    100          
461 5849         15928 pos($$str_ref) = $mark;
462 5849         13755 last;
463             } elsif ($$str_ref !~ m{ \G ($QR_OP) }gcxo) {
464 3774         10236 pos($$str_ref) = $mark;
465 3774         8885 last;
466             }
467 3047 100 100     14125 if ($OP_ASSIGN->{$1} && ! $ARGS->{'allow_parened_ops'}) { # only allow assignment in parens
468 1955         5086 pos($$str_ref) = $mark;
469 1955         4934 last;
470             }
471 1092         2572 local $self->{'_operator_precedence'} = 1;
472 1092         2133 my $op = $1;
473 1092 100 100     2642 $op = 'eq' if $op eq '==' && (! defined($self->{'V2EQUALS'}) || $self->{'V2EQUALS'});
      100        
474 1092 100 100     2233 $op = 'ne' if $op eq '!=' && (! defined($self->{'V2EQUALS'}) || $self->{'V2EQUALS'});
      100        
475              
476             ### allow for postfix - doesn't check precedence - someday we might change - but not today (only affects post ++ and --)
477 1092 100 66     3855 if ($OP_POSTFIX->{$op}) {
    100          
478 9         30 $var = [[undef, $op, $var, 1], 0]; # cheat - give a "second value" to postfix ops
479 9         28 next;
480              
481             ### allow for prefix operator precedence
482             } elsif ($has_prefix && $OP->{$op}->[1] < $OP_PREFIX->{$has_prefix->[-1]}->[1]) {
483 9 50       28 if ($tree) {
484 0 0       0 if ($#$tree == 1) { # only one operator - keep simple things fast
485 0         0 $var = [[undef, $tree->[0], $var, $tree->[1]], 0];
486             } else {
487 0         0 unshift @$tree, $var;
488 0         0 $var = $self->apply_precedence($tree, $found, $str_ref);
489             }
490 0         0 undef $tree;
491 0         0 undef $found;
492             }
493 9         34 $var = [[undef, $has_prefix->[-1], $var ], 0];
494 9         23 pop @$has_prefix;
495 9 50       34 $has_prefix = undef if ! @$has_prefix;
496             }
497              
498             ### add the operator to the tree
499 1083         2993 my $var2 = $self->parse_expr($str_ref);
500 1081 100       2460 $self->throw('parse', 'Missing variable after "'.$op.'"', undef, pos($$str_ref)) if ! defined $var2;
501 1075   100     1653 push (@{ $tree ||= [] }, $op, $var2);
  1075         4924  
502 1075         6270 $found->{$OP->{$op}->[1]}->{$op} = 1; # found->{precedence}->{op}
503             }
504              
505             ### if we found operators - tree the nodes by operator precedence
506 11578 100       24409 if ($tree) {
507 863 100       2147 if (@$tree == 2) { # only one operator - keep simple things fast
508 744 100 100     3072 if ($OP->{$tree->[0]}->[0] eq 'assign' && $tree->[0] =~ /(.+)=/) {
509 30         197 $var = [[undef, '=', $var, [[undef, $1, $var, $tree->[1]], 0]], 0]; # "a += b" => "a = a + b"
510             } else {
511 714         3724 $var = [[undef, $tree->[0], $var, $tree->[1]], 0];
512             }
513             } else {
514 119         355 unshift @$tree, $var;
515 119         377 $var = $self->apply_precedence($tree, $found, $str_ref);
516             }
517             }
518             }
519              
520             ### allow for prefix on non-chained variables
521 12959 100       26480 if ($has_prefix) {
522 89         487 $var = [[undef, $_, $var], 0] for reverse @$has_prefix;
523             }
524              
525 12959         49503 return $var;
526             }
527              
528             ### this is used to put the parsed variables into the correct operations tree
529             sub apply_precedence {
530 146     146 0 343 my ($self, $tree, $found, $str_ref) = @_;
531              
532 146         247 my @var;
533             my $trees;
534             ### look at the operators we found in the order we found them
535 146         648 for my $prec (sort keys %$found) {
536 146         310 my $ops = $found->{$prec};
537 146         298 local $found->{$prec};
538 146         263 delete $found->{$prec};
539              
540             ### split the array on the current operators for this level
541 146         243 my @ops;
542             my @exprs;
543 146         522 for (my $i = 1; $i <= $#$tree; $i += 2) {
544 383 100       997 next if ! $ops->{ $tree->[$i] };
545 290         863 push @ops, $tree->[$i];
546 290         652 push @exprs, [splice @$tree, 0, $i, ()];
547 290         481 shift @$tree;
548 290         731 $i = -1;
549             }
550 146 50       396 next if ! @exprs; # this iteration didn't have the current operator
551 146 50       373 push @exprs, $tree if scalar @$tree; # add on any remaining items
552              
553             ### simplify sub expressions
554 146         304 for my $node (@exprs) {
555 436 100       871 if (@$node == 1) {
    100          
556 370         761 $node = $node->[0]; # single item - its not a tree
557             } elsif (@$node == 3) {
558 39         158 $node = [[undef, $node->[1], $node->[0], $node->[2]], 0]; # single operator - put it straight on
559             } else {
560 27         67 $node = $self->apply_precedence($node, $found, $str_ref); # more complicated - recurse
561             }
562             }
563              
564             ### assemble this current level
565              
566             ### some rules:
567             # 1) items at the same precedence level must all be either right or left or ternary associative
568             # 2) ternary items cannot share precedence with anybody else.
569             # 3) there really shouldn't be another operator at the same level as a postfix
570 146         415 my $type = $OP->{$ops[0]}->[0];
571              
572 146 100 100     583 if ($type eq 'ternary') {
    100          
573 72         141 my $op = $OP->{$ops[0]}->[2]->[0]; # use the first op as what we are using
574              
575             ### return simple ternary
576 72 100       160 if (@exprs == 3) {
577 57 50       153 $self->throw('parse', "Ternary operator mismatch", undef, pos($$str_ref)) if $ops[0] ne $op;
578 57 50 33     203 $self->throw('parse', "Ternary operator mismatch", undef, pos($$str_ref)) if ! $ops[1] || $ops[1] eq $op;
579 57         371 return [[undef, $op, @exprs], 0];
580             }
581              
582              
583             ### reorder complex ternary - rare case
584 15         46 while ($#ops >= 1) {
585             ### if we look starting from the back - the first lead ternary op will always be next to its matching op
586 15         49 for (my $i = $#ops; $i >= 0; $i --) {
587 60 100       185 next if $OP->{$ops[$i]}->[2]->[1] eq $ops[$i];
588 30         79 my ($op, $op2) = splice @ops, $i, 2, (); # remove the pair of operators
589 30         118 my $node = [[undef, $op, @exprs[$i .. $i + 2]], 0];
590 30         122 splice @exprs, $i, 3, $node;
591             }
592             }
593 15         90 return $exprs[0]; # at this point the ternary has been reduced to a single operator
594              
595             } elsif ($type eq 'right' || $type eq 'assign') {
596 27         63 my $val = $exprs[-1];
597 27         93 for (reverse (0 .. $#exprs - 1)) {
598 39 100 100     175 if ($type eq 'assign' && $ops[$_ - 1] =~ /(.+)=$/) {
599 9         71 $val = [[undef, '=', $exprs[$_], [[undef, $1, $exprs[$_], $val], 0]], 0];
600             } else {
601 30         123 $val = [[undef, $ops[$_ - 1], $exprs[$_], $val], 0];
602             }
603             }
604 27         208 return $val;
605              
606             } else {
607 47         92 my $val = $exprs[0];
608 47         339 $val = [[undef, $ops[$_ - 1], $val, $exprs[$_]], 0] for (1 .. $#exprs);
609 47         281 return $val;
610              
611             }
612             }
613              
614 0         0 $self->throw('parse', "Couldn't apply precedence", undef, pos($$str_ref));
615             }
616              
617             ### look for arguments - both positional and named
618             sub parse_args {
619 2153     2153 1 3793 my $self = shift;
620 2153         3410 my $str_ref = shift;
621 2153   50     4689 my $ARGS = shift || {};
622              
623 2153         4984 my @args;
624             my @named;
625 2153         0 my $name;
626 2153   100     5352 my $end = $self->{'_end_tag'} || '(?!)';
627 2153         3170 while (1) {
628 4679         8135 my $mark = pos $$str_ref;
629              
630             ### look to see if the next thing is a directive or a closing tag
631 4679 100 100     23954 if (! $ARGS->{'is_parened'}
    100 100        
      50        
      66        
      66        
632             && ! $ARGS->{'require_arg'}
633             && $$str_ref =~ m{ \G \s* $QR_COMMENTS $QR_DIRECTIVE (?: \s+ | (?: \s* $QR_COMMENTS (?: ;|[+=~-]?$end))) }gcxo
634             && ((pos($$str_ref) = $mark) || 1) # always revert
635             && $DIRECTIVES->{$self->{'ANYCASE'} ? uc($1) : $1} # looks like a directive - we are done
636             ) {
637 26         62 last;
638             }
639 4653 100       21239 if ($$str_ref =~ m{ \G [+=~-]? $end }gcx) {
640 645         1687 pos($$str_ref) = $mark;
641 645         1445 last;
642             }
643              
644             ### find the initial arg
645 4008         6475 my $name;
646 4008 100       7810 if ($ARGS->{'allow_bare_filenames'}) {
647 755         3524 $name = $self->parse_expr($str_ref, {auto_quote => "
648             ($QR_FILENAME # file name
649             | $QR_BLOCK # or block
650             (?= [+=~-]? $end # an end tag
651             | \\s*[+,;] # followed by explicit + , or ;
652             | \\s+ (?! [\\s=]) # or space not before an =
653             ) \\s* $QR_COMMENTS"});
654             # filenames can be separated with a "+" - why a "+" ?
655 755 100       2962 if ($$str_ref =~ m{ \G \+ (?! \s* $QR_COMMENTS [+=~-]? $end) }gcxo) {
656 6         17 push @args, $name;
657 6         13 $ARGS->{'require_arg'} = 1;
658 6         16 next;
659             }
660             }
661 4002 100       8795 if (! defined $name) {
662 3450         8179 $name = $self->parse_expr($str_ref);
663 3435 100       8148 if (! defined $name) {
664 1467 50 33     4866 if ($ARGS->{'require_arg'} && ! @args && ! $ARGS->{'positional_only'} && ! @named) {
      33        
      0        
665 0         0 $self->throw('parse', 'Argument required', undef, pos($$str_ref));
666             } else {
667 1467         2999 last;
668             }
669             }
670             }
671              
672 2520         7950 $$str_ref =~ m{ \G \s* $QR_COMMENTS }gcxo;
673              
674             ### see if it is named or positional
675 2520 100       7848 if ($$str_ref =~ m{ \G \s* $QR_COMMENTS = >? }gcxo) {
676 597 50       1580 $self->throw('parse', 'Named arguments not allowed', undef, $mark) if $ARGS->{'positional_only'};
677 597         1398 my $val = $self->parse_expr($str_ref);
678 597 100 66     4116 $name = $name->[0] if ref($name) && @$name == 2 && ! $name->[1]; # strip a level of indirection on named arguments
      66        
679 597         1809 push @named, $name, $val;
680             } else {
681 1923         4565 push @args, $name;
682             }
683              
684             ### look for trailing comma
685 2520   100     11881 $ARGS->{'require_arg'} = ($$str_ref =~ m{ \G \s* $QR_COMMENTS , }gcxo) || 0;
686             }
687              
688             ### allow for named arguments to be added at the front (if asked)
689 2138 100       5334 if ($ARGS->{'named_at_front'}) {
    100          
690 1178         4311 unshift @args, [[undef, '{}', @named], 0];
691             } elsif (scalar @named) { # only add at end - if there are some
692 82         381 push @args, [[undef, '{}', @named], 0]
693             }
694              
695 2138         7555 return \@args;
696             }
697              
698             ###----------------------------------------------------------------###
699              
700             sub parse_BLOCK {
701 459     459 0 1167 my ($self, $str_ref, $node) = @_;
702              
703 459   50     1337 my $end = $self->{'_end_tag'} || '(?!)';
704 459         2730 my $block_name = $self->parse_expr($str_ref, {auto_quote => "
705             ($QR_FILENAME # file name
706             | $QR_BLOCK # or block
707             (?= [+=~-]? $end # an end tag
708             | \\s*[+,;] # followed by explicit + , or ;
709             | \\s+ (?! [\\s=]) # or space not before an =
710             ) \\s* $QR_COMMENTS"});
711              
712 459 100       2006 return '' if ! defined $block_name;
713              
714 391 50       892 my $prepend = join "/", map {$_->[3]} grep {ref($_) && $_->[0] eq 'BLOCK'} @{ $self->{'_state'} || {} };
  6 50       20  
  44         361  
  391         1681  
715 391 100       1864 return $prepend ? "$prepend/$block_name" : $block_name;
716             }
717              
718 78     78 0 281 sub parse_CALL { $DIRECTIVES->{'GET'}->[0]->(@_) }
719              
720             sub parse_CASE {
721 30     30 0 68 my ($self, $str_ref) = @_;
722 30 100       93 return if $$str_ref =~ m{ \G DEFAULT \s* }gcx;
723 27         72 return $self->parse_expr($str_ref);
724             }
725              
726             sub parse_CATCH {
727 131     131 0 333 my ($self, $str_ref) = @_;
728 131         734 return $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b (?: \\.\\w+\\b)*) $QR_AQ_SPACE \\s* $QR_COMMENTS"});
729             }
730              
731             sub parse_CONFIG {
732 124     124 0 314 my ($self, $str_ref) = @_;
733              
734 124         291 my %ctime = map {$_ => 1} @Template::Alloy::CONFIG_COMPILETIME;
  1736         3456  
735 124         409 my %rtime = map {$_ => 1} @Template::Alloy::CONFIG_RUNTIME;
  620         1232  
736              
737 124         278 my $mark = pos($$str_ref);
738 124         512 my $config = $self->parse_args($str_ref, {named_at_front => 1, is_parened => 1});
739 124         346 my $ref = $config->[0]->[0];
740 124         389 for (my $i = 2; $i < @$ref; $i += 2) {
741 109         307 my $key = $ref->[$i] = uc $ref->[$i];
742 109         241 my $val = $ref->[$i + 1];
743 109 100       393 if ($ctime{$key}) {
    100          
744 52         204 $self->{$key} = $self->play_expr($val);
745 52 100       189 if ($key eq 'INTERPOLATE') {
746 7 50       141 $self->{'_start_tag'} = (! $self->{'INTERPOLATE'}) ? $self->{'START_TAG'} : qr{(?: $self->{'START_TAG'} | (\$))}sx;
747             }
748             } elsif (! $rtime{$key}) {
749 3         26 $self->throw('parse', "Unknown CONFIG option \"$key\"", undef, pos($$str_ref));
750             }
751             }
752 121         359 for (my $i = 1; $i < @$config; $i++) {
753 15         43 my $key = $config->[$i] = uc $config->[$i]->[0];
754 15 100       59 if ($ctime{$key}) {
    50          
755 12 100       65 $config->[$i] = "CONFIG $key = ".(defined($self->{$key}) ? $self->{$key} : 'undef');
756             } elsif (! $rtime{$key}) {
757 0         0 $self->throw('parse', "Unknown CONFIG option \"$key\"", undef, pos($$str_ref));
758             }
759             }
760 121         758 return $config;
761             }
762              
763             sub parse_DEBUG {
764 3     3 0 12 my ($self, $str_ref) = @_;
765 3 50       20 $$str_ref =~ m{ \G ([Oo][Nn] | [Oo][Ff][Ff] | [Ff][Oo][Rr][Mm][Aa][Tt]) \s* }gcx
766             || $self->throw('parse', "Unknown DEBUG option", undef, pos($$str_ref));
767 3         15 my $ret = [lc($1)];
768 3 50       16 if ($ret->[0] eq 'format') {
769 3 50       36 $$str_ref =~ m{ \G ([\"\']) (|.*?[^\\]) \1 \s* }gcxs
770             || $self->throw('parse', "Missing format string", undef, pos($$str_ref));
771 3         19 $ret->[1] = $2;
772             }
773 3         16 return $ret;
774             }
775              
776 9     9 0 31 sub parse_DEFAULT { $DIRECTIVES->{'SET'}->[0]->(@_) }
777              
778             sub parse_DUMP {
779 84     84 0 234 my ($self, $str_ref) = @_;
780 84         362 return $self->parse_args($str_ref, {named_at_front => 1});
781             }
782              
783             sub parse_EVAL {
784 22     22 0 55 my ($self, $str_ref) = @_;
785 22         88 return $self->parse_args($str_ref, {named_at_front => 1});
786             }
787              
788             sub parse_FILTER {
789 74     74 0 181 my ($self, $str_ref) = @_;
790 74         152 my $name = '';
791 74 100       261 if ($$str_ref =~ m{ \G ([^\W\d]\w*) \s* = \s* }gcx) {
792 18         44 $name = $1;
793             }
794              
795 74         196 my $filter = $self->parse_expr($str_ref);
796 74 50       193 $filter = '' if ! defined $filter;
797              
798 74         340 return [$name, $filter];
799             }
800              
801             sub parse_FOR {
802 253     253 0 616 my ($self, $str_ref) = @_;
803 253         716 my $items = $self->parse_expr($str_ref);
804 253         526 my $var;
805 253 100       1418 if ($$str_ref =~ m{ \G \s* $QR_COMMENTS (= | [Ii][Nn]\b) \s* }gcxo) {
806 184         537 $var = [@$items];
807 184         460 $items = $self->parse_expr($str_ref);
808             }
809 253         1270 return [$var, $items];
810             }
811              
812             sub parse_GET {
813 385     385 0 823 my ($self, $str_ref) = @_;
814 385         1091 my $ref = $self->parse_expr($str_ref);
815 385 100       1187 $self->throw('parse', "Missing variable name", undef, pos($$str_ref)) if ! defined $ref;
816 368 100       956 if ($self->{'AUTO_FILTER'}) {
817 13 100       54 $ref = [[undef, '~', $ref], 0] if ! ref $ref;
818 13 100 66     81 push @$ref, '|', $self->{'AUTO_FILTER'}, 0 if @$ref < 3 || $ref->[-3] ne '|';
819             }
820 368         1416 return $ref;
821             }
822              
823             sub parse_IF {
824 244     244 0 560 my ($self, $str_ref) = @_;
825 244         659 return $self->parse_expr($str_ref);
826             }
827              
828 119     119 0 377 sub parse_INCLUDE { $DIRECTIVES->{'PROCESS'}->[0]->(@_) }
829              
830 21     21 0 70 sub parse_INSERT { $DIRECTIVES->{'PROCESS'}->[0]->(@_) }
831              
832             sub parse_LOOP {
833 18     18 0 49 my ($self, $str_ref, $node) = @_;
834 18   33     47 return $self->parse_expr($str_ref)
835             || $self->throw('parse', 'Missing variable on LOOP directive', undef, pos($$str_ref));
836             }
837              
838             sub parse_MACRO {
839 86     86 0 221 my ($self, $str_ref, $node) = @_;
840              
841 86         350 my $name = $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b) (?! \\.)"});
842 86 50       350 $self->throw('parse', "Missing macro name", undef, pos($$str_ref)) if ! defined $name;
843 86 50       228 if (! ref $name) {
844 86         229 $name = [ $name, 0 ];
845             }
846              
847 86         152 my $args;
848 86 100       366 if ($$str_ref =~ m{ \G \( \s* }gcx) {
    100          
849 47         216 $args = $self->parse_args($str_ref, {positional_only => 1});
850 47 50       255 $$str_ref =~ m{ \G \) \s* }gcx || $self->throw('parse.missing', "Missing close ')'", undef, pos($$str_ref));
851             } elsif ($self->{'V1DOLLAR'}) { # allow for Velocity style macro args (no parens - but dollars are fine)
852 24         103 while ($$str_ref =~ m{ \G (\s+ \$) }gcx) {
853 22         59 my $lead = $1;
854 22         52 my $arg = $self->parse_expr($str_ref);
855 22 50       64 if (! defined $arg) {
856 0         0 pos($$str_ref) -= length($lead);
857 0         0 last;
858             }
859 22         93 push @$args, $arg;
860             }
861             }
862              
863 86         226 $node->[6] = 1; # set a flag to keep parsing
864 86         365 return [$name, $args];
865             }
866              
867             sub parse_META {
868 136     136 0 344 my ($self, $str_ref) = @_;
869 136         494 my $args = $self->parse_args($str_ref, {named_at_front => 1});
870 136         314 my $hash;
871 136 50 33     530 return [%$hash] if ($hash = $self->play_expr($args->[0])) && UNIVERSAL::isa($hash, 'HASH');
872 0         0 return undef;
873             }
874              
875             sub parse_PROCESS {
876 546     546 0 1214 my ($self, $str_ref) = @_;
877              
878 546         2630 return $self->parse_args($str_ref, {
879             named_at_front => 1,
880             allow_bare_filenames => 1,
881             require_arg => 1,
882             });
883             }
884              
885             sub parse_RETURN {
886 15     15 0 35 my ($self, $str_ref) = @_;
887 15         43 my $ref = $self->parse_expr($str_ref); # optional return value
888 15         55 return $ref;
889             }
890              
891             sub parse_SET {
892 1220     1220 0 4273 my ($self, $str_ref, $node, $initial_op, $initial_var) = @_;
893 1220         2554 my @SET;
894             my $func;
895              
896 1220 100       2586 if ($initial_op) {
897 804 50 100     8173 if ($initial_op eq '='
    100 50        
      66        
      66        
898             && $$str_ref =~ m{ \G \s* $QR_COMMENTS $QR_DIRECTIVE }gcx # find a word
899             && ((pos($$str_ref) -= length($1)) || 1) # always revert
900             && $DIRECTIVES->{$self->{'ANYCASE'} ? uc $1 : $1}) { # make sure its a directive - if so set up capturing
901 75         217 $node->[6] = 1; # set a flag to keep parsing
902 75   50     313 my $val = $node->[4] ||= []; # setup storage
903 75         490 return [[$initial_op, $initial_var, $val]];
904             } else { # get a normal variable
905 729         2216 my $val = $self->parse_expr($str_ref);
906 729 100       1999 if ($initial_op =~ /(.+)=$/) {
907 33         78 $initial_op = '=';
908 33         131 $val = [[undef, $1, $initial_var, $val], 0];
909             }
910 729         4125 return [[$initial_op, $initial_var, $val]];
911             }
912             }
913              
914 416         634 while (1) {
915 829         2078 my $set = $self->parse_expr($str_ref);
916 829 100       2082 last if ! defined $set;
917              
918 426 100       3625 if ($$str_ref =~ m{ \G \s* $QR_COMMENTS ($QR_OP_ASSIGN) >? }gcx) {
919 399         958 my $op = $1;
920 399 100 66     3952 if ($op eq '='
    100 50        
      66        
      66        
921             && $$str_ref =~ m{ \G \s* $QR_COMMENTS $QR_DIRECTIVE }gcx # find a word
922             && ((pos($$str_ref) -= length($1)) || 1) # always revert
923             && $DIRECTIVES->{$self->{'ANYCASE'} ? uc $1 : $1}) { # make sure its a directive - if so set up capturing
924 7         28 $node->[6] = 1; # set a flag to keep parsing
925 7   50     39 my $val = $node->[4] ||= []; # setup storage
926 7 50       28 if ($op =~ /(.+)=$/) {
927 0         0 $op = '=';
928 0         0 $val = [[undef, $1, $set, $val], 0];
929             }
930 7         23 push @SET, [$op, $set, $val];
931 7         20 last;
932             } else { # get a normal variable
933 392         1177 push @SET, [$op, $set, $self->parse_expr($str_ref)];
934             }
935             } else {
936 27         127 push @SET, ['=', $set, undef];
937             }
938             }
939              
940 410         1852 return \@SET;
941             }
942              
943 30     30 0 107 sub parse_SWITCH { $DIRECTIVES->{'GET'}->[0]->(@_) }
944              
945             sub parse_TAGS {
946 78     78 0 204 my ($self, $str_ref, $node) = @_;
947              
948 78         141 my ($start, $end);
949 78 100       279 if ($$str_ref =~ m{ \G (\w+) }gcxs) {
950 57   33     303 my $ref = $TAGS->{lc $1} || $self->throw('parse', "Invalid TAGS name \"$1\"", undef, pos($$str_ref));
951 57         243 ($start, $end) = @$ref;
952              
953             } else {
954 21         57 local $self->{'_operator_precedence'} = 1; # prevent operator matching
955 21 100 33     233 $start = $$str_ref =~ m{ \G (?= \s* $QR_COMMENTS [\'\"\/]) }gcx
956             ? $self->parse_expr($str_ref)
957             : $self->parse_expr($str_ref, {auto_quote => "(\\S+) \\s+ $QR_COMMENTS"})
958             || $self->throw('parse', "Invalid opening tag in TAGS", undef, pos($$str_ref));
959 18 100 33     193 $end = $$str_ref =~ m{ \G (?= \s* $QR_COMMENTS [\'\"\/]) }gcx
960             ? $self->parse_expr($str_ref)
961             : $self->parse_expr($str_ref, {auto_quote => "(\\S+) \\s* $QR_COMMENTS"})
962             || $self->throw('parse', "Invalid closing tag in TAGS", undef, pos($$str_ref));
963 18         57 for my $tag ($start, $end) {
964 36         116 $tag = $self->play_expr($tag);
965 36 100       143 $tag = quotemeta($tag) if ! ref $tag;
966             }
967             }
968 75         453 return [$start, $end];
969             }
970              
971             sub parse_THROW {
972 109     109 0 265 my ($self, $str_ref, $node) = @_;
973 109         568 my $name = $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b (?: \\.\\w+\\b)*) $QR_AQ_SPACE \\s* $QR_COMMENTS"});
974 109 50       362 $self->throw('parse.missing', "Missing name in THROW", $node, pos($$str_ref)) if ! $name;
975 109         384 my $args = $self->parse_args($str_ref, {named_at_front => 1});
976 109         567 return [$name, $args];
977             }
978              
979             sub parse_UNLESS {
980 22     22 0 67 my $ref = $DIRECTIVES->{'IF'}->[0]->(@_);
981 22         115 return [[undef, '!', $ref], 0];
982             }
983              
984             sub parse_USE {
985 101     101 0 298 my ($self, $str_ref) = @_;
986              
987 101         193 my $var;
988 101         249 my $mark = pos $$str_ref;
989 101 100 66     582 if (defined(my $_var = $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b) (?! \\.) \\s* $QR_COMMENTS"}))
      66        
990             && ($$str_ref =~ m{ \G = >? \s* $QR_COMMENTS }gcxo # make sure there is assignment
991             || ((pos($$str_ref) = $mark) && 0)) # otherwise we need to rollback
992             ) {
993 7         20 $var = $_var;
994             }
995              
996 101         807 my $module = $self->parse_expr($str_ref, {auto_quote => "(\\w+\\b (?: (?:\\.|::) \\w+\\b)*) (?! \\.) \\s* $QR_COMMENTS"});
997 101 50       415 $self->throw('parse', "Missing plugin name while parsing $$str_ref", undef, pos($$str_ref)) if ! defined $module;
998 101         320 $module =~ s/\./::/g;
999              
1000 101         185 my $args;
1001 101         472 my $open = $$str_ref =~ m{ \G \( \s* $QR_COMMENTS }gcxo;
1002 101         704 $args = $self->parse_args($str_ref, {is_parened => $open, named_at_front => 1});
1003              
1004 101 100       359 if ($open) {
1005 64 50       400 $$str_ref =~ m{ \G \) \s* $QR_COMMENTS }gcxo || $self->throw('parse.missing', "Missing close ')'", undef, pos($$str_ref));
1006             }
1007              
1008 101         586 return [$var, $module, $args];
1009             }
1010              
1011             sub parse_VIEW {
1012 56     56 0 226 my ($self, $str_ref) = @_;
1013              
1014 56         355 my $ref = $self->parse_args($str_ref, {
1015             named_at_front => 1,
1016             require_arg => 1,
1017             });
1018              
1019 56         274 return $ref;
1020             }
1021              
1022 42     42 0 162 sub parse_WHILE { $DIRECTIVES->{'IF'}->[0]->(@_) }
1023              
1024 30     30 0 95 sub parse_WRAPPER { $DIRECTIVES->{'PROCESS'}->[0]->(@_) }
1025              
1026             ###----------------------------------------------------------------###
1027              
1028             sub dump_parse_tree {
1029 0     0 1   my $self = shift;
1030 0 0         $self = $self->new if ! ref $self;
1031 0           my $str = shift;
1032 0 0         my $ref = ref($str) ? $str : \$str;
1033 0           my $sub;
1034             my $nest;
1035             $sub = sub {
1036 0     0     my ($tree, $indent) = @_;
1037 0           my $out = "[\n";
1038 0           foreach my $node (@$tree) {
1039 0 0 0       if (! ref($node) || (! $node->[4] && ! $node->[5])) {
      0        
1040 0           $out .= "$indent ".$self->ast_string($node).",\n";
1041 0           next;
1042             }
1043 0           $out .= "$indent " . $nest->($node, "$indent ");
1044             }
1045 0           $out .= "$indent]";
1046 0           };
1047             $nest = sub {
1048 0     0     my ($node, $indent) = @_;
1049 0           my $out = $self->ast_string([@{$node}[0..3]]);
  0            
1050 0           chop $out;
1051 0 0         if ($node->[4]) {
1052 0           $out .= ", ";
1053 0           $out .= $sub->($node->[4], "$indent");
1054             }
1055 0 0         if ($node->[5]) {
    0          
1056 0           $out .= ", ". $nest->($node->[5], "$indent") . $indent;
1057             } elsif (@$node >= 6) {
1058 0           $out .= ", ". $self->ast_string($node->[5]);
1059             }
1060 0 0         if (@$node >= 7) { $out.= ", ". $self->ast_string($node->[6]) };
  0            
1061 0           $out .= "],\n";
1062 0           return $out;
1063 0           };
1064              
1065 0           return $sub->($self->parse_tree($ref), '') ."\n";
1066             }
1067              
1068             sub dump_parse_expr {
1069 0     0 1   my $self = shift;
1070 0 0         $self = $self->new if ! ref $self;
1071 0           my $str = shift;
1072 0 0         my $ref = ref($str) ? $str : \$str;
1073 0           return $self->ast_string($self->parse_expr($ref));
1074             }
1075              
1076             ###----------------------------------------------------------------###
1077              
1078             1;
1079              
1080             __END__