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   42562 use strict; use utf8; use warnings FATAL => 'all';
  98     98   114  
  98     98   2216  
  98         1754  
  98         129  
  98         381  
  98         1737  
  98         103  
  98         2984  
3 98     98   1393 use parent 'DTL::Fast::Replacer';
  98         832  
  98         393  
4              
5 98     98   4911 use DTL::Fast;
  98         133  
  98         3302  
6 98     98   356 use DTL::Fast::Variable;
  98         135  
  98         2513  
7 98     98   33788 use DTL::Fast::Expression::Operator;
  98         160  
  98         2709  
8 98     98   420 use DTL::Fast::Replacer::Replacement;
  98         91  
  98         1706  
9              
10 98     98   54036 use Data::Dumper;
  98         612902  
  98         72356  
11              
12             our %EXPRESSION_CACHE = ();
13             our $EXPRESSION_CACHE_HITS = 0;
14              
15             # @todo cache mechanism via get_expression
16             sub new
17             {
18 1817     1817 0 27681 my( $proto, $expression, %kwargs ) = @_;
19 1817         1437 my $result = undef;
20              
21 1817         4834 $expression =~ s/^\s+|\s+$//xgsi;
22            
23 1817 100 66     7605 if(
      66        
24             not $kwargs{'replacement'} # cache only top-level expressions
25             and not $kwargs{'level'} # same ^^
26             and $EXPRESSION_CACHE{$expression} # has cached expression
27             )
28             {
29 1367         1279 $result = $EXPRESSION_CACHE{$expression};
30 1367         1110 $EXPRESSION_CACHE_HITS++;
31             }
32             else
33             {
34 450         616 $kwargs{'expression'} = $expression;
35 450   100     938 $kwargs{'level'} //= 0;
36            
37 450         1389 my $self = $proto->SUPER::new(%kwargs);
38              
39 450         1177 $self->{'expression'} = $self->_parse_expression(
40             $self->_parse_brackets(
41             $self->backup_strings($expression)
42             )
43             );
44              
45 441         1582 $EXPRESSION_CACHE{$expression} = $result = $self->{'expression'};
46             }
47            
48 1808         4151 return $result;
49             }
50              
51             sub _parse_brackets
52             {
53 450     450   441 my( $self, $expression ) = @_;
54              
55 450         1198 $expression =~ s/\s+/ /xgsi;
56 450         1088 while( $expression =~ s/
57             \(\s*([^()]+)\s*\)
58             /
59 11         60 $self->backup_expression($1)
60             /xge ){};
61            
62 450 100       842 die $self->get_parse_error('unpaired brackets in expression')
63             if $expression =~ /[()]/;
64            
65 449         1074 return $expression;
66             }
67              
68             sub get_parse_error
69             {
70 4     4 0 11 my ($self, $message, @additional) = @_;
71            
72             return $self->SUPER::get_parse_error(
73             $message
74             , @additional
75 4         30 , 'Expression' => $self->{'expression'}
76             );
77             }
78              
79             sub _parse_expression
80             {
81 449     449   450 my( $self, $expression ) = @_;
82            
83 449         432 my $result = undef;
84            
85 449         1128 for( my $level = $self->{'level'}; $level < scalar @DTL::Fast::OPS_RE; $level++ )
86             {
87 2034         2212 my $operators = $DTL::Fast::OPS_RE[$level];
88 2034         1753 my @result = ();
89 2034         37062 my @source = split /
90             (?:^|\s+)
91             ($operators)
92             (?:$|\s+)
93             /six, $expression;
94              
95 2034 100       7278 if( scalar @source > 1 )
96             {
97             # processing operands
98 186         510 while( defined ( my $token = shift @source) )
99             {
100 565 100       926 next if $token eq '';
101            
102 519 100       2883 if( $token =~ /^$operators$/six ) # operation
103             {
104 190         463 push @result, $token;
105             }
106             else
107             {
108 329         934 push @result, $self->get_backup_or_expression($token, $level);
109             }
110             }
111            
112             # processing operators
113 182         416 while( my $token = shift @result )
114             {
115 323 100       466 if( ref $token ) # operand
116             {
117 136         322 $result = $token;
118             }
119             else # operator
120             {
121 187 100 66     735 if(
122             scalar @result # there is a next token
123             and ref $result[0] # it's an operand
124             )
125             {
126 186         209 my $operand = shift @result;
127            
128 186 50 66     528 if( not exists $DTL::Fast::OPS_HANDLERS{$token}
129             and exists $DTL::Fast::KNOWN_OPS_PLAIN{$token}
130             )
131             {
132 31         1472 require Module::Load;
133 31         2037 Module::Load::load($DTL::Fast::KNOWN_OPS_PLAIN{$token});
134 31         398 $DTL::Fast::LOADED_MODULES{$DTL::Fast::KNOWN_OPS_PLAIN{$token}} = time;
135 31         69 $DTL::Fast::OPS_HANDLERS{$token} = $DTL::Fast::KNOWN_OPS_PLAIN{$token};
136             }
137            
138 186   50     481 my $handler = $DTL::Fast::OPS_HANDLERS{$token} || die $self->get_parse_error("there is no processor for $token operator");
139            
140 186 100       1446 if($handler->isa('DTL::Fast::Expression::Operator::Binary'))
    50          
141             {
142 140 100       237 if( defined $result )
143             {
144 139         542 $result = $handler->new( $result, $operand );
145             }
146             else
147             {
148 1   50     12 die $self->get_parse_error(
149             sprintf('binary operator `%s` has no left argument'
150             , $token // 'undef'
151             )
152             );
153             }
154             }
155             elsif( $handler->isa('DTL::Fast::Expression::Operator::Unary') )
156             {
157 46 100       85 if( defined $result )
158             {
159 1   50     12 die $self->get_parse_error(
160             sprintf('unary operator `%s` got left argument'
161             , $token // 'undef'
162             )
163             );
164             }
165             else
166             {
167 45         166 $result = $handler->new( $operand);
168             }
169             }
170             else
171             {
172 0         0 die $self->get_parse_error('Unknown operator handler: '.$handler);
173             }
174             }
175             else # got operator but there is no more operands
176             {
177 1   50     13 die $self->get_parse_error(
178             sprintf('operator `%s` has no right argument'
179             , $token // 'undef'
180             )
181             );
182             }
183             }
184             }
185 179 50       477 last if $result; # parsed level
186             }
187            
188             }
189             return
190 442   66     1583 $result
191             // $self->get_backup_or_variable($expression)
192             ;
193             }
194              
195             1;