File Coverage

blib/lib/HTML/Template/Parser.pm
Criterion Covered Total %
statement 87 88 98.8
branch 18 22 81.8
condition n/a
subroutine 14 14 100.0
pod 1 1 100.0
total 120 125 96.0


line stmt bran cond sub pod time code
1             package HTML::Template::Parser;
2              
3 10     10   272715 use 5.008_001;
  10         38  
  10         405  
4 10     10   55 use strict;
  10         21  
  10         338  
5 10     10   62 use warnings;
  10         16  
  10         547  
6              
7             our $VERSION = '0.1011';
8              
9 10     10   58 use base qw(Class::Accessor::Fast);
  10         18  
  10         9875  
10             __PACKAGE__->mk_accessors(qw());
11              
12 10     10   67535 use Parse::RecDescent;
  10         691977  
  10         82  
13 10     10   10755 use English;
  10         53842  
  10         65  
14              
15 10     10   13142 use HTML::Template::Parser::NodeBuilder;
  10         40  
  10         434  
16              
17 10     10   130 use vars '$errortext';
  10         17  
  10         603  
18 10     10   48 use vars '$errorprefix';
  10         21  
  10         11949  
19              
20             sub parse {
21 92     92 1 134962 my($self, $template_string) = @_;
22              
23 92         313 $self->_list_to_tree($self->_template_string_to_list($template_string));
24             }
25              
26             sub _template_string_to_list {
27 98     98   11663 my($self, $template_string) = @_;
28              
29 98         152 my @list;
30 98         187 my($line, $column) = (1, 1); # 1 orign
31 98         618 while($template_string =~ m!
32 164         517 my $pre = $PREMATCH;
33 164         1052 my $tag = $MATCH . $POSTMATCH;
34 164         242 my $tmp;
35              
36             # keep as plain text
37 164 100       724 push(@list, ['string', [$line, $column], $pre]) if(length($pre));
38              
39             # calc line & column
40 164         464 $line += (($tmp = $pre) =~ s/\n//g);
41 164 100       485 $column = 1 if($pre =~ /\n/);
42 164         417 ($tmp = $pre) =~ s/.*\n//s;
43 164         268 $column += length($tmp);
44 164         743 my $xxx = ( split(/\n/, $tag) )[0];
45              
46             # parse TMPL_* tag
47 164         292 my $tag_temp = $tag;
48 164         224 my $parsed_tag;
49             # capture error message.
50 164         292 my $error_string = '';
51 164         256 eval {
52 164         585 local (*STDERR, *Parse::RecDescent::ERROR);
53 164 50       1493 if(Parse::RecDescent->can('_write_ERROR')){ # @@@ @@@
54 164     8   2602 open(STDERR, '>:scalar', \$error_string);
  8         106  
  8         16  
  8         72  
55             }else{
56 0 0       0 open(Parse::RecDescent::ERROR, '>', \my $error_string) or die "open:[$!]\n";
57             }
58 164         30835 $parsed_tag = $self->_get_parser_instance->tag(\$tag_temp);
59             };
60 164 100       3593600 if($@){
61 1         11 die "line $line. column $column. something wrong. $@\n";
62             }
63 163 100       706 if($tag eq $tag_temp){
64 21         90 my $first_line_of_tag = ( split(/\n/, $tag) )[0];
65 21         63 my $first_line_of_error_string = ( split(/\n/, $error_string) )[0];
66 21         217 die "line $line. column $column. something wrong. Couldn't parse tag well\n[$first_line_of_tag][$first_line_of_error_string]\n";
67             }
68 142         595 splice(@$parsed_tag, 1, 0, [$line, $column]);
69 142         346 push(@list, $parsed_tag);
70              
71             # calc line & column
72 142         334 my $num_parsed = length($tag)-length($tag_temp);
73 142         386 my $parsed_string = substr($tag, 0, $num_parsed);
74 142         423 $line += (($tmp = $parsed_string) =~ s/\n//g);
75 142 50       2747 $column = 1 if($parsed_string =~ /\n/);
76 142         346 ($tmp = $parsed_string) =~ s/.*\n//s;
77 142         242 $column += length($tmp);
78              
79 142         905 $template_string = $tag_temp;
80             }
81 76 100       292 push(@list, ['string', [$line, $column], $template_string]) if(length($template_string));
82 76         389 \@list;
83             }
84              
85             sub _list_to_tree {
86 70     70   165 my($self, $raw_list) = @_;
87              
88             # insert Node::Group before Node::(If|Loop|Unless) and insert Node::GrooupEnd after Node::(IfEnd|LoopEnd|UnlessEnd) to make easier to convert.
89 70         122 my @node_list;
90 70         185 foreach my $raw_item (@$raw_list){
91 207         1526 my $node = HTML::Template::Parser::NodeBuilder::createNode($raw_item);
92              
93 207 100       976 if($node->type =~ /\A(if|loop|unless)\Z/){
94 27         352 push(@node_list, HTML::Template::Parser::Node::Group->new({sub_type => $1, line => $node->line, column => $node->column}));
95             }
96 207         1444 push(@node_list, $node);
97 207 100       524 if($node->type =~ /\A(if|loop|unless)_end\Z/){
98 25         301 push(@node_list, HTML::Template::Parser::Node::GroupEnd->new({sub_type => $1, line => $node->line, column => $node->column}));
99             }
100             }
101              
102 70         1010 my $root = HTML::Template::Parser::Node::Root->new();
103 70         339 $root->add_chidren(\@node_list);
104 59         623 $root;
105             }
106              
107             my $_instance;
108              
109             sub _get_parser_instance {
110 164 100   164   1662 return $_instance if $_instance;
111 8         22 $::RD_ERRORS=1;
112 8         20 $::RD_WARN=1;
113 8         18 $::RD_HINT=1;
114             # $::RD_TRACE=1; # @@@
115 8         82 return $_instance = Parse::RecDescent->new(<<'END;');
116             {
117             use strict;
118             use warnings;
119              
120             use HTML::Template::Parser::ExprParser;
121              
122             sub _parse_name_or_expr {
123             my $name_or_expr = shift;
124              
125             if($name_or_expr->[0] eq 'name'){
126             my $name = $name_or_expr->[1];
127             if($name =~ /^\$/){
128             die "Can't use \${name} at NAME. [$name]\n";
129             }
130             $name =~ s/\$?{([^}]+)}/$1/;
131             return [ 'name', [ 'variable', $name ] ];
132             }
133              
134             my $expr = $name_or_expr->[1];
135             my $expr_temp = $expr;
136             my $parsed_expr = HTML::Template::Parser::ExprParser->new->parse(\$expr_temp);
137             if($expr_temp !~ /\A\s*\Z/){
138             die "something wrong. Couldn't parse expr well\n[$expr]=>[$expr_temp]\n";
139             }
140             [ 'expr', $parsed_expr ];
141             }
142              
143             sub __dump_item__ {
144             require Data::Dumper;
145             my($thisrule, $a_item, $h_item) = @_;
146             print STDERR "Rule: $thisrule\n";
147             print STDERR Data::Dumper->Dump([{
148             '@item' => $a_item,
149             '%item' => $h_item,
150             }]);
151             }
152             }
153              
154             tag: htp_tag |
155              
156             htp_tag: htp_var
157             htp_tag: htp_include
158             htp_tag: htp_if
159             htp_tag: htp_else
160             htp_tag: htp_elsif
161             htp_tag: htp_unless
162             htp_tag: htp_loop
163              
164             htp_var: '<' /TMPL_VAR/i escape_1(?) name_or_expr escape_2(?) default(?) m!/?>! {
165             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
166             my $escape = $item{'escape_1(?)'}->[0] || $item{'escape_2(?)'}->[0];
167             my $default = $item{'default(?)'}->[0];
168              
169             [ 'var', $name_or_expr, $escape, $default ];
170             }
171              
172             htp_include: '<' /TMPL_INCLUDE/i name_or_expr m!/?>! {
173             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
174             [ 'include', $name_or_expr, ];
175             }
176             htp_include: '<' /TMPL_INCLUDE/i name_or_expr_bare m!/?>! {
177             my $name = [ 'name', $item{name_or_expr_bare} ];
178             [ 'include', ['name', $name, ]];
179             }
180              
181             htp_if: '<' /TMPL_IF/i name_or_expr '>' {
182             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
183            
184             [ 'if', $name_or_expr, ];
185             }
186             htp_if: '<' /TMPL_IF/i name_or_expr_bare '>' {
187             my $name = $item{name_or_expr_bare};
188              
189             [ 'if', [ 'name', [ 'variable', $name, ] ] ];
190             }
191             htp_if: '' {
192             [ 'if_end' ];
193             }
194              
195             htp_else: '<' /TMPL_ELSE/i '>' {
196             [ 'else' ];
197             }
198              
199             htp_elsif: '<' /TMPL_ELSIF/i name_or_expr '>' {
200             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
201             [ 'elsif', $name_or_expr, ];
202             }
203             htp_elsif: '<' /TMPL_ELSIF/i name_or_expr_bare '>' {
204             my $name = $item{name_or_expr_bare};
205             [ 'elsif', $name, ];
206             }
207              
208             htp_unless: '<' /TMPL_UNLESS/i name_or_expr '>' {
209             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
210             [ 'unless', $name_or_expr, ];
211             }
212             htp_unless: '<' /TMPL_UNLESS/i name_or_expr_bare '>' {
213             my $name = $item{name_or_expr_bare};
214             [ 'unless', $name, ];
215             }
216             htp_unless: '' {
217             [ 'unless_end' ];
218             }
219              
220             htp_loop: '<' /TMPL_LOOP/i name_or_expr default(?) '>' {
221             # __dump_item__($thisrule, \@item, \%item);
222             my $name_or_expr = _parse_name_or_expr($item{name_or_expr});
223             my $default = $item{'default(?)'}->[0];
224             [ 'loop', $name_or_expr, $default ];
225             }
226             htp_loop: '' {
227             [ 'loop_end' ];
228             }
229              
230             name_or_expr: /NAME|EXPR/i '=' name_or_expr_bare {
231             my $type = lc($item[1]);
232              
233             [ $type, $item{name_or_expr_bare} ];
234             }
235              
236             name_or_expr_bare: /'([^']*)'/ { $1; }
237             name_or_expr_bare: /"([^"]*)"/ { $1; }
238              
239             name_or_expr_bare: /[^>\s]+/ { $item[1]; }
240              
241              
242             escape_1: escape
243             escape_2: escape
244              
245             escape: /ESCAPE/i '=' /['"]?(0|1|URL|NONE|HTML|JS)['"]?/i {
246             lc($1);
247             }
248              
249             default: /DEFAULT/i '=' name_or_expr_bare { [ 'default', $item[3] ]; }
250              
251             END;
252             }
253              
254             1;
255              
256             1;
257             __END__