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   63747 use strict; use utf8; use warnings FATAL => 'all';
  98     98   191  
  98     98   2694  
  98         3130  
  98         201  
  98         492  
  98         2483  
  98         169  
  98         3575  
3 98     98   2452 use parent 'DTL::Fast::Replacer';
  98         1108  
  98         624  
4              
5 98     98   5684 use DTL::Fast;
  98         194  
  98         4193  
6 98     98   532 use DTL::Fast::Variable;
  98         204  
  98         3108  
7 98     98   54544 use DTL::Fast::Expression::Operator;
  98         288  
  98         3230  
8 98     98   543 use DTL::Fast::Replacer::Replacement;
  98         163  
  98         2407  
9              
10 98     98   96336 use Data::Dumper;
  98         932488  
  98         103220  
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 31090 my( $proto, $expression, %kwargs ) = @_;
19 1817         2116 my $result = undef;
20              
21 1817         6939 $expression =~ s/^\s+|\s+$//xgsi;
22            
23 1817 100 66     10664 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         2087 $result = $EXPRESSION_CACHE{$expression};
30 1367         1845 $EXPRESSION_CACHE_HITS++;
31             }
32             else
33             {
34 450         930 $kwargs{'expression'} = $expression;
35 450   100     1254 $kwargs{'level'} //= 0;
36            
37 450         1745 my $self = $proto->SUPER::new(%kwargs);
38              
39 450         1616 $self->{'expression'} = $self->_parse_expression(
40             $self->_parse_brackets(
41             $self->backup_strings($expression)
42             )
43             );
44              
45 441         2367 $EXPRESSION_CACHE{$expression} = $result = $self->{'expression'};
46             }
47            
48 1808         6587 return $result;
49             }
50              
51             sub _parse_brackets
52             {
53 450     450   724 my( $self, $expression ) = @_;
54              
55 450         1421 $expression =~ s/\s+/ /xgsi;
56 450         1360 while( $expression =~ s/
57             \(\s*([^()]+)\s*\)
58             /
59 11         106 $self->backup_expression($1)
60             /xge ){};
61            
62 450 100       1254 die $self->get_parse_error('unpaired brackets in expression')
63             if $expression =~ /[()]/;
64            
65 449         1491 return $expression;
66             }
67              
68             sub get_parse_error
69             {
70 4     4 0 10 my ($self, $message, @additional) = @_;
71            
72             return $self->SUPER::get_parse_error(
73             $message
74             , @additional
75 4         20 , 'Expression' => $self->{'expression'}
76             );
77             }
78              
79             sub _parse_expression
80             {
81 449     449   694 my( $self, $expression ) = @_;
82            
83 449         607 my $result = undef;
84            
85 449         1475 for( my $level = $self->{'level'}; $level < scalar @DTL::Fast::OPS_RE; $level++ )
86             {
87 2034         3390 my $operators = $DTL::Fast::OPS_RE[$level];
88 2034         2967 my @result = ();
89 2034         52710 my @source = split /
90             (?:^|\s+)
91             ($operators)
92             (?:$|\s+)
93             /six, $expression;
94              
95 2034 100       11488 if( scalar @source > 1 )
96             {
97             # processing operands
98 186         535 while( defined ( my $token = shift @source) )
99             {
100 565 100       1330 next if $token eq '';
101            
102 519 100       3798 if( $token =~ /^$operators$/six ) # operation
103             {
104 190         743 push @result, $token;
105             }
106             else
107             {
108 329         1171 push @result, $self->get_backup_or_expression($token, $level);
109             }
110             }
111            
112             # processing operators
113 182         507 while( my $token = shift @result )
114             {
115 323 100       715 if( ref $token ) # operand
116             {
117 136         396 $result = $token;
118             }
119             else # operator
120             {
121 187 100 66     840 if(
122             scalar @result # there is a next token
123             and ref $result[0] # it's an operand
124             )
125             {
126 186         278 my $operand = shift @result;
127            
128 186 50 66     650 if( not exists $DTL::Fast::OPS_HANDLERS{$token}
129             and exists $DTL::Fast::KNOWN_OPS_PLAIN{$token}
130             )
131             {
132 31         2139 require Module::Load;
133 31         2593 Module::Load::load($DTL::Fast::KNOWN_OPS_PLAIN{$token});
134 31         421 $DTL::Fast::LOADED_MODULES{$DTL::Fast::KNOWN_OPS_PLAIN{$token}} = time;
135 31         73 $DTL::Fast::OPS_HANDLERS{$token} = $DTL::Fast::KNOWN_OPS_PLAIN{$token};
136             }
137            
138 186   50     865 my $handler = $DTL::Fast::OPS_HANDLERS{$token} || die $self->get_parse_error("there is no processor for $token operator");
139            
140 186 100       1738 if($handler->isa('DTL::Fast::Expression::Operator::Binary'))
    50          
141             {
142 140 100       364 if( defined $result )
143             {
144 139         675 $result = $handler->new( $result, $operand );
145             }
146             else
147             {
148 1   50     9 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       102 if( defined $result )
158             {
159 1   50     9 die $self->get_parse_error(
160             sprintf('unary operator `%s` got left argument'
161             , $token // 'undef'
162             )
163             );
164             }
165             else
166             {
167 45         193 $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     9 die $self->get_parse_error(
178             sprintf('operator `%s` has no right argument'
179             , $token // 'undef'
180             )
181             );
182             }
183             }
184             }
185 179 50       622 last if $result; # parsed level
186             }
187            
188             }
189             return
190 442   66     2139 $result
191             // $self->get_backup_or_variable($expression)
192             ;
193             }
194              
195             1;