File Coverage

blib/lib/DTL/Fast/Expression.pm
Criterion Covered Total %
statement 80 81 98.7
branch 23 26 88.4
condition 16 25 64.0
subroutine 13 13 100.0
pod 0 2 0.0
total 132 147 89.8


line stmt bran cond sub pod time code
1             package DTL::Fast::Expression;
2 98     98   39307 use strict;
  98         208  
  98         2377  
3 98     98   1718 use utf8;
  98         210  
  98         419  
4 98     98   2018 use warnings FATAL => 'all';
  98         186  
  98         2729  
5 98     98   1398 use parent 'DTL::Fast::Replacer';
  98         808  
  98         501  
6              
7 98     98   5412 use DTL::Fast;
  98         202  
  98         3317  
8 98     98   525 use DTL::Fast::Variable;
  98         194  
  98         2537  
9 98     98   31819 use DTL::Fast::Expression::Operator;
  98         269  
  98         2818  
10 98     98   586 use DTL::Fast::Replacer::Replacement;
  98         179  
  98         1847  
11              
12 98     98   47147 use Data::Dumper;
  98         616688  
  98         70703  
13              
14             our %EXPRESSION_CACHE = ();
15             our $EXPRESSION_CACHE_HITS = 0;
16              
17             # @todo cache mechanism via get_expression
18             sub new
19             {
20 1817     1817 0 33092 my ( $proto, $expression, %kwargs ) = @_;
21 1817         2787 my $result = undef;
22              
23 1817         5633 $expression =~ s/^\s+|\s+$//xgsi;
24              
25 1817 100 66     8829 if (
      66        
26             not $kwargs{replacement} # cache only top-level expressions
27             and not $kwargs{level} # same ^^
28             and $EXPRESSION_CACHE{$expression} # has cached expression
29             )
30             {
31 1367         2238 $result = $EXPRESSION_CACHE{$expression};
32 1367         1921 $EXPRESSION_CACHE_HITS++;
33             }
34             else
35             {
36 450         926 $kwargs{expression} = $expression;
37 450   100     1294 $kwargs{level} //= 0;
38              
39 450         1549 my $self = $proto->SUPER::new(%kwargs);
40              
41 450         1374 $self->{expression} = $self->_parse_expression(
42             $self->_parse_brackets(
43             $self->backup_strings($expression)
44             )
45             );
46              
47 441         1850 $EXPRESSION_CACHE{$expression} = $result = $self->{expression};
48             }
49              
50 1808         5471 return $result;
51             }
52              
53             sub _parse_brackets
54             {
55 450     450   872 my ( $self, $expression ) = @_;
56              
57 450         1437 $expression =~ s/\s+/ /xgsi;
58 450         1340 while( $expression =~ s/
59             \(\s*([^()]+)\s*\)
60             /
61 11         44 $self->backup_expression($1)
62             /xge ){};
63              
64 450 100       1151 die $self->get_parse_error('unpaired brackets in expression')
65             if ($expression =~ /[()]/);
66              
67 449         1253 return $expression;
68             }
69              
70             sub get_parse_error
71             {
72 4     4 0 13 my ($self, $message, @additional) = @_;
73              
74             return $self->SUPER::get_parse_error(
75             $message
76             , @additional
77             , Expression => $self->{expression}
78 4         23 );
79             }
80              
81             sub _parse_expression
82             {
83 449     449   870 my ( $self, $expression ) = @_;
84              
85 449         697 my $result = undef;
86              
87 449         1290 for (my $level = $self->{level}; $level < scalar @DTL::Fast::OPS_RE; $level++)
88             {
89 2034         3712 my $operators = $DTL::Fast::OPS_RE[$level];
90 2034         3208 my @result = ();
91 2034         39128 my @source = split /
92             (?:^|\s+)
93             ($operators)
94             (?:$|\s+)
95             /six, $expression;
96              
97 2034 100       8793 if (scalar @source > 1)
98             {
99             # processing operands
100 186         604 while( defined ( my $token = shift @source) )
101             {
102 565 100       1336 next if ($token eq '');
103              
104 519 100       3110 if ($token =~ /^$operators$/six) # operation
105             {
106 190         642 push @result, $token;
107             }
108             else
109             {
110 329         1060 push @result, $self->get_backup_or_expression($token, $level);
111             }
112             }
113              
114             # processing operators
115 182         526 while( my $token = shift @result )
116             {
117 323 100       658 if (ref $token) # operand
118             {
119 136         350 $result = $token;
120             }
121             else # operator
122             {
123 187 100 66     801 if (
124             scalar @result # there is a next token
125             and ref $result[0] # it's an operand
126             )
127             {
128 186         312 my $operand = shift @result;
129              
130 186 50 66     607 if (not exists $DTL::Fast::OPS_HANDLERS{$token}
131             and exists $DTL::Fast::KNOWN_OPS_PLAIN{$token}
132             )
133             {
134 31         1087 require Module::Load;
135 31         1751 Module::Load::load($DTL::Fast::KNOWN_OPS_PLAIN{$token});
136 31         409 $DTL::Fast::LOADED_MODULES{$DTL::Fast::KNOWN_OPS_PLAIN{$token}} = time;
137 31         70 $DTL::Fast::OPS_HANDLERS{$token} = $DTL::Fast::KNOWN_OPS_PLAIN{$token};
138             }
139              
140 186   50     501 my $handler = $DTL::Fast::OPS_HANDLERS{$token} || die $self->get_parse_error("there is no processor for $token operator");
141              
142 186 100       1270 if ($handler->isa('DTL::Fast::Expression::Operator::Binary'))
    50          
143             {
144 140 100       304 if (defined $result)
145             {
146 139         520 $result = $handler->new( $result, $operand );
147             }
148             else
149             {
150 1   50     8 die $self->get_parse_error(
151             sprintf('binary operator `%s` has no left argument'
152             , $token // 'undef'
153             )
154             );
155             }
156             }
157             elsif ($handler->isa('DTL::Fast::Expression::Operator::Unary'))
158             {
159 46 100       104 if (defined $result)
160             {
161 1   50     8 die $self->get_parse_error(
162             sprintf('unary operator `%s` got left argument'
163             , $token // 'undef'
164             )
165             );
166             }
167             else
168             {
169 45         151 $result = $handler->new( $operand);
170             }
171             }
172             else
173             {
174 0         0 die $self->get_parse_error('Unknown operator handler: '.$handler);
175             }
176             }
177             else # got operator but there is no more operands
178             {
179 1   50     8 die $self->get_parse_error(
180             sprintf('operator `%s` has no right argument'
181             , $token // 'undef'
182             )
183             );
184             }
185             }
186             }
187 179 50       630 last if ($result); # parsed level
188             }
189              
190             }
191             return
192 442   66     1735 $result
193             // $self->get_backup_or_variable($expression)
194             ;
195             }
196              
197             1;