File Coverage

blib/lib/Math/Expression.pm
Criterion Covered Total %
statement 333 427 77.9
branch 314 394 79.7
condition 142 180 78.8
subroutine 17 21 80.9
pod 14 17 82.3
total 820 1039 78.9


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