Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

# [func] calc() - math expression evaluation incl brackets

7 replies to this topic
• Members
• 872 posts
• Last active: Mar 19 2013 04:42 PM
• Joined: 23 Nov 2007
This very compacted function (its not perfect - i know) allows calculating expressions given by a string. it also supports the following functions: sqrt, log, ln, exp, sin, cos, tan, asin, acos, atan, rad, deg, abs, fib, gcb, min, max, sgn, **, ^, *, /, //, +, -, %, pi, e.

it may be extended more or less easily. its just another evaluating function. there are better alternatives (but also more complex) like the ones from laszlo (monster, eval, and friends).

basically i wrote this one to see how far an expession evaluating function can be compacted and still having a fair amount of functionality.

```; dRs CALC! - http://www.opensource.org/licenses/mit-license.html
term1 := "10 * (10 + 12) / (5 * (8 + 2 * (1 + 3)) * (50 + 2))"
term2 := "1 * 2-1 * sin( cos(pi) )"
term3 := "sqrt(2^2/2)*(2**(1/2))"
term4 := "6 % 4"
term5 := "( 1 + 1 + 1 ) !"
term6 := "fib(6)"

msgbox % term1 " = " calc( term1 )
. "`n" term2 " = " calc( term2 )
. "`n" term3 " = " calc( term3 )
. "`n" term4 " = " calc( term4 )
. "`n" term5 " = " calc( term5 )
. "`n" term6 " = " calc( term6 )
exitapp

calc( t, t0="", t1="", t2="" )
{   ; c-labeled functions by Laszlo: http://www.autohotkey.com/forum/topic17058.html
static f := "sqrt|log|ln|exp|sin|cos|tan|asin|acos|atan|rad|deg|abs", c := "fib|gcb|min|max|sgn"
o := "\*\*|\^|\*|/|//|\+|\-|%", pi:="pi", e:="e"
if ( t0 = "fib"  && t1 != "" && t2 != "" ) {
a := 0, b := 1
Loop % abs(t1)-1
c := b, b += a, a := c
return t1=0 ? 0 : t1>0 || t1&1 ? b : -b
} else if ( t0 != "" && RegExMatch( t0, "(" f "|" c "|" o "|!)" ) && t1 != "" && t2 != "" )
return t0 == "**" ? t1 ** t2 : t0 == "^" ? t1 ** t2
: t0 == "*" ? t1 * t2   : t0 == "/" ? t1 / t2 : t0 == "+" ? t1 + t2 : t0 == "-" ? t1 - t2
: t0 == "//" ? t1 // t2 : t0 == "%" ? Mod( t1, t2 ) : t0 = "abs" ? abs( calc( t1 ) )
: t0 == "!" ? ( t1 < 2 ? 1 : t1 * calc( t, t0, t1-1, 0 ) )
: t0 = "log" ? log( calc( t1 ) ) : t0 = "ln" ? ln( calc ( t1 ) )
: t0 = "sqrt" ? sqrt( calc( t1 ) ) : t0 = "exp" ? Exp( calc ( t1 ) )
: t0 = "rad" ? calc( calc( t1 ) "* pi / 180" ) : t0 = "deg" ? calc( calc( t1 ) "* 180 / pi" )
: t0 = "sin" ? sin( calc( "rad(" t1 ")" ) ) : t0 = "asin" ? asin( calc( "rad(" t1 ")" ) )
: t0 = "cos" ? cos( calc( "rad(" t1 ")" ) ) : t0 = "acos" ? acos( calc( "rad(" t1 ")" ) )
: t0 = "tan" ? tan( calc( "rad(" t1 ")" ) ) : t0 = "atan" ? atan( calc( "rad(" t1 ")" ) )
: t0 = "min" ? ( t1 < t2 ? t1 : t2 ) : t0 = "max" ? ( t1 < t2 ? t2 : t1 )
: t0 = "gcd" ? ( t2 = 0 ? abs(t1) : calc( t, t0, calc( t1 "%" t2 ) ) )
: t0 = "sgn" ? (t1>0)-(t1<0) : 0

t := RegExReplace( t, "\s*", "" )
while ( RegExMatch( t, "i)" f "|" c "|" o "|" pi "|" e "|!" ) )
if ( RegExMatch( t, "i)\b" pi "\b" ) )
t := RegExReplace( t, "i)\b" pi "\b", 4 * atan(1) )
else if ( RegExMatch( t, "i)\b" e "\b" ) )
t := RegExReplace( t, "i)\b" e "\b", 2.718281828459045 )
else if ( RegExMatch( t, "i)(" f "|" c ").*", s )
&& RegExMatch( s, "(?>[^\(\)]*)\((?>[^\(\)]*)(?R)*(?>[^\(\)]*)\)", m )
&& RegExMatch( m, "(?P<0>[^(]+)\((?P<1>[^,]*)(,(?P<2>.*))?\)", p ) )
t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 != "" ? p2 : 0 ) )
else if ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)!", p) )
t := RegExReplace( t, "\Q" p "\E", calc( "", "!", p1, 0 ) )
else if ( RegExMatch( t, "\((?P<0>[^(]+)\)", p ) )
t := RegExReplace( t, "\Q(" p0 ")\E", calc( p0 ) )
else
loop, parse, o, |
while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )
return t
}

esc::exitapp```

greets
dR

All scripts, unless otherwise noted, are hereby released under CC-BY

• Moderators
• 1119 posts
• Last active: May 02 2015 06:05 PM
• Joined: 23 Aug 2010
Amazing! This seems like a very good alternative when Monster is too big and complicated.

Just one question: is there an alternative to recursion? For example, with a loop and a pseudo-stack?

• Members
• 872 posts
• Last active: Mar 19 2013 04:42 PM
• Joined: 23 Nov 2007
at the current stage of development - the function is designed to work only due to recursion. probably it might be redesigned in a manner to use internal jumplabels thus avoiding the recursions. thx for the interest.

greets
dR

All scripts, unless otherwise noted, are hereby released under CC-BY

• Members
• 120 posts
• Last active: Feb 23 2014 09:58 PM
• Joined: 13 Dec 2006
Hi,

Great script; exactly what I need...
But I've found one bug: when I try to call calc with something like cos(90), cos(179), etc., AutoHotkey goes in an endless loop

I've tried with AutoHotkey and AHK_L, under XP and Win7. Still the same.

• Members
• 141 posts
• Last active: Jun 28 2019 01:05 AM
• Joined: 11 Nov 2012

But I've found one bug: when I try to call calc with something like cos(90), cos(179), etc., AutoHotkey goes in an endless loop

I've tried with AutoHotkey and AHK_L, under XP and Win7. Still the same.

Found the problem, here's the fix. Find the line:
`: t0 = "rad" ? calc( calc( t1 ) "* pi / 180" ) : t0 = "deg" ? calc( calc( t1 ) "* 180 / pi" )`
change to:
`: t0 = "rad" ? calc( t1 )*4*atan(1)/180 : t0 = "deg" ? calc( t1 )*180/4*atan(1)`

• Members
• 107 posts
• Last active: Jul 15 2017 04:06 PM
• Joined: 14 Nov 2008

Very useful function, thank you!

However, I have found that running

```equation=-3+1
MsgBox % calc(equation)
ExitApp
```

seems to result in an infinite loop.

• Members
• 141 posts
• Last active: Jun 28 2019 01:05 AM
• Joined: 11 Nov 2012
Seems to infinite loop on any negative number.
`MsgBox % calc(-5)`
Something to do with the regex parsing for negative numbers, I tried looking into it but I don't understand some of those Regex tags. The infinite loop seems to occur in the following piece of code.
```      else
loop, parse, o, |
while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )```
Anyone understand whats going on? This is my favorite calc function since looking for something minimalistic.

• Members
• 23 posts
• Last active: Dec 18 2014 05:05 PM
• Joined: 10 Jun 2014

I don't mean to resurrect an old thread but I happened across this and thought I'd contribute.

The problem stems (I believe) from the regex thinking negative numbers are equations because they contain the character "-".

It's not ideal but I believe you can use the following to terminate the loop and return negative values.

```; dRs CALC! - http://www.opensource.org/licenses/mit-license.html

Msgbox % Calc("-3+1")

exitapp
return

Esc::ExitApp

calc( t, t0="", t1="", t2="" )
{   ; c-labeled functions by Laszlo: http://www.autohotkey.com/forum/topic17058.html
static f := "sqrt|log|ln|exp|sin|cos|tan|asin|acos|atan|rad|deg|abs", c := "fib|gcb|min|max|sgn"
o := "\*\*|\^|\*|/|//|\+|\-|%", pi:="pi", e:="e"
if ( t0 = "fib"  && t1 != "" && t2 != "" ) {
a := 0, b := 1
Loop % abs(t1)-1
c := b, b += a, a := c
return t1=0 ? 0 : t1>0 || t1&1 ? b : -b
} else if ( t0 != "" && RegExMatch( t0, "(" f "|" c "|" o "|!)" ) && t1 != "" && t2 != "" )
return t0 == "**" ? t1 ** t2 : t0 == "^" ? t1 ** t2
: t0 == "*" ? t1 * t2   : t0 == "/" ? t1 / t2 : t0 == "+" ? t1 + t2 : t0 == "-" ? t1 - t2
: t0 == "//" ? t1 // t2 : t0 == "%" ? Mod( t1, t2 ) : t0 = "abs" ? abs( calc( t1 ) )
: t0 == "!" ? ( t1 < 2 ? 1 : t1 * calc( t, t0, t1-1, 0 ) )
: t0 = "log" ? log( calc( t1 ) ) : t0 = "ln" ? ln( calc ( t1 ) )
: t0 = "sqrt" ? sqrt( calc( t1 ) ) : t0 = "exp" ? Exp( calc ( t1 ) )
: t0 = "rad" ? calc( t1 )*4*atan(1)/180 : t0 = "deg" ? calc( t1 )*180/4*atan(1)
: t0 = "sin" ? sin( calc( "rad(" t1 ")" ) ) : t0 = "asin" ? asin( calc( "rad(" t1 ")" ) )
: t0 = "cos" ? cos( calc( "rad(" t1 ")" ) ) : t0 = "acos" ? acos( calc( "rad(" t1 ")" ) )
: t0 = "tan" ? tan( calc( "rad(" t1 ")" ) ) : t0 = "atan" ? atan( calc( "rad(" t1 ")" ) )
: t0 = "min" ? ( t1 < t2 ? t1 : t2 ) : t0 = "max" ? ( t1 < t2 ? t2 : t1 )
: t0 = "gcd" ? ( t2 = 0 ? abs(t1) : calc( t, t0, calc( t1 "%" t2 ) ) )
: t0 = "sgn" ? (t1>0)-(t1<0) : 0

t := RegExReplace( t, "\s*", "" )
while ( RegExMatch( t, "i)" f "|" c "|" o "|" pi "|" e "|!" ) )
if ( RegExMatch( t, "i)\b" pi "\b" ) )
t := RegExReplace( t, "i)\b" pi "\b", 4 * atan(1) )
else if ( RegExMatch( t, "i)\b" e "\b" ) )
t := RegExReplace( t, "i)\b" e "\b", 2.718281828459045 )
else if ( RegExMatch( t, "i)(" f "|" c ").*", s )
&& RegExMatch( s, "(?>[^\(\)]*)\((?>[^\(\)]*)(?R)*(?>[^\(\)]*)\)", m )
&& RegExMatch( m, "(?P<0>[^(]+)\((?P<1>[^,]*)(,(?P<2>.*))?\)", p ) )
t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 != "" ? p2 : 0 ) )
else if ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)!", p) )
t := RegExReplace( t, "\Q" p "\E", calc( "", "!", p1, 0 ) )
else if ( RegExMatch( t, "\((?P<0>[^(]+)\)", p ) )
t := RegExReplace( t, "\Q(" p0 ")\E", calc( p0 ) )
else
loop, parse, o, |
{
while ( RegExMatch( t, "(?P<1>-*\d+(\.\d+)?)(?P<0>" A_LoopField ")(?P<2>-*\d+(\.\d+)?)", p ) )
t := RegExReplace( t, "\Q" p "\E", calc( "", p0, p1, p2 ) )
If ( RegExMatch(SubStr(t, 2), "[(\*\*)\^\*(//)/\+\-`%]") = 0 )
return t
}
return t
}
```