Here is a still larger version with parenthesis, function and variable handling. There could be a problem with expressions like 2*(1-2), which is reduced to 2*-1, an invalid expression. To combat that the subtraction operator is changed internally to # (so it could not be used in function names, as @ is forbidden, used as the power operator). After the replacement the negative sign is not considered an operator but part of a number.
The script also recognizes 3 functions, abs(), sqrt() and floor(), as examples. You can add as many to the list as you wish, even your private functions of one parameter. (If a function name contains another, it has to be listed first, like abs0, MyAbs, ABS.)
Constants might conflict with function names, like e is in Ceil(). To avoid problems, constants have to be in [ ], like [e] or [pi]. Now these can be any variable name, defined in the function Eval(). (Some vars are used, like x, i, j, L,and R. If you want to use them in expression, rename the ones used by the function to some obscure string, like appending a $ sign to them.)
Bitwise operators (~,&,|,^,<<,>>) can be easily added, just follow the pattern of the other operators. Still missing is multi parameter functions, like round([pi],3). It would require handling the "," as operator, which is not hard, but seldom needed.
; AHK 1.0.37
; evaluate arithmetic expressions containing
; unary +,- (-2*3; +3)
; +,-(or # = subtract); *,/,%(= mod); **(or @ = power)
; (,); [var] (like [pi],[e]); abs(),sqrt(),floor()
Eval(x) ; top-level process
{
pi = 3.141592653589793
e = 2.718281828459045 ; add below your constants
StringReplace x, x, %A_Space%,, All ; remove white space
StringReplace x, x, %A_Tab%,, All
StringReplace x, x, -, #, All ; # = subtraction
StringReplace x, x, (#, (0#, All ; (-x -> (0-x
If (Asc(x) = Asc("#"))
x = 0%x% ; leading -x -> 0-x
StringReplace x, x, (+, (, All ; (+x -> (x
If (Asc(x) = Asc("+"))
StringTrimLeft x, x, 1 ; leading +x -> x
StringReplace x, x, **, @, All ; ** -> @ for easier process
Loop
{ ; replace constants
StringGetPos i, x, [ ; find [
IfLess i,0, Break
StringGetPos j, x, ], L, i+1 ; closest ]
StringMid y, x, i+2, j-i-1 ; variable in []
StringLeft L, x, i
StringTrimLeft R, x, j+1
x := L . %y% . R ; replace [var] with value of var
}
Loop
{ ; finding an innermost (..)
StringGetPos i, x, (, R ; rightmost (
IfLess i,0, Break
StringGetPos j, x, ), L, i+1 ; closest )
StringMid y, x, i+2, j-i-1 ; expression in ()
StringLeft L, x, i
StringTrimLeft R, x, j+1
x := L . Eval@(y) . R ; replace (x) with value of x
}
Return Eval@(x)
}
Eval@(x)
{
StringGetPos i, x, +, R ; i = -1 if no + is found
StringGetPos j, x, #, R
If (i > j)
Return Left(x,i)+Right(x,i)
If (j > i) ; i = j only if no + or - found
Return Left(x,j)-Right(x,j)
StringGetPos i, x, *, R
StringGetPos j, x, /, R
StringGetPos k, x,`%, R
If (i > j && i > k)
Return Left(x,i)*Right(x,i)
If (j > i && j > k)
Return Left(x,j)/Right(x,j)
If (k > i && k > j)
Return Mod(Left(x,k),Right(x,k))
StringGetPos i, x, @, R ; power
If (i >= 0)
Return Left(x,i)**Right(x,i)
StringGetPos i1, x, abs, R ; no more operators
StringGetPos i2, x, sqrt, R ; look for functions
StringGetPos i3, x, floor, R ; insert further functions below
m := Max1(i1,i2,i3)
If (m = i1) ; apply the rightmost function
Return abs(Right(x,i1+2)) ; only one function is applied
Else If (m = i2) ; in one recursion
Return sqrt(Right(x,i2+3))
Else If (m = i3)
Return floor(Right(x,i3+4)) ; offset j + StrLen(func) - 2
Return x
}
Left(x,i)
{
StringLeft x, x, i
Return Eval@(x)
}
Right(x,i)
{
StringTrimLeft x, x, i+1
Return Eval@(x)
}
Max1(x0,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="",x10="",x11="",x12="",x13="",x14="",x15="",x16="",x17="",x18="",x19="",x20="")
{
x := x0
Loop 20
{
IfEqual x%A_Index%,, Break
IfGreater x%A_Index%, %x%
x := x%A_Index%
}
IfLess x,0, Return -2 ; prevent match with -1
Return %x%
}
The script grew quite complex, so there might be some bugs left. Please experiment with it and report any abnormal behavior.