File Coverage

blib/lib/Math/Expression.pm
Criterion Covered Total %
statement 333 429 77.6
branch 314 394 79.7
condition 142 180 78.8
subroutine 17 21 80.9
pod 14 17 82.3
total 820 1041 78.7


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.48 09/06/16 10:48:57
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   966 use strict;
  2         5  
  2         69  
25              
26             package Math::Expression;
27              
28 2     2   12 use Exporter;
  2         4  
  2         77  
29 2     2   904 use POSIX qw(strftime mktime);
  2         11402  
  2         12  
30              
31             # What local variables - visible elsewhere
32 2         8266 use vars qw/
33             @ISA @EXPORT
34 2     2   2526 /;
  2         4  
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.48";
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 30 my $self = shift;
164 18 50 33     78 if(defined $self->{PrintErrFunc} && $self->{PrintErrFunc}) {
165 18         58 $self->{PrintErrFunc}(@_);
166 18         2343 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 2450 my ($self, $name, @value) = @_;
182              
183 1000 50       2076 unless(defined($name)) {
184 0         0 $self->PrintError("Undefined variable name '$name' - need () to force left to right assignment ?");
185             } else {
186 1000 100       2299 if($name =~ /^(.+)\[(\d+)\]$/) {
187 25 50       124 unless(defined($self->{VarHash}->{$1})) {
    100          
    100          
188 2 100       6 if($2 == 0) {
189 1         4 $self->{VarHash}->{$1} = $value[-1];
190             } else {
191 1         6 $self->PrintError("Can only create variable '%s' by setting element 0", $1);
192             }
193 0         0 } elsif($2 > $self->{ArrayMaxIndex}) {
194 1         7 $self->PrintError("Array index %d is too large. Max is %d", $2, $self->{ArrayMaxIndex});
195 0         0 } elsif($2 > @{$self->{VarHash}->{$1}}) {
  22         78  
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         70 $self->{VarHash}->{$1}[$2] = $value[-1];
200             }
201             } else {
202 975         2137 $self->{VarHash}->{$name} = \@value;
203             }
204             }
205              
206 1000         2452 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 1083 my ($self, $expr) = @_;
247              
248 527         957 my @operators = (); # Operators stacked here until needed
249 527         954 my @tree; # Parsed tree ends up here
250             my $newt; # New Token
251 527         926 my $ln = ''; # Last $newt->{oper}
252              
253 527         849 my $operlast = 1; # Operator was last, ie not: var, const, ; ) string flow. Used to idenify monadic operators
254 527         816 my $endAlready = 0;
255 527         926 my $GenSemiColon = 0; # Need to generate a ';'. Always do so after a '}'
256              
257 527         798 while(1) {
258 4815         7461 my $semi = 0;
259 4815         7997 $newt = {};
260              
261             # Lexical part:
262              
263 4815         16367 $expr =~ s/^\s*//;
264 4815         9387 my $EndInput = $expr eq '';
265              
266 4815 100 66     27804 if($GenSemiColon) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
267             # Generate an extra semicolon - after a close brace
268 45         97 $newt->{oper} = ';';
269 45         70 $operlast = 0;
270 45         75 $EndInput = $GenSemiColon = 0;
271             } # End of input string:
272             elsif($EndInput) {
273 1551         2340 $operlast = 0;
274             # First time generate a ';' to terminate a set of statements:
275 1551 100       2781 if($endAlready) {
276 1028         1803 undef $newt;
277             } else {
278 523         1038 $newt->{oper} = 'EOF';
279 523         861 $EndInput = 0;
280             }
281 1551         2273 $endAlready = 1;
282             } # Match integer/float constant:
283             elsif($expr =~ s/^(((\d+(\.\d*)?)|(\.\d+))([ed][-+]?\d+)?)//i) {
284 755         1820 $newt->{oper} = 'const';
285 755         1738 $newt->{val} = $1;
286 755         1373 $newt->{type} = 'num'; # Used in debug/tree-print
287 755         1076 $operlast = 0;
288             } # Match string bounded by ' or "
289             elsif($expr =~ /^(['"])/ and $expr =~ s/^($1)([^$1]*)$1//) {
290 107         289 $newt->{oper} = 'const';
291 107         247 $newt->{val} = $2;
292             # Double quoted, understand some escapes:
293 107 100       329 $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       39  
    100          
294 107         198 $newt->{type} = 'str';
295 107         171 $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         112 $newt->{oper} = '}';
300 45         71 $GenSemiColon = 1;
301 45         66 $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         3807 $newt->{oper} = $1;
305             # Monadic if the previous token was an operator and this one can be monadic:
306 1387 100 100     3879 if($operlast && defined($MonOp{$1})) {
307 69         175 $newt->{oper} = 'M' . $1;
308 69         152 $newt->{monop} = $1; # Monop flag & for error reporting
309             }
310 1387 100       3437 if(defined($MonVarOp{$1})) {
311 25         55 $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     3662 if($ln eq '(' && $1 eq ')') {
316 5         14 push @tree, {oper => 'var', name => 'EmptyList'};
317             } else {
318 1382 100 100     5906 $operlast = 1 unless($1 eq ')' or $1 eq ']');
319             }
320             } # Flow: if/while:
321             elsif($expr =~ s@^(if|while)@@) {
322 51         139 $newt->{oper} = 'flow';
323 51         122 $newt->{flow} = $1;
324 51         86 $operlast = 0;
325             } # Semi-colon:
326             elsif($expr =~ s@^;@@) {
327 167         415 $newt->{oper} = ';';
328 167         268 $operlast = 0;
329             } # Match 'function(', leave '(' in input:
330             elsif($expr =~ s/^([_a-z][\w]*)\(/(/i) {
331 72 100       278 unless($self->{Functions}->{$1}) {
332 1         6 $self->PrintError("When parsing: found unknown function '%s'", $1);
333 1         6 return;
334             }
335 71         157 $newt->{oper} = 'func';
336 71         152 $newt->{fname} = $1;
337 71         120 $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         1744 $newt->{oper} = 'var';
341 634 0       2070 $newt->{name} = defined($1) ? $1 : defined($2) ? $2 : $3;
    50          
342 634         1087 $operlast = 0;
343             } else {
344 1         15 $self->PrintError("Unrecognised input in expression at '%s'", $expr);
345 1         5 return;
346             }
347              
348             # Processed everything ?
349 4813 100 100     14850 if(!@operators && $EndInput) {
350 514         2017 return pop @tree;
351             }
352              
353             # What is new token ?
354 4299 100       9342 $ln = $newt ? $newt->{oper} : '';
355              
356             # Grammatical part
357             # Move what we can from @operators to @tree
358              
359 4299         6513 my $loopb = 0; # Loop buster
360 4299   100     12382 while(@operators || $newt) {
361              
362             # End of input ?
363 5436 50 66     14101 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     23114 if($newt and ($newt->{oper} eq 'var' or $newt->{oper} eq 'const')) {
      66        
374 1496 100       3625 $operators[-1]->{after} = 1 if(@operators);
375              
376 1496         2637 push @tree, $newt;
377 1496         2799 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     18974 if($newt and @operators and $operators[-1]->{oper} eq '(' and $newt->{oper} eq ')') {
      100        
      100        
382 200 50 33     533 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         315 pop @operators;
388 200         524 last; # get next token
389             }
390              
391             # Should have a new node to play with - unless end of string
392 3740 50 66     8713 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         5593 my $NewOpPrec; # EOF is ultra low precedence
401 3740 100       8873 $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     12943 if($newt && @operators) {
407 2100 50       4528 print "Undefined NEWOPrec\n" unless defined $NewOpPrec;
408 2100 50       5132 print "undefeined op-1 oper '$operators[-1]->{oper}'\n" unless(defined $OperPrec{$operators[-1]->{oper}}[0]);
409             }
410 3740 100 66     17499 if($newt && (!@operators or (@operators && $NewOpPrec > $OperPrec{$operators[-1]->{oper}}[0]))) {
      66        
411 1954 100       4535 $operators[-1]->{after} = 1 if(@operators);
412 1954         3214 push @operators, $newt;
413 1954         3602 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     7822 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       114 $operators[-1]->{after} = 1 if(@operators);
419 35         61 push @operators, $newt;
420 35         64 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     7137 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     5049 if($operators[-1]->{oper} eq ';' && !defined $operators[-1]->{after}) {
430 79         120 pop @operators;
431 79         315 next;
432             }
433              
434             # If top op is { & new op is } - pop them:
435 1672 100 66     8743 if(@operators && $newt && $operators[-1]->{oper} eq '{' && $newt->{oper} eq '}') {
      100        
      100        
436 44         66 pop @operators; # Lose the open curly
437              
438             # Unless we uncovered a flow - get next token
439 44 100 66     199 last unless(@operators && $operators[-1]->{oper} eq 'flow');
440              
441 35         71 $newt = undef; # So that we do a last below
442             }
443 1663         2968 my $op = pop @operators;
444 1663         2998 my $func = $op->{oper} eq 'func';
445 1663         3002 my $flow = $op->{oper} eq 'flow';
446 1663         2659 my $monop = defined($op->{monop});
447              
448             # Enough on the tree ?
449 1663 100       4823 unless(@tree >= (($func | $monop | $flow) ? 1 : 2)) {
    100          
450             # ';' are special, don't need operands, also can lose empty ';' nodes
451 523 100 100     3818 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       41 ($expr ne '' ? "'$expr'" : 'end'));
455              
456 11         68 return;
457             }
458              
459             # Push $op to @tree, first give it right & left children taken from the top of @tree
460 1140         2220 $op->{right} = pop @tree;
461 1140 100 100     4116 unless($monop or $func) {
462             # Monadic operators & functions do not have a 'left' child.
463 978         1750 $op->{left} = pop @tree;
464             }
465              
466 1140 100       2575 $op->{oper} = ';' if($op->{oper} eq 'EOF'); # ie join to previous
467 1140         1891 push @tree, $op;
468              
469             $newt = undef
470 1140 100 100     4690 if($newt && $op->{oper} eq '[' && $newt->{oper} eq ']');
      66        
471              
472 1140 100       4959 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 4060 $ChkErrs = 0;
486 2634         4992 return &CheckTreeInt(@_);
487             }
488              
489             # Internal CheckTree
490             sub CheckTreeInt {
491 2634     2634 0 4501 my ($self, $tree) = @_;
492 2634 100       5634 return unless(defined($tree));
493              
494 2621 100 100     12107 return $tree if($tree->{oper} eq 'var' or $tree->{oper} eq 'const');
495              
496 1134         1776 my $ok = 1;
497              
498 1134 50 33     4551 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     2761 if(defined($MonVarOp{$tree->{oper}}) and (!defined($tree->{right}) or ($tree->{right}{oper} ne '[' and $tree->{right}{oper} ne 'var'))) {
      66        
504 1         5 $self->PrintError("Operand to '%s' must be a variable or indexed array element", $tree->{oper});
505 1         3 $ok = 0;
506             }
507              
508 1134 100 100     2909 if($tree->{oper} eq '?' and $tree->{right}{oper} ne ':') {
509 1 50       8 $self->PrintError("Missing ':' operator after '?' operator") unless($ChkErrs);
510 1         3 $ok = 0;
511             }
512              
513 1134 100       2430 if($tree->{oper} ne 'func') {
514 1064 50 66     4194 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       2366 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       2594 if($tree->{oper} eq 'func') {
525 70         121 my $fname = $tree->{fname};
526 70 0 33     251 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       2375 $ChkErrs = 1 unless($ok);
534 1134 100       3196 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 75785 my ($self, $expr) = @_;
543              
544 527         1432 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 47 my ($self, $tree) = @_;
587 9         24 my @res = $self->Eval($tree);
588              
589 9         28 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 15 my ($self, $expr) = @_;
599              
600 1         5 my $tree = $self->Parse($expr);
601 1 50       4 return undef unless($tree);
602 1         4 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 6555 my ($self, $tree) = @_;
611              
612 512         863 $self->{LoopCount} = 0; # Count all loops
613 512         1480 $self->{VarSetFun}($self, '_TIME', time);
614              
615 512         1337 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 6280 my ($self, $tree, $wantlv) = @_;
627              
628 3571 50       7473 return unless(defined($tree));
629              
630 3571         6088 my $oper = $tree->{oper};
631              
632 3571 100       8537 return $tree->{val} if($oper eq 'const');
633 2530 100       6526 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       3306 $self->EvalTree($tree->{right}, defined($InFunLV{$tree->{fname}}))) if($oper eq 'func');
638              
639 1504 100 100     5442 if($oper eq '++' or $oper eq '--') {
640 113         174 my ($right, @right, @left, $index, $name);
641             # The variable is either a simple variable or an indexed array
642 113 100       222 if($tree->{right}->{oper} eq '[') {
643 7         50 $name = $tree->{right}->{left}->{name};
644 7         18 $index = 1;
645              
646 7         25 @left = $self->EvalTree($tree->{right}->{left}, 0);
647              
648 7         110 @right = $self->EvalTree($tree->{right}->{right}, 0);
649 7         66 $index = $right[-1];
650              
651 7 50       46 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       31 $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     57 return undef if($index < 0 or $index > @left); # Out of bounds
659              
660 5         16 $right = $left[$index];
661 5         18 $name = "$name\[$index\]";
662             } else {
663 106         214 @right = $self->EvalTree($tree->{right}, 0);
664 106         752 $right = $right[-1];
665 106         171 $name = $tree->{right}{name};
666             }
667              
668 111 100       235 $oper eq '++' ? $right++ : $right--;
669              
670 111         263 $self->{VarSetFun}($self, $name, ($right));
671              
672 111         270 return $right;
673             }
674              
675             # Monadic operators:
676 1391 100 66     3540 if(!defined($tree->{left}) and defined($tree->{monop})) {
677 68         121 $oper = $tree->{monop};
678              
679             # Evaluate the (RH) operand
680 68         165 my @right = $self->EvalTree($tree->{right}, 0);
681 68         185 my $right = $right[$#right];
682 68 50       180 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       369 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         243 $right = "$1$2$3";
695              
696 68 100       303 return -$right if($oper eq '-');
697 11 50       41 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       2796 if($oper eq ':=') {
709 360         828 my @left = $self->EvalTree($tree->{left}, 1);
710 360         869 my @right = $self->EvalTree($tree->{right}, $wantlv);
711              
712             # Easy case, assigning to one variable, assign the whole array:
713 360 100       1195 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         153 for(my $i = 0; $i <= $#left; $i++) {
718 52 100       140 last if($i > $#right);
719              
720 51 100 100     224 if($i == $#left and $i != $#right) {
721 44         206 $self->{VarSetFun}($self, $left[$i], @right[$i ... $#right]);
722 44         97 last;
723             }
724 7         20 $self->{VarSetFun}($self, $left[$i], $right[$i]);
725             }
726              
727 46         224 return @right;
728             }
729              
730             # Flow control: if/while
731 963 100       1998 if($oper eq 'flow') {
732 78 100       179 if($tree->{flow} eq 'if') {
733             # left is condition, right is body when true
734 56         114 my @left = $self->EvalTree($tree->{left}, 0);
735 56 100       185 return ($left[-1]) ? ($self->EvalTree($tree->{right}, 0))[-1] : 0;
736             }
737 22 50       53 if($tree->{flow} eq 'while') {
738 22         32 my $ret = 0; # Return val, until get something better
739 22 50       51 if( !$self->{PermitLoops}) {
740 0         0 $self->PrintError("Loops not enabled, set property PermitLoops to do so");
741 0         0 return;
742             }
743 22         29 while(1) {
744 121 50 33     469 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         238 my @left = $self->EvalTree($tree->{left}, 0);
750 121 100       281 return $ret unless($left[-1]);
751 99         206 $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         1925 my @left = $self->EvalTree($tree->{left}, $wantlv);
760 885         3327 my $left = $left[$#left];
761 885 50 100     2246 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       1887 $self->EvalTree($tree->{right}{right}, $wantlv) if($oper eq '?');
    100          
772              
773             # Constructing a list of variable names (for assignment):
774 873 100 100     2225 return (@left, $self->EvalTree($tree->{right}, 1)) if($oper eq ',' and $wantlv);
775              
776             # More lazy evaluation:
777 861 100 100     3142 if($oper eq '&&' or $oper eq '||') {
778 10 100 100     40 return 0 if($oper eq '&&' and !$left);
779 8 100 100     39 return 1 if($oper eq '||' and $left);
780              
781 6         14 my @right = $self->EvalTree($tree->{right}, 0);
782              
783 6 100       20 return($right[$#right] ? 1 : 0);
784             }
785              
786             # Everything else is a binary operator, get right side - value(s):
787 851         1837 my @right = $self->EvalTree($tree->{right}, 0);
788 851         1841 my $right = $right[-1];
789              
790 851 100       2081 return (@left, @right) if($oper eq ',');
791 730 100       1797 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       1144 if($oper eq '[') {
796             return undef # Check if the array member could exist; ie have index
797 38 50       181 if($right !~ /^-?\d+$/);
798              
799 38 100       134 @left = $self->{VarGetFun}($self, $left[0]) if($wantlv);
800              
801 38         239 my $index = $right[-1];
802 38 100       108 $index += @left if($index < 0); # Convert -ve index to a +ve one
803              
804 38 100       142 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     92 if($index < 0 || $index > @left);
809              
810 16         64 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       987 if($oper eq '.') {
818             # If one side is undef, treat as empty:
819 14 100       42 $left = "" unless(defined($left));
820 14 50       39 $right = "" unless(defined($right));
821 14 50       53 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         65 return $left . $right;
826             }
827              
828 467 50       936 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       934 return $left lt $right ? 1 : 0 if($oper eq 'lt');
    100          
837 463 100       969 return $left gt $right ? 1 : 0 if($oper eq 'gt');
    100          
838 457 100       917 return $left le $right ? 1 : 0 if($oper eq 'le');
    100          
839 454 100       856 return $left ge $right ? 1 : 0 if($oper eq 'ge');
    100          
840 451 100       895 return $left eq $right ? 1 : 0 if($oper eq 'eq');
    100          
841 449 100       910 return $left ne $right ? 1 : 0 if($oper eq 'ne');
    100          
842              
843 445 50       849 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       1629 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         1266 $left = "$1$2$3";
857             }
858              
859 445 100       1206 unless($right =~ /^([-+]?)0*([\d.]+)([ef][-+]?\d*|)/i) {
860 1 50 33     7 unless($self->{AutoInit} and $right eq '') {
861 1         5 $self->PrintError("Right hand operator to '%s' is not numeric '%s'", $oper, $right);
862 1         5 return;
863             }
864 0         0 $right = 0;
865             } else {
866 444         901 $right = "$1$2$3";
867             }
868              
869 444 100       1120 return $left * $right if($oper eq '*');
870 375 100       788 return $left / $right if($oper eq '/');
871 363 100       727 return $left % $right if($oper eq '%');
872 341 100       884 return $left + $right if($oper eq '+');
873 221 100       491 return $left - $right if($oper eq '-');
874 185 100       366 return $left ** $right if($oper eq '**');
875              
876             # Force return of true/false -- NOT undef
877 181 100       504 return $left > $right ? 1 : 0 if($oper eq '>');
    100          
878 100 100       321 return $left < $right ? 1 : 0 if($oper eq '<');
    100          
879 48 100       107 return $left >= $right ? 1 : 0 if($oper eq '>=');
    100          
880 45 100       100 return $left <= $right ? 1 : 0 if($oper eq '<=');
    100          
881 42 100       160 return $left == $right ? 1 : 0 if($oper eq '==');
    100          
882 5 100       24 return $left != $right ? 1 : 0 if($oper eq '!=');
    100          
883 2 100       13 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 306 my ($self, $tree, $fname, @arglist) = @_;
892              
893             # If there is a user supplied extra function evaluator, try that first:
894 70         123 my $res;
895 70 100 100     295 return $res if(defined($self->{ExtraFuncEval}) && defined($res = $self->{ExtraFuncEval}(@_)));
896              
897 62         629 my $last = $arglist[$#arglist];
898              
899 62 100       157 return int($last) if($fname eq 'int');
900 56 100       132 return abs($last) if($fname eq 'abs');
901              
902             # Round in a +ve direction unless RoundNegatives when round away from zero:
903 50 100       154 return int($last + 0.5 * ($self->{RoundNegatives} ? $last <=> 0 : 1)) if($fname eq 'round');
    100          
904              
905 39 100       139 return split $arglist[0], $arglist[$#arglist] if($fname eq 'split');
906 35 100       84 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       69 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         7 my $s = sprintf $arglist[0], @arglist[1 ... $#arglist];
915 1 50       7 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       111 return mktime(@arglist) if($fname eq 'mktime');
921 30 100       144 return strftime($arglist[0], @arglist[1 ... $#arglist]) if($fname eq 'strftime');
922 28 100       136 return localtime($last) if($fname eq 'localtime');
923              
924 26 100       61 return $self->{VarIsDefFun}($self, $last) if($fname eq 'defined');
925              
926 24 100 100     90 if($fname eq 'pop' or $fname eq 'shift') {
927 7         23 my @a = $self->{VarGetFun}($self, $arglist[0]);
928 7 100       69 my $p = $fname eq 'pop' ? pop(@a) : shift(@a);
929 7         20 $self->{VarSetFun}($self, $last, @a);
930              
931 7         22 return $p;
932             }
933              
934 17 100 100     67 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         13 my @vv = $self->{VarGetFun}($self, $vn);# var value
939 5         52 my @vp = $self->EvalTree($tree->{right}->{right}, 0); # var to push/unshift
940              
941 5 100       24 $fname eq 'push' ? push(@vv, @vp) : unshift(@vv, @vp);
942 5         15 $self->{VarSetFun}($self, $vn, @vv);
943              
944 5         18 return scalar @vv;
945             }
946              
947 12 100       32 return length($last) if($fname eq 'strlen');
948 11 100       38 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       11 if($fname eq 'aindex') {
952 4         8 my $val = $arglist[$#arglist];
953 4         14 for( my $inx = 0; $inx <= $#arglist - 1; $inx++) {
954 25 100       69 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 72 my $class = shift;
968              
969             # What we store about this evaluation environment, default values:
970 2         110 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         9 my $self = bless \%ExprVars => $class;
994 2         12 $self->SetOpt(@_); # Process new options
995              
996 2         6 return $self;
997             }
998              
999             # Set an option in the %template.
1000             sub SetOpt {
1001 5     5 1 122 my $self = shift @_;
1002              
1003 5         18 while($#_ > 0) {
1004 9 50       33 $self->PrintError("Unknown option '$_[0]'") unless(exists($self->{$_[0]}));
1005 9 50       26 $self->PrintError("No value to option '$_[0]'") unless(defined($_[1]));
1006 9         17 $self->{$_[0]} = $_[1];
1007 9         14 shift;shift;
  9         20  
1008             }
1009             }
1010              
1011             1;
1012              
1013             __END__