File Coverage

blib/lib/Math/Expression.pm
Criterion Covered Total %
statement 329 423 77.7
branch 307 388 79.1
condition 140 180 77.7
subroutine 17 21 80.9
pod 14 17 82.3
total 807 1029 78.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # /\
3             # / \ (C) Copyright 2003 Parliament Hill Computers Ltd.
4             # \ / All rights reserved.
5             # \/
6             # . Author: Alain Williams, First written January 2003; updated March 2015
7             # . addw@phcomp.co.uk
8             # .
9             # .
10             #
11             # SCCS: @(#)Expression.pm 1.45 04/11/15 00:37:10
12             #
13             # This module is free software; you can redistribute it and/or modify
14             # it under the same terms as Perl itself. You must preserve this entire copyright
15             # notice in any use or distribution.
16             # The author makes no warranty what so ever that this code works or is fit
17             # for purpose: you are free to use this code on the understanding that any problems
18             # are your responsibility.
19              
20             # Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is
21             # hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and
22             # this permission notice appear in supporting documentation.
23              
24 2     2   1015 use strict;
  2         3  
  2         72  
25              
26             package Math::Expression;
27              
28 2     2   8 use Exporter;
  2         3  
  2         85  
29 2     2   860 use POSIX qw(strftime mktime);
  2         8774  
  2         11  
30              
31             # What local variables - visible elsewhere
32 2         8361 use vars qw/
33             @ISA @EXPORT
34 2     2   1478 /;
  2         2  
35              
36             @ISA = ('Exporter');
37              
38             @EXPORT = qw(
39             &CheckTree
40             &Eval
41             &EvalToScalar
42             &EvalTree
43             &FuncValue
44             &Parse
45             &ParseString
46             &ParseToScalar
47             &SetOpts
48             &VarSetFun
49             &VarSetScalar
50             $Version
51             );
52              
53             our $VERSION = "1.45";
54              
55             # Fundamental to this is a tree of nodes.
56             # Nodes are hashes with members:
57             # oper (var, *, >, ...)
58             # left & right (refs to nodes)
59             # monop (boolean)
60             # name (on var nodes)
61             # fname (on func nodes)
62             # val (on const nodes)
63             # flow (on flow nodes)
64              
65              
66             # Operator precedence, higher means evaluate first.
67             # If precedence values are the same associate to the left.
68             # 2 values, depending on if it is the TopOfStack or JustRead operator - [TOS, JR]. See ':=' which right associates.
69             # Just binary operators makes life easier as well.
70             # Getting the precedence values right is a pain and for things like close paren, non obvious.
71             # Far apart numbers makes adding new ones easier.
72              
73             my %OperPrec = (
74             'var' => [240, 240],
75             'const' => [240, 240],
76             '[' => [70, 230],
77             '++' => [220, 220],
78             '--' => [220, 220],
79             'M-' => [200, 210], # Monadic -
80             'M+' => [200, 210], # Monadic +
81             'M!' => [200, 210],
82             'M~' => [200, 210],
83             '**' => [190, 190],
84             '*' => [180, 180],
85             '/' => [180, 180],
86             '%' => [180, 180],
87             '+' => [170, 170],
88             '-' => [170, 170],
89             '.' => [160, 160],
90             '>' => [150, 150],
91             '<' => [150, 150],
92             '>=' => [150, 150],
93             '<=' => [150, 150],
94             '==' => [150, 150],
95             '!=' => [150, 150],
96             '<>' => [150, 150],
97             'lt' => [150, 150],
98             'gt' => [150, 150],
99             'le' => [150, 150],
100             'ge' => [150, 150],
101             'eq' => [150, 150],
102             'ne' => [150, 150],
103             '&&' => [140, 140],
104             '||' => [130, 130],
105             ':' => [120, 120],
106             '?' => [110, 110],
107             ',' => [100, 101], # Build list 1,2,3,4 as ,L[1]R[,L[2]R[,L[3]R[4]]]
108             '(' => [90, 220],
109             ')' => [90, 90],
110             'func' => [210, 220],
111             ']' => [70, 70],
112             ':=' => [50, 60], # 6 to make := right assosc
113             '}' => [40, 00],
114             'flow' => [30, 40],
115             ';' => [20, 20],
116             '{' => [10, 0],
117             'EOF' => [-50, -50],
118             );
119             # TOS, JR
120             # Nothing special about -ve precedence, just saves renumbering when I got to zero.
121              
122             # Monadic/Unary operators:
123             my %MonOp = (
124             '-' => 20,
125             '+' => 20,
126             '!' => 20,
127             '~' => 20,
128             );
129              
130             # MonVarOp - operate on variables, but treat much like monops:
131             my %MonVarOp = (
132             '++' => 22,
133             '--' => 22,
134             );
135              
136             # Closing operators on opening ones. NOT [ ]
137             my %MatchOp = (
138             '(' => ')',
139             '{' => '}',
140             );
141              
142             my %MatchOpClose = reverse %MatchOp; # Reverse lookup
143              
144             # Inbuilt functions
145             my %InFuns = map { $_ => 1} qw/ abs aindex count defined int join localtime mktime pop printf push round shift split strftime strlen unshift /;
146             # Inbuilt functions that must be given a L value
147             my %InFunLV = map { $_ => 1} qw / defined pop push shift unshift /;
148              
149             # Escape chars recognised:
150             my %escapes = ( n => "\n", r => "\r", t => "\t", '\\' => '\\' );
151              
152             # Default error output function
153             sub PrintError {
154 18     18 0 16 my $self = shift;
155 18 50 33     70 if(defined $self->{PrintErrFunc} && $self->{PrintErrFunc}) {
156 18         31 $self->{PrintErrFunc}(@_);
157 18         2252 return;
158             }
159              
160 0         0 printf STDERR @_;
161 0         0 print STDERR "\n";
162             }
163              
164             # Default function to set a variable value, store as a reference to an array.
165             # Assign to a variable. (Default function.) Args:
166             # 0 Self
167             # 1 Variable name, might look like a[2] in which case set element with last value in arg 2
168             # Don't make an array bigger it already is, except to make it 1 element bigger
169             # 2 Value - an array
170             # Return the value;
171             sub VarSetFun {
172 967     967 1 1245 my ($self, $name, @value) = @_;
173              
174 967 50       1075 unless(defined($name)) {
175 0         0 $self->PrintError("Undefined variable name '$name' - need () to force left to right assignment ?");
176             } else {
177 967 100       1311 if($name =~ /^(.+)\[(\d+)\]$/) {
178 25 100       73 unless(defined($self->{VarHash}->{$1})) {
    100          
    50          
179 2 100       6 if($2 == 0) {
180 1         4 $self->{VarHash}->{$1} = $value[-1];
181             } else {
182 1         4 $self->PrintError("Can only create variable '%s' by setting element 0", $1);
183             }
184 22         61 } elsif($2 > $self->{ArrayMaxIndex}) {
185 1         6 $self->PrintError("Array index %d is too large. Max is %d", $2, $self->{ArrayMaxIndex});
186             } elsif($2 > @{$self->{VarHash}->{$1}}) {
187 0         0 $self->PrintError("Extending array too much, '%s' has %d elements, trying to set element %d", $1, scalar @{$self->{VarHash}->{$1}}, $2);
  0         0  
188            
189             } else {
190 22         38 $self->{VarHash}->{$1}[$2] = $value[-1];
191             }
192             } else {
193 942         1324 $self->{VarHash}->{$name} = \@value;
194             }
195             }
196              
197 967         1620 return @value;
198             }
199              
200             # Set a scalar variable function
201             # 0 Self
202             # 1 Variable name
203             # 2 Value - a scalar
204             # Return the value;
205             sub VarSetScalar {
206 0     0 1 0 my ($self, $name, $value) = @_;
207 0         0 my @arr;
208 0         0 $arr[0] = $value;
209 0         0 $self->{VarSetFun}($self, $name, @arr);
210 0         0 return $value;
211             }
212              
213             # Return the value of a variable - return an array
214             # 0 Self
215             # 1 Variable name
216             sub VarGetFun {
217 0     0 1 0 my ($self, $name) = @_;
218              
219 0 0       0 return '' unless(exists($self->{VarHash}->{$name}));
220 0         0 return @{$self->{VarHash}->{$name}};
  0         0  
221             }
222              
223             # Return 1 if a variable is defined - ie has been assigned to
224             # 0 Self
225             # 1 Variable name
226             sub VarIsDefFun {
227 0     0 1 0 my ($self, $name) = @_;
228              
229 0 0       0 return exists($self->{VarHash}->{$name}) ? 1 : 0;
230             }
231              
232             # Parse a string argument, return a tree that can be evaluated.
233             # Report errors with $ErrFunc.
234             # 0 Self
235             # 1 String argument
236             sub ParseString {
237 503     503 1 400 my ($self, $expr) = @_;
238              
239 503         485 my @operators = (); # Operators stacked here until needed
240 503         323 my @tree; # Parsed tree ends up here
241             my $newt; # New Token
242              
243 503         328 my $operlast = 1; # Operator was last, ie not: var, const, ; ) string flow. Used to idenify monadic operators
244 503         304 my $endAlready = 0;
245 503         309 my $GenSemiColon = 0; # Need to generate a ';'. Always do so after a '}'
246              
247 503         363 while(1) {
248 4646         3384 my $semi = 0;
249 4646         3840 $newt = {};
250              
251             # Lexical part:
252              
253 4646         9600 $expr =~ s/^\s*//;
254 4646         3924 my $EndInput = $expr eq '';
255              
256 4646 100 66     19541 if($GenSemiColon) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
257             # Generate an extra semicolon - after a close brace
258 44         55 $newt->{oper} = ';';
259 44         33 $operlast = 0;
260 44         35 $EndInput = $GenSemiColon = 0;
261             } # End of input string:
262             elsif($EndInput) {
263 1479         910 $operlast = 0;
264             # First time generate a ';' to terminate a set of statements:
265 1479 100       1419 if($endAlready) {
266 980         781 undef $newt;
267             } else {
268 499         532 $newt->{oper} = 'EOF';
269 499         383 $EndInput = 0;
270             }
271 1479         1178 $endAlready = 1;
272             } # Match integer/float constant:
273             elsif($expr =~ s/^(((\d+(\.\d*)?)|(\.\d+))([ed][-+]?\d+)?)//i) {
274 741         851 $newt->{oper} = 'const';
275 741         988 $newt->{val} = $1;
276 741         764 $newt->{type} = 'num'; # Used in debug/tree-print
277 741         567 $operlast = 0;
278             } # Match string bounded by ' or "
279             elsif($expr =~ /^(['"])/ and $expr =~ s/^($1)([^$1]*)$1//) {
280 104         146 $newt->{oper} = 'const';
281 104         158 $newt->{val} = $2;
282             # Double quoted: understand some escapes:
283 104 100       200 $newt->{val} =~ s/\\([nrt\\]|x[\da-fA-F]{2})/length($1) == 1 ? $escapes{$1} : (chr hex '0'.$1)/ge if($1 eq '"');
  4 100       14  
284 104         97 $newt->{type} = 'str';
285 104         80 $operlast = 0;
286             } elsif($expr =~ s/^}//) {
287             # Always need a ';' after this - magic one up to be sure
288             # If not then flow operators screw up.
289 44         69 $newt->{oper} = '}';
290 44         35 $GenSemiColon = 1;
291 44         40 $operlast = 1;
292             } # Match (operators). Need \b after things like 'ne' so that it is not start of var name:
293             elsif($expr =~ s@^(\+\+|--|:=|>=|<=|==|<>|!=|&&|\|\||lt\b|gt\b|le\b|ge\b|eq\b|ne\b|\*\*|[-~!./*%+,<>\?:\(\)\[\]{])@@) {
294 1340         2126 $newt->{oper} = $1;
295             # Monadic if the previous token was an operator and this one can be monadic:
296 1340 100 100     2268 if($operlast && defined($MonOp{$1})) {
297 69         97 $newt->{oper} = 'M' . $1;
298 69         78 $newt->{monop} = $1; # Monop flag & for error reporting
299             }
300 1340 100       1926 if(defined($MonVarOp{$1})) {
301 25         31 $newt->{monop} = $1; # Monop flag & for error reporting
302             }
303              
304 1340 100 100     3976 $operlast = 1 unless($1 eq ')' or $1 eq ']');
305              
306             } # Flow: if/while:
307             elsif($expr =~ s@^(if|while)@@) {
308 50         87 $newt->{oper} = 'flow';
309 50         105 $newt->{flow} = $1;
310 50         69 $operlast = 0;
311             } # Semi-colon:
312             elsif($expr =~ s@^;@@) {
313 166         215 $newt->{oper} = ';';
314 166         126 $operlast = 0;
315             } # Match 'function(', leave '(' in input:
316             elsif($expr =~ s/^([_a-zA-Z][\w]*)\(/(/) {
317 63 100       162 unless($InFuns{$1}) {
318 1         4 $self->PrintError("Unknown function '%s'", $1);
319 1         4 return;
320             }
321 62         103 $newt->{oper} = 'func';
322 62         86 $newt->{fname} = $1;
323 62         49 $operlast = 1; # So that argument can be monadic
324             } # Match VarName or $VarName or $123
325             elsif($expr =~ s/^\$?([_a-z]\w*)//i) {
326 614         850 $newt->{oper} = 'var';
327 614 0       1303 $newt->{name} = defined($1) ? $1 : defined($2) ? $2 : $3;
    50          
328 614         500 $operlast = 0;
329             } else {
330 1         4 $self->PrintError("Unrecognised input in expression at '%s'", $expr);
331 1         4 return;
332             }
333              
334             # Processed everything ?
335 4644 100 100     9030 if(!@operators && $EndInput) {
336 490         1203 return pop @tree;
337             }
338              
339             # Grammatical part
340             # Move what we can from @operators to @tree
341              
342 4154         2853 my $loopb = 0; # Loop buster
343 4154   100     7176 while(@operators || $newt) {
344              
345             # End of input ?
346 5264 50 66     7866 if($EndInput and @operators == 0) {
347 0 0       0 if(@tree != 1) { # There should be one node left - the root
348 0 0       0 $self->PrintError("Expression error - %s",
349             $#tree == -1 ? "it's incomplete" : "missing operator");
350 0         0 return;
351             }
352 0         0 return pop @tree;
353             }
354              
355             # Terminal (var/const). Shift: push it onto the tree:
356 5264 100 100     16334 if($newt and ($newt->{oper} eq 'var' or $newt->{oper} eq 'const')) {
      66        
357 1459 100       2147 $operators[-1]->{after} = 1 if(@operators);
358              
359 1459         1130 push @tree, $newt;
360 1459         1347 last; # get next token
361             } # It must be an operator, which must have a terminal to it's left side:
362              
363             # Eliminate () - where current node is a close bracket
364 3805 100 100     13855 if($newt and @operators and $operators[-1]->{oper} eq '(' and $newt->{oper} eq ')') {
      100        
      100        
365 185 50 33     313 if($EndInput and $#operators != 0) {
366 0         0 $self->PrintError("Unexpected end of expression with unmatched '$operators[-1]->{oper}'");
367 0         0 return;
368             }
369              
370 185         119 pop @operators;
371 185         303 last; # get next token
372             }
373              
374             # Should have a new node to play with - unless end of string
375 3620 50 66     4393 if(!$newt && !$EndInput) {
376 0 0       0 if($loopb++ > 40) {
377 0         0 $self->PrintError("Internal error, infinite loop at: $expr");
378 0         0 return;
379             }
380 0         0 next;
381             }
382              
383 3620         2145 my $NewOpPrec; # EOF is ultra low precedence
384 3620 100       5129 $NewOpPrec = ($newt) ? $OperPrec{$newt->{oper}}[1] : -100; # Just read precedence
385            
386              
387             # If there is a new operator & it is higher precedence than the one at the top of @operators, push it
388             # Also put if @operators is empty
389 3620 100 100     8533 if($newt && @operators) {
390 2046 50       2369 print "Undefined NEWOPrec\n" unless defined $NewOpPrec;
391 2046 50       3188 print "undefeined op-1 oper '$operators[-1]->{oper}'\n" unless(defined $OperPrec{$operators[-1]->{oper}}[0]);
392             }
393 3620 100 66     11691 if($newt && (!@operators or (@operators && $NewOpPrec > $OperPrec{$operators[-1]->{oper}}[0]))) {
      66        
394 1887 100       2940 $operators[-1]->{after} = 1 if(@operators);
395 1887         1342 push @operators, $newt;
396 1887         1684 last; # get next token
397             }
398              
399             # Flows (if/while) must not be reduced unless the newop is ';' '}' 'EOF' - ALSO PUSH
400 1733 100 66     5567 if(@operators && $operators[-1]->{oper} eq 'flow' && $newt && $newt->{oper} ne ';' && $newt->{oper} ne 'EOF' && $newt->{oper} ne '}') {
      66        
      100        
      100        
      100        
401 34 50       56 $operators[-1]->{after} = 1 if(@operators);
402 34         28 push @operators, $newt;
403 34         34 last;
404             }
405              
406             # Reduce, ie where we have everything move operators from @operators to @tree, their operands will be on @tree
407             # Reduce when the new operator precedence is lower than or equal to the one at the top of @operators
408 1699 50 33     5022 if(@operators && $NewOpPrec <= $OperPrec{$operators[-1]->{oper}}[0]) {
409              
410             # One of the pains is a trailing ';', ie nothing following it.
411             # Detect it and junk it
412 1699 100 100     2927 if($operators[-1]->{oper} eq ';' && !defined $operators[-1]->{after}) {
413 78         57 pop @operators;
414 78         239 next;
415             }
416              
417             # If top op is { & new op is } - pop them:
418 1621 100 66     6181 if(@operators && $newt && $operators[-1]->{oper} eq '{' && $newt->{oper} eq '}') {
      100        
      100        
419 43         31 pop @operators; # Lose the open curly
420              
421             # Unless we uncovered a flow - get next token
422 43 100 66     163 last unless(@operators && $operators[-1]->{oper} eq 'flow');
423              
424 34         32 $newt = undef; # So that we do a last below
425             }
426 1612         1257 my $op = pop @operators;
427 1612         1510 my $func = $op->{oper} eq 'func';
428 1612         1157 my $flow = $op->{oper} eq 'flow';
429 1612         1276 my $monop = defined($op->{monop});
430              
431             # Enough on the tree ?
432 1612 100       2782 unless(@tree >= (($func | $monop | $flow) ? 1 : 2)) {
    100          
433             # ';' are special, don't need operands, also can lose empty ';' nodes
434 499 100 100     2710 next if($op->{oper} eq ';' or $op->{oper} eq 'EOF');
435              
436 11 100       29 $self->PrintError("Missing operand to operator '%s' at %s", $op->{oper},
437             ($expr ne '' ? "'$expr'" : 'end'));
438              
439 11         56 return;
440             }
441              
442 1113         1044 $op->{right} = pop @tree;
443 1113 100 100     2655 unless($monop or $func) {
444 960         858 $op->{left} = pop @tree;
445             }
446              
447 1113 100       1416 $op->{oper} = ';' if($op->{oper} eq 'EOF'); # ie join to previous
448 1113         790 push @tree, $op;
449              
450 1113 100 100     3162 $newt = undef
      66        
451             if($newt && $op->{oper} eq '[' && $newt->{oper} eq ']');
452              
453 1113 100       3053 last unless($newt); # get next token
454             }
455             }
456             }
457             }
458              
459             # Check the tree for problems, args:
460             # 0 Self
461             # 1 a tree, return that tree, return undef on error.
462             # Report errors with $ErrFunc.
463             # To prevent a cascade of errors all due to one fault, use $ChkErrs to only print the first one.
464             my $ChkErrs;
465             sub CheckTree {
466 2565     2565 1 1733 $ChkErrs = 0;
467 2565         2528 return &CheckTreeInt(@_);
468             }
469              
470             # Internal CheckTree
471             sub CheckTreeInt {
472 2565     2565 0 1871 my ($self, $tree) = @_;
473 2565 100       3257 return unless(defined($tree));
474              
475 2552 100 100     8447 return $tree if($tree->{oper} eq 'var' or $tree->{oper} eq 'const');
476              
477 1107         694 my $ok = 1;
478              
479 1107 50 33     3154 if(defined($MatchOp{$tree->{oper}}) or defined($MatchOpClose{$tree->{oper}})) {
480 0         0 $self->PrintError("Unmatched bracket '%s'", $tree->{oper});
481 0         0 $ok = 0;
482             }
483              
484 1107 100 33     1615 if(defined($MonVarOp{$tree->{oper}}) and (!defined($tree->{right}) or ($tree->{right}{oper} ne '[' and $tree->{right}{oper} ne 'var'))) {
      66        
485 1         4 $self->PrintError("Operand to '%s' must be a variable or indexed array element", $tree->{oper});
486 1         1 $ok = 0;
487             }
488              
489 1107 100 100     1710 if($tree->{oper} eq '?' and $tree->{right}{oper} ne ':') {
490 1 50       11 $self->PrintError("Missing ':' operator after '?' operator") unless($ChkErrs);
491 1         1 $ok = 0;
492             }
493              
494 1107 100       1481 if($tree->{oper} ne 'func') {
495 1046 50 66     2644 unless((!defined($tree->{left}) and defined($tree->{monop})) or $self->CheckTree($tree->{left})) {
      66        
496 0 0       0 $self->PrintError("Missing LH expression to '%s'", defined($tree->{monop}) ? $tree->{monop} : $tree->{oper}) unless($ChkErrs);
    0          
497 0         0 $ok = 0;
498             }
499             }
500 1107 50       1326 unless(&CheckTree($self, $tree->{right})) {
501 0 0       0 $self->PrintError("Missing RH expression to '%s'", defined($tree->{monop}) ? $tree->{monop} : $tree->{oper}) unless($ChkErrs);
    0          
502 0         0 $ok = 0;
503             }
504              
505 1107 100       1467 if($tree->{oper} eq 'func') {
506 61         62 my $fname = $tree->{fname};
507 61 0 100     380 if(($fname eq 'pop' or $fname eq 'shift' or $fname eq 'push' or $fname eq 'unshift') and
      33        
      66        
508             (!defined($tree->{right}->{oper}) or (($tree->{right}->{oper} ne 'var' and $tree->{right}->{oper} ne ',') and (!defined($tree->{right}->{left}->{oper}) or $tree->{right}->{left}->{oper} ne 'var')))) {
509 0         0 $self->PrintError("First argument to $fname must be a variable");
510 0         0 $ok = 0;
511             }
512             }
513              
514 1107 100       1209 $ChkErrs = 1 unless($ok);
515 1107 100       2046 return $ok ? $tree : undef;
516             }
517              
518             # Parse & check an argument string, return the parsed tree.
519             # Report errors with $ErrFunc.
520             # 0 Self
521             # 1 an expression
522             sub Parse {
523 503     503 1 30539 my ($self, $expr) = @_;
524              
525 503         667 return $self->CheckTree($self->ParseString($expr));
526             }
527              
528             # Print a tree - for debugging purposes. Args:
529             # 0 Self
530             # 1 A tree
531             # Hidden second argument is the initial indent level.
532             sub PrintTree {
533 0     0 0 0 my ($self, $nodp, $dl) = @_;
534              
535 0 0       0 $dl = 0 unless(defined($dl));
536 0         0 $dl++;
537              
538 0 0       0 unless(defined($nodp)) {
539 0         0 print " " x $dl . "UNDEF\n";
540 0         0 return;
541             }
542              
543 0         0 print " " x $dl;
544 0         0 print "nod=$nodp [$nodp->{oper}] P $OperPrec{$nodp->{oper}}[0] ";
545              
546 0 0       0 if($nodp->{oper} eq 'var') {
    0          
547 0         0 print "var($nodp->{name}) \n";
548             } elsif($nodp->{oper} eq 'const') {
549 0         0 print "const($nodp->{val}) \n";
550             } else {
551 0         0 print "\n";
552 0         0 print " " x $dl;print "Desc L \n";
  0         0  
553 0         0 $self->PrintTree($nodp->{left}, $dl);
554              
555 0         0 print " " x $dl;print "op '$nodp->{oper}' P $OperPrec{$nodp->{oper}}[0] at $nodp\n";
  0         0  
556              
557 0         0 print " " x $dl;print "Desc R \n";
  0         0  
558 0         0 $self->PrintTree($nodp->{right}, $dl);
559             }
560             }
561              
562             # Evaluate a tree. Return a scalar.
563             # Args:
564             # 0 Self
565             # 1 The root of a tree.
566             sub EvalToScalar {
567 9     9 1 25 my ($self, $tree) = @_;
568 9         16 my @res = $self->Eval($tree);
569              
570 9         18 return $res[$#res];
571             }
572              
573             # Parse a string, check and evaluate it, return a scalar
574             # Args:
575             # 0 Self
576             # 1 String to evaluate.
577             # Return undef on error.
578             sub ParseToScalar {
579 1     1 1 9 my ($self, $expr) = @_;
580              
581 1         3 my $tree = $self->Parse($expr);
582 1 50       3 return undef unless($tree);
583 1         3 return $self->EvalToScalar($tree);
584             }
585              
586             # Evaluate a tree. The result is an array, if you are expecting a single value it is the last (probably $#'th) element.
587             # Args:
588             # 0 Self
589             # 1 The root of a tree.
590             sub Eval {
591 488     488 1 3572 my ($self, $tree) = @_;
592              
593 488         468 $self->{LoopCount} = 0; # Count all loops
594 488         816 $self->{VarSetFun}($self, '_TIME', time);
595              
596 488         696 return $self->EvalTree($tree, 0);
597             }
598              
599             # Evaluate a tree. The result is an array, if you are expecting a single value it is the last (probably $#'th) element.
600             # Args:
601             # 0 Self
602             # 1 The root of a tree.
603             # 2 Want Lvalue flag -- return variable name rather than it's value
604             # Report errors with the function $PrintErrFunc
605             # Checking undefined values is a pain, assignment of undef & concat undef is OK.
606             sub EvalTree {
607 3504     3504 1 2691 my ($self, $tree, $wantlv) = @_;
608              
609 3504 50       3976 return unless(defined($tree));
610              
611 3504         2883 my $oper = $tree->{oper};
612              
613 3504 100       4942 return $tree->{val} if($oper eq 'const');
614 2480 100       4028 return $wantlv ? $tree->{name} : $self->{VarGetFun}($self, $tree->{name}) if($oper eq 'var');
    100          
615              
616             # Some functions need to be given a lvalue
617 1548 100       1776 return $self->{FuncEval}($self, $tree, $tree->{fname},
618             $self->EvalTree($tree->{right}, defined($InFunLV{$tree->{fname}}))) if($oper eq 'func');
619              
620 1487 100 100     3604 if($oper eq '++' or $oper eq '--') {
621 113         74 my ($right, @right, @left, $index, $name);
622             # The variable is either a simple variable or an indexed array
623 113 100       138 if($tree->{right}->{oper} eq '[') {
624 7         10 $name = $tree->{right}->{left}->{name};
625 7         7 $index = 1;
626              
627 7         9 @left = $self->EvalTree($tree->{right}->{left}, 0);
628              
629 7         45 @right = $self->EvalTree($tree->{right}->{right}, 0);
630 7         26 $index = $right[-1];
631              
632 7 50       25 unless($index =~ /^-?\d+$/) {
633 0         0 $self->PrintError("Array '%s' index is not integer '%s'", $name, $index);
634 0         0 return undef;
635             }
636              
637 7 100       16 $index += @left if($index < 0); # Convert -ve index to a +ve one, will still be -ve if it was very -ve to start with
638              
639 7 100 100     24 return undef if($index < 0 or $index > @left); # Out of bounds
640              
641 5         6 $right = $left[$index];
642 5         9 $name = "$name\[$index\]";
643             } else {
644 106         118 @right = $self->EvalTree($tree->{right}, 0);
645 106         502 $right = $right[-1];
646 106         100 $name = $tree->{right}{name};
647             }
648              
649 111 100       134 $oper eq '++' ? $right++ : $right--;
650              
651 111         136 $self->{VarSetFun}($self, $name, ($right));
652              
653 111         154 return $right;
654             }
655              
656             # Monadic operators:
657 1374 100 66     2218 if(!defined($tree->{left}) and defined($tree->{monop})) {
658 68         59 $oper = $tree->{monop};
659              
660             # Evaluate the (RH) operand
661 68         95 my @right = $self->EvalTree($tree->{right}, 0);
662 68         71 my $right = $right[$#right];
663 68 50       97 unless(defined($right)) {
664 0 0       0 unless($self->{AutoInit}) {
665 0         0 $self->PrintError("Operand to mondaic operator '%s' is not defined", $oper);
666 0         0 return;
667             }
668 0         0 $right = 0; # Monadics are all numeric
669             }
670              
671 68 50       237 unless($right =~ /^([-+]?)0*([\d.]+)([ef][-+]?\d*|)$/i) {
672 0         0 $self->PrintError("Operand to monadic '%s' is not numeric '%s'", $oper, $right);
673 0         0 return;
674             }
675 68         135 $right = "$1$2$3";
676              
677 68 100       221 return -$right if($oper eq '-');
678 11 50       28 return $right if($oper eq '+');
679 0 0       0 return !$right if($oper eq '!');
680 0 0       0 return ~$right if($oper eq '~');
681              
682 0         0 $self->PrintError("Unknown monadic operator when evaluating: '%s'", $oper);
683 0         0 return;
684             }
685              
686             # This is complicated by multiple assignment: (a, b, c) := (1, 2, 3, 4). 'c' is given '(3, 4)'.
687             # Assign the right value to the left node
688             # Where the values list is shorter, leave vars alone: (a, b, c) := (1, 2) does not change c.
689 1306 100       1507 if($oper eq ':=') {
690 352         457 my @left = $self->EvalTree($tree->{left}, 1);
691 352         457 my @right = $self->EvalTree($tree->{right}, $wantlv);
692              
693             # Easy case, assigning to one variable, assign the whole array:
694 352 100       761 return $self->{VarSetFun}($self, @left, @right) if($#right <= 0);
695              
696             # Assign conseq values to conseq variables. The last var gets the rest of the values.
697             # Ignore too many vars.
698 45         81 for(my $i = 0; $i <= $#left; $i++) {
699 51 100       103 last if($i > $#right);
700              
701 50 100 100     151 if($i == $#left and $i != $#right) {
702 43         108 $self->{VarSetFun}($self, $left[$i], @right[$i ... $#right]);
703 43         44 last;
704             }
705 7         13 $self->{VarSetFun}($self, $left[$i], $right[$i]);
706             }
707              
708 45         131 return @right;
709             }
710              
711             # Flow control: if/while
712 954 100       1123 if($oper eq 'flow') {
713 77 100       115 if($tree->{flow} eq 'if') {
714             # left is condition, right is body when true
715 55         108 my @left = $self->EvalTree($tree->{left}, 0);
716 55 100       132 return ($left[-1]) ? ($self->EvalTree($tree->{right}, 0))[-1] : 0;
717             }
718 22 50       35 if($tree->{flow} eq 'while') {
719 22         19 my $ret = 0; # Return val, until get something better
720 22 50       38 if( !$self->{PermitLoops}) {
721 0         0 $self->PrintError("Loops not enabled, set property PermitLoops to do so");
722 0         0 return;
723             }
724 22         15 while(1) {
725 121 50 33     390 if($self->{MaxLoopCount} && ++$self->{LoopCount} > $self->{MaxLoopCount}) {
726 0         0 $self->PrintError("Loop exceeded maximum iterations: MaxLoopCount = $self->{MaxLoopCount}");
727 0         0 return;
728             }
729             # left is loop condition, right is body:
730 121         162 my @left = $self->EvalTree($tree->{left}, 0);
731 121 100       180 return $ret unless($left[-1]);
732 99         151 $ret = ($self->EvalTree($tree->{right}, 0))[-1];
733             }
734 0         0 return $ret;
735             }
736             }
737              
738             # Evaluate left - may be able to avoid evaluating right.
739             # Take care to avoid evaluating a tree twice, not just inefficient but nasty side effects with ++ & -- operators
740 877         1139 my @left = $self->EvalTree($tree->{left}, $wantlv);
741 877         1917 my $left = $left[$#left];
742 877 50 100     1354 if(!defined($left) and $oper ne ',' and $oper ne '.' and $oper ne ';') {
      66        
      33        
743 0 0       0 unless($self->{AutoInit}) {
744 0         0 $self->PrintError("Left value to operator '%s' is not defined", $oper);
745 0         0 return;
746             }
747 0         0 $left = ''; # Set to the empty string
748             }
749              
750             # Lazy evaluation:
751 877 100       1072 return $left ? $self->EvalTree($tree->{right}{left}, $wantlv) :
    100          
752             $self->EvalTree($tree->{right}{right}, $wantlv) if($oper eq '?');
753              
754             # Constructing a list of variable names (for assignment):
755 865 100 100     1360 return (@left, $self->EvalTree($tree->{right}, 1)) if($oper eq ',' and $wantlv);
756              
757             # More lazy evaluation:
758 854 100 100     2152 if($oper eq '&&' or $oper eq '||') {
759 10 100 100     37 return 0 if($oper eq '&&' and !$left);
760 8 100 100     30 return 1 if($oper eq '||' and $left);
761              
762 6         12 my @right = $self->EvalTree($tree->{right}, 0);
763              
764 6 100       20 return($right[$#right] ? 1 : 0);
765             }
766              
767             # Everything else is a binary operator, get right side - value(s):
768 844         1079 my @right = $self->EvalTree($tree->{right}, 0);
769 844         892 my $right = $right[-1];
770              
771 844 100       1195 return (@left, @right) if($oper eq ',');
772 728 100       1040 return @right if($oper eq ';');
773              
774             # Array index. Beware: works differently depending on $wantlv.
775             # Because when $wantlv it is the array name, not its contents
776 518 100       566 if($oper eq '[') {
777             return undef # Check if the array member could exist; ie have index
778 38 50       109 if($right !~ /^-?\d+$/);
779              
780 38 100       73 @left = $self->{VarGetFun}($self, $left[0]) if($wantlv);
781              
782 38         126 my $index = $right[-1];
783 38 100       65 $index += @left if($index < 0); # Convert -ve index to a +ve one
784              
785 38 100       80 return "$left\[$index]" # Return var[index] for assignment
786             if($wantlv);
787              
788             return undef # Check if the array member exists
789 18 100 100     60 if($index < 0 || $index > @left);
790              
791 16         43 return $left[$index];
792             }
793              
794              
795             # Everything else just takes a simple (non array) value, use last value in a list which is in $right.
796             # It is OK to concat undef.
797              
798 480 100       529 if($oper eq '.') {
799             # If one side is undef, treat as empty:
800 14 100       26 $left = "" unless(defined($left));
801 14 50       21 $right = "" unless(defined($right));
802 14 50       29 if(length($left) + length($right) > $self->{StringMaxLength}) {
803 0         0 $self->PrintError("Joined string would exceed maximum allowed %d", $self->{StringMaxLength});
804 0         0 return "";
805             }
806 14         47 return $left . $right;
807             }
808              
809 466 50       566 unless(defined($right)) {
810 0 0       0 unless($self->{AutoInit}) {
811 0         0 $self->PrintError("Right value to operator '%s' is not defined", $oper);
812 0         0 return;
813             }
814 0         0 $right = '';
815             }
816              
817 466 100       531 return $left lt $right ? 1 : 0 if($oper eq 'lt');
    100          
818 462 100       561 return $left gt $right ? 1 : 0 if($oper eq 'gt');
    100          
819 456 100       547 return $left le $right ? 1 : 0 if($oper eq 'le');
    100          
820 453 100       491 return $left ge $right ? 1 : 0 if($oper eq 'ge');
    100          
821 450 100       461 return $left eq $right ? 1 : 0 if($oper eq 'eq');
    100          
822 448 100       468 return $left ne $right ? 1 : 0 if($oper eq 'ne');
    100          
823              
824 444 50       474 return ($left, $right) if($oper eq ':'); # Should not be used, done in '?'
825             # return $left ? $right[0] : $right[1] if($oper eq '?'); # Non lazy version
826              
827             # Everthing else is an arithmetic operator, check for left & right being numeric. NB: '-' 'cos may be -ve.
828             # Returning undef may result in a cascade of errors.
829             # Perl would treat 012 as an octal number, that would confuse most people, convert to a decimal interpretation.
830 444 50       1207 unless($left =~ /^([-+]?)0*([\d.]+)([ef][-+]?\d*|)/i) {
831 0 0 0     0 unless($self->{AutoInit} and $left eq '') {
832 0         0 $self->PrintError("Left hand operator to '%s' is not numeric '%s'", $oper, $left);
833 0         0 return;
834             }
835 0         0 $left = 0;
836             } else {
837 444         793 $left = "$1$2$3";
838             }
839              
840 444 100       784 unless($right =~ /^([-+]?)0*([\d.]+)([ef][-+]?\d*|)/i) {
841 1 50 33     4 unless($self->{AutoInit} and $right eq '') {
842 1         2 $self->PrintError("Right hand operator to '%s' is not numeric '%s'", $oper, $right);
843 1         5 return;
844             }
845 0         0 $right = 0;
846             } else {
847 443         517 $right = "$1$2$3";
848             }
849              
850 443 100       721 return $left * $right if($oper eq '*');
851 374 100       455 return $left / $right if($oper eq '/');
852 362 100       489 return $left % $right if($oper eq '%');
853 340 100       619 return $left + $right if($oper eq '+');
854 220 100       337 return $left - $right if($oper eq '-');
855 185 100       226 return $left ** $right if($oper eq '**');
856              
857             # Force return of true/false -- NOT undef
858 181 100       365 return $left > $right ? 1 : 0 if($oper eq '>');
    100          
859 100 100       235 return $left < $right ? 1 : 0 if($oper eq '<');
    100          
860 48 100       75 return $left >= $right ? 1 : 0 if($oper eq '>=');
    100          
861 45 100       69 return $left <= $right ? 1 : 0 if($oper eq '<=');
    100          
862 42 100       120 return $left == $right ? 1 : 0 if($oper eq '==');
    100          
863 5 100       18 return $left != $right ? 1 : 0 if($oper eq '!=');
    100          
864 2 100       10 return $left != $right ? 1 : 0 if($oper eq '<>');
    50          
865              
866 0         0 $self->PrintError("Unknown operator when evaluating: '%s'", $oper);
867 0         0 return;
868             }
869              
870             # Evaluate a function:
871             sub FuncValue {
872 61     61 1 156 my ($self, $tree, $fname, @arglist) = @_;
873              
874             # If there is a user supplied extra function evaluator, try that first:
875 61         50 my $res;
876 61 50 33     110 return $res if(defined($self->{ExtraFuncEval}) && defined($res = $self->{ExtraFuncEval}(@_)));
877              
878 61         60 my $last = $arglist[$#arglist];
879              
880 61 100       110 return int($last) if($fname eq 'int');
881 55 100       82 return abs($last) if($fname eq 'abs');
882              
883             # Round in a +ve direction unless RoundNegatives when round away from zero:
884 49 100       102 return int($last + 0.5 * ($self->{RoundNegatives} ? $last <=> 0 : 1)) if($fname eq 'round');
    100          
885              
886 38 100       91 return split $arglist[0], $arglist[$#arglist] if($fname eq 'split');
887 34 100       56 return join $arglist[0], @arglist[1 ... $#arglist] if($fname eq 'join');
888              
889             # Beware: could exceed max length with: printf("%2000s", "foo");
890 31 100       37 if($fname eq 'printf') {
891 1 50       4 unless($self->{EnablePrintf}) {
892 0         0 $self->PrintError("Function 'printf' not enabled");
893 0         0 return "";
894             }
895 1         11 my $s = sprintf $arglist[0], @arglist[1 ... $#arglist];
896 1 50       7 return $s if(length($s) <= $self->{StringMaxLength});
897 0         0 $self->PrintError("String would exceed maximum allowed %d", $self->{StringMaxLength});
898 0         0 return "";
899             }
900              
901 30 100       63 return mktime(@arglist) if($fname eq 'mktime');
902 29 100       82 return strftime($arglist[0], @arglist[1 ... $#arglist]) if($fname eq 'strftime');
903 27 100       240 return localtime($last) if($fname eq 'localtime');
904              
905 25 100       35 return $self->{VarIsDefFun}($self, $last) if($fname eq 'defined');
906              
907 23 100 100     70 if($fname eq 'pop' or $fname eq 'shift') {
908 7         14 my @a = $self->{VarGetFun}($self, $arglist[0]);
909 7 100       43 my $p = $fname eq 'pop' ? pop(@a) : shift(@a);
910 7         10 $self->{VarSetFun}($self, $last, @a);
911              
912 7         15 return $p;
913             }
914              
915 16 100 100     52 if($fname eq 'push' or $fname eq 'unshift') {
916             # Evaluate right->right and push/unshift that
917 4         5 my $vn = shift @arglist; # var name
918              
919 4         8 my @vv = $self->{VarGetFun}($self, $vn);# var value
920 4         35 my @vp = $self->EvalTree($tree->{right}->{right}, 0); # var to push/unshift
921              
922 4 100       13 $fname eq 'push' ? push(@vv, @vp) : unshift(@vv, @vp);
923 4         8 $self->{VarSetFun}($self, $vn, @vv);
924              
925 4         10 return scalar @vv;
926             }
927              
928 12 100       21 return length($last) if($fname eq 'strlen');
929 11 100       35 return scalar @arglist if($fname eq 'count');
930              
931             # aindex(array, val) returns index (from 0) of val in array, -1 on error
932 4 50       8 if($fname eq 'aindex') {
933 4         5 my $val = $arglist[$#arglist];
934 4         17 for( my $inx = 0; $inx <= $#arglist - 1; $inx++) {
935 25 100       57 return $inx if($val eq $arglist[$inx]);
936             }
937 1         5 return -1;
938             }
939              
940 0         0 $self->PrintError("Unknown function '$fname'");
941              
942 0         0 return '';
943             }
944              
945             # Create a new parse/evalutation object.
946             # Initialise default options.
947             sub new {
948 2     2 1 40 my $class = shift;
949              
950             # What we store about this evaluation environment, default values:
951 2         34 my %ExprVars = (
952             PrintErrFunc => '', # Printf errors
953             VarHash => {( # Variable hash
954             EmptyArray => [()],
955             EmptyList => [()],
956             )},
957             VarGetFun => \&VarGetFun, # Get a variable - function
958             VarIsDefFun => \&VarIsDefFun, # Is a variable defined - function
959             VarSetFun => \&VarSetFun, # Set an array variable - function
960             VarSetScalar => \&VarSetScalar, # Set a scalar variable - function
961             FuncEval => \&FuncValue, # Evaluate - function
962             AutoInit => 0, # If true auto initialise variables
963             ExtraFuncEval => undef, # User supplied extra function evaluator function
964             RoundNegatives => 0, # Round behaves differently with -ve numbers
965             PermitLoops => 0, # Are loops allowed
966             MaxLoopCount => 50, # Max # all loops
967             ArrayMaxIndex => 100, # Max index of an array
968             StringMaxLength => 1000, # Max string length
969             EnablePrintf => 0, # Enable printf function
970              
971             );
972              
973 2         6 my $self = bless \%ExprVars => $class;
974 2         9 $self->SetOpt(@_); # Process new options
975              
976 2         5 return $self;
977             }
978              
979             # Set an option in the %template.
980             sub SetOpt {
981 5     5 1 191 my $self = shift @_;
982              
983 5         17 while($#_ > 0) {
984 8 50       22 $self->PrintError("Unknown option '$_[0]'") unless(exists($self->{$_[0]}));
985 8 50       15 $self->PrintError("No value to option '$_[0]'") unless(defined($_[1]));
986 8         10 $self->{$_[0]} = $_[1];
987 8         6 shift;shift;
  8         12  
988             }
989             }
990              
991             1;
992              
993             __END__