| 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__ |