File Coverage

blib/lib/HTML/Template/Compiled/Expr.pm
Criterion Covered Total %
statement 77 79 97.4
branch 25 30 83.3
condition 2 3 66.6
subroutine 8 8 100.0
pod 0 3 0.0
total 112 123 91.0


line stmt bran cond sub pod time code
1             package HTML::Template::Compiled::Expr;
2 1     1   768 use strict;
  1         1  
  1         23  
3 1     1   2 use warnings;
  1         1  
  1         20  
4 1     1   33 use Carp qw(croak carp);
  1         1  
  1         53  
5             #use HTML::Template::Compiled::Expression qw(:expressions);
6 1     1   3 use HTML::Template::Compiled;
  1         1  
  1         4  
7 1     1   3 use Parse::RecDescent;
  1         2  
  1         7  
8             our $VERSION = '1.002_001'; # TRIAL VERSION
9              
10             my $re = qr# (?:
11             \b(?:eq | ne | ge | le | gt | lt )\b
12             |
13             (?: == | != | <= | >= | > | <)
14             |
15             (?: [0-9]+ )
16             ) #x;
17              
18             my $GRAMMAR = <<'END';
19             expression : paren /^$/ { $return = $item[1] }
20              
21             paren : '(' binary_op ')' { $item[2] }
22             | '(' subexpression ')' { $item[2] }
23             | subexpression { $item[1] }
24             | '(' paren ')' { $item[2] }
25              
26             subexpression : function_call
27             | method_call
28             | var_deref
29             | var
30             | literal
31             |
32              
33             binary_op : paren (op paren { [ $item[2], $item[1] ] })(s)
34             { $return = [ 'SUB_EXPR', $item[1], map { @$_ } @{$item[2]} ] }
35              
36             op : />=?|<=?|!=|==/ { [ 'BIN_OP', $item[1] ] }
37             | /le|ge|eq|ne|lt|gt/ { [ 'BIN_OP', $item[1] ] }
38             | /\|\||or|&&|and/ { [ 'BIN_OP', $item[1] ] }
39             | /[-+*\/%.]/ { [ 'BIN_OP', $item[1] ] }
40              
41             method_call : var '(' args ')' { [ 'METHOD_CALL', $item[1], $item[3] ] }
42              
43             function_call : function_name '(' args ')'
44             { [ 'FUNCTION_CALL', $item[1], $item[3] ] }
45             | function_name ...'(' paren
46             { [ 'FUNCTION_CALL', $item[1], [ $item[3] ] ] }
47             | function_name '(' ')'
48             { [ 'FUNCTION_CALL', $item[1] ] }
49              
50             function_name : /[A-Za-z_][A-Za-z0-9_]*/
51              
52             args :
53              
54             var : /[.\/A-Za-z_][.\/A-Za-z0-9_]*/ { [ 'VAR', $item[1] ] }
55             | /\$[.\/A-Za-z_][.\/A-Za-z0-9_]*/ { [ 'VAR', $item[1] ] }
56              
57             var_deref : var deref(s) { [ 'VAR_DEREF', $item[1], $item[2] ] }
58             | var deref(s) { [ 'VAR_DEREF', $item[1], $item[2] ] }
59              
60             deref : deref_hash | deref_array
61              
62             deref_hash : '{' hash_key '}' { [ 'DEREF_HASH', $item[2] ] }
63              
64             deref_array : '[' array_index ']' { [ 'DEREF_ARRAY', $item[2] ] }
65              
66             hash_key : literal | paren | var
67              
68             array_index : /-?\d+/ | paren | var
69              
70             literal : /-?\d*\.\d+/ { [ 'LITERAL', $item[1] ] }
71             | /-?\d+/ { [ 'LITERAL', $item[1] ] }
72             | { [ 'LITERAL_STRING', $item[1][1], $item[1][2] ] }
73              
74             END
75             my %FUNC = (
76             'sprintf' => sub { sprintf( shift, @_ ); },
77             'substr' => sub {
78             return substr( $_[0], $_[1] ) if @_ == 2;
79             return substr( $_[0], $_[1], $_[2] );
80             },
81             'lc' => sub { lc( $_[0] ); },
82             'lcfirst' => sub { lcfirst( $_[0] ); },
83             'uc' => sub { uc( $_[0] ); },
84             'ucfirst' => sub { ucfirst( $_[0] ); },
85             'length' => sub { length( $_[0] ); },
86             'defined' => sub { defined( $_[0] ); },
87             'abs' => sub { abs( $_[0] ); },
88             'atan2' => sub { atan2( $_[0], $_[1] ); },
89             'cos' => sub { cos( $_[0] ); },
90             'exp' => sub { exp( $_[0] ); },
91             'hex' => sub { hex( $_[0] ); },
92             'int' => sub { int( $_[0] ); },
93             'log' => sub { log( $_[0] ); },
94             'oct' => sub { oct( $_[0] ); },
95             'rand' => sub { rand( $_[0] ); },
96             'sin' => sub { sin( $_[0] ); },
97             'sqrt' => sub { sqrt( $_[0] ); },
98             'srand' => sub { srand( $_[0] ); },
99             );
100             # under construction
101             my $DEFAULT_PARSER;
102             sub parse_expr {
103 27     27 0 75 my ($class, $compiler, $htc, %args) = @_;
104             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\%args], ['args']);
105 27         38 my $string = $args{expr};
106 27   66     54 my $PARSER = $DEFAULT_PARSER ||= Parse::RecDescent->new($GRAMMAR);
107             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$string], ['string']);
108 27         66210 my $tree = $PARSER->expression("( $string )");
109             # warn Data::Dumper->Dump([\$tree], ['tree']);
110 27         223800 my $expr = $class->sub_expression($tree, $compiler, $htc, %args);
111             # warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$expr], ['expr']);
112 27         162 return $expr;
113              
114             }
115              
116             sub bin_op {
117 19     19 0 50 my ($class, $op, $args, $compiler, $htc, %args) = @_;
118 19 50       36 unless (@$args) {
119 0         0 return '';
120             }
121 19         22 my $right = pop @$args;
122 19         50 my $right_expr = $class->sub_expression($right, $compiler, $htc, %args);
123 19         20 my $left_expr = '';
124 19 100       30 if (@$args > 1) {
125 2         2 my $new_op = pop @$args;
126 2         7 my $sub = $class->bin_op($new_op->[1], $args, $compiler, $htc, %args);
127 2         3 $left_expr = $sub;
128             }
129             else {
130 17         38 $left_expr = $class->sub_expression($args->[0], $compiler, $htc, %args);
131             }
132 19         40 my $expr = ' ( ' . $left_expr
133             . ' ' . $op . ' '
134             . $right_expr
135             . ' ) ';
136             # warn __PACKAGE__.':'.__LINE__.": !!! $expr\n";
137 19         36 return $expr;
138             }
139              
140             sub sub_expression {
141 111     111 0 278 my ($class, $tree, $compiler, $htc, %args) = @_;
142 111         144 my ($type, @args) = @$tree;
143             #warn __PACKAGE__.':'.__LINE__.": $type\n";
144             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$tree], ['tree']);
145 111 100       354 if ($type eq 'SUB_EXPR') {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
146 17         17 my $op = pop @args;
147             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$op], ['op']);
148             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@args], ['args']);
149 17         18 my $expr = '';
150 17 50       38 if ($op->[0] eq 'BIN_OP') {
151 17         57 $expr = $class->bin_op($op->[1], [@args], $compiler, $htc, %args);
152             }
153             #warn __PACKAGE__.':'.__LINE__.": $expr\n";
154 17         40 return $expr;
155             }
156             elsif ($type eq 'VAR') {
157 30         132 my $expr = $compiler->parse_var($htc,
158             %args,
159             var => $args[0],
160             );
161             #warn __PACKAGE__.':'.__LINE__.": VAR $expr\n";
162 30         101 return $expr;
163             }
164             elsif ($type eq 'LITERAL') {
165             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@args], ['args']);
166 15         14 my $expr = $args[0];
167 15         39 return $expr;
168             }
169             elsif ($type eq 'LITERAL_STRING') {
170 16         25 my $expr = $args[0] . $args[1] . $args[0];
171 16         39 return $expr;
172             }
173             elsif ($type eq 'METHOD_CALL') {
174             #warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\@args], ['args']);
175 1         2 my ($var, $params) = @args[0,1];
176 1         1 my $method_args = '';
177 1         5 for my $i (0 .. $#$params) {
178 2         6 $method_args .= $class->sub_expression($params->[$i], $compiler, $htc, %args) . ' , ';
179             }
180 1         5 my $expr = $compiler->parse_var($htc,
181             %args,
182             var => $var->[1],
183             method_args => $method_args,
184             );
185             }
186             elsif ($type eq 'VAR_DEREF') {
187 14         19 my ($var, $deref) = @args;
188 14         43 my $str = $class->sub_expression($var, $compiler, $htc, %args);
189 14         27 for my $d (@$deref) {
190 16         43 my $deref_str = $class->sub_expression($d, $compiler, $htc, %args);
191 16         30 $str .= $deref_str;
192             }
193 14         34 return $str;
194             }
195             elsif ($type eq 'DEREF_HASH') {
196 12         16 my ($key) = @args;
197 12         31 my $str = $class->sub_expression($args[0], $compiler, $htc, %args);
198 12         25 $str = '->{' . $str . '}';
199 12         25 return $str;
200             }
201             elsif ($type eq 'DEREF_ARRAY') {
202 4         4 my ($index) = @args;
203 4         4 my $str;
204 4 100       13 if (ref $index) {
    50          
205 2         7 $str = $class->sub_expression($index, $compiler, $htc, %args);
206             }
207             elsif ($index !~ m/-?[0-9]+/) {
208 0         0 die "invalid array index $index";
209             }
210             else {
211 2         3 $str = $index;
212             }
213 4         8 $str = '->[' . $str . ']';
214 4         9 return $str;
215             }
216              
217             elsif ($type eq 'FUNCTION_CALL') {
218 2         3 my $name = shift @args;
219 2 50       2 @args = @{ $args[0] || [] };
  2         7  
220 2         3 my $expr = "$name( ";
221 2         7 for my $i (0 .. $#args) {
222 2         6 $expr .= $class->sub_expression($args[$i], $compiler, $htc, %args) . ' , ';
223             }
224 2         3 $expr .= ")";
225 2         6 return $expr;
226             }
227             }
228              
229             1;
230              
231             __END__