Math evaluation is incorrect using calc function. Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Math evaluation is incorrect using calc function.

08 Jul 2020, 16:34

I am using the calc function found here https://autohotkey.com/board/topic/59087-func-calc-math-expression-evaluation-incl-brackets/ in a calculator program that I use instead of the windows calculator.

I have been using it for several months now and just came across a problem where it evaluates incorrectly like shown in the first two examples. I am sure it involves the regex functions which I am not very experienced with.

From the contents of calcHistory it looks like the first calculation evals correctly between the first two parmeters but incorrectly parses the third, and completely skips the fourth.

Could someone help me find the problem?

Code: Select all

global calcHistory := ""
msgbox start

calcHistory := ""
result := calc("1+11+11+11") ;evals to 124
tooltip % calcHistory
msgbox % 1+11+11+11 . " : " . result 

calcHistory := ""
result := calc("2+22+22+22") ;evals to 248
tooltip % calcHistory
msgbox % 2+22+22+22 . " : " . result

calcHistory := ""
result := calc("1+22+22+22") ;evals to 67
tooltip % calcHistory
msgbox % 1+22+22+22 . " : " . result

calcHistory := ""
result := calc("22+22+22+22") ;evals to 88
tooltip % calcHistory
msgbox % 22+22+22+22 . " : " . result


calc( t, t0="", t1="", t2="" )
{   ; c-labeled functions by Laszlo: http://www.autohotkey.com/forum/topic17058.html
	calcHistory .= t . "`n"
	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 != "" )
	{
		calcHistory .= t1 . t0 . t2 . "`n"
      	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 )
				{
					calcHistory .= "`nfinal answer loop : " . t
					return t
				}
			}
		}
	}
	calcHistory .= "`nfinal answer : " . t
	return t
}
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 13:11

try searching for Eval() as a replacement? Of course test thoroughly but I use it often and haven't noticed any problems
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 15:32

Thanks for the input.
I did look at Eval() when I was deciding what to use. I guess I went with calc() because it is concise and I only needed math functions. I would really like to find the bug because this code is so compact. Until then I will switch to eval() to get the program running.
sofista
Posts: 650
Joined: 24 Feb 2020, 13:59
Location: Buenos Aires

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 15:57

colt wrote:
09 Jul 2020, 15:32
Until then I will switch to eval() to get the program running.
Concerning the eval() function, take a look at this post:

https jacks-autohotkey-blog.com /2020/03/23/eval-function-autohotkey-hotkey-math-calculations/ Broken Link for safety

Have fun!
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 17:27

flyingDman wrote:
09 Jul 2020, 16:58
You might also want to look at this: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=15389 .
Wow! I had no idea this version of Eval existed. Brilliant!
teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 18:00

colt wrote: Brilliant!
I'm not sure.

Code: Select all

MsgBox, % eval("5**2")

eval(exp)
{
	transform, exp, deref, %exp%
	; make everything lowercase, set constants to uppercase
	exp:=format("{:l}", exp) 
	exp:=regExReplace(exp, "i)(E|LN2|LN10|LOG2E|LOG10E|PI|SQRT1_2|SQRT2)", "$U1")
	exp:=regExReplace(exp, "i)(abs|acos|asin|atan|atan2|ceil|cos|exp|floor"
	. "|log|max|min|pow|random|round|sin|sqrt|tan"
	. "|E|LN2|LN10|LOG2E|LOG10E|PI|SQRT1_2|SQRT2)", "Math.$1")
    obj:=ComObjCreate("HTMLfile")
    obj.write("<body><script>document.body.innerText=eval('" exp "');</script>")
    return inStr(cabbage:=obj.body.innerText, "body") ? "ERROR" : cabbage
}
User avatar
flyingDman
Posts: 2817
Joined: 29 Sep 2013, 19:01

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 18:36

None of these eval functions is perfect. I had adapted @tidbit's to include ** and ^ like so:

Code: Select all

evalq(exp)																				;tidbit's https://www.autohotkey.com/boards/viewtopic.php?f=6&t=15389 
{
 	transform, exp, deref, %exp%
    exp := RegExReplace(exp, "\s")
	exp:=format("{:l}", exp) 	                                                        ; make everything lowercase, set constants to uppercase
    exp:=regExReplace(StrReplace(exp, "**", "^"), "(\w+(\.*\d+)?)\^(\w+(\.*\d+)?)", "pow($1,$3)")                      ; 2**3 -> 2^3 -> pow(2,3) added fy FD
	exp:=regExReplace(exp, "i)(E|LN2|LN10|LOG2E|LOG10E|PI|SQRT1_2|SQRT2)", "$U1")
	exp:=regExReplace(exp, "i)(abs|acos|asin|atan|atan2|ceil|cos|exp|floor|log|max|min|pow|random|round|sin|sqrt|tan|E|LN2|LN10|LOG2E|LOG10E|PI|SQRT1_2|SQRT2)", "Math.$1")
    obj:=ComObjCreate("HTMLfile")
    obj.write("<body><script>document.body.innerText=eval('" exp "');</script>")
    return inStr(tmp:=obj.body.innerText, "body") ? "ERROR" : tmp
}
But I believe further functionality could be added. I think Laszlo's Monster is the most complete in terms of functionality but it is a real monster....( https://autohotkey.com/board/topic/15675-monster-evaluate-math-expressions-in-strings/)
BTW, @tidbit script includes powers but the notation is pow(2,3) rather than 2^3 or 2**3. But it does not do Mod(x,y) or x\y (at least I have not found out how . Edit: 9%7).
Last edited by flyingDman on 09 Jul 2020, 21:25, edited 2 times in total.
14.3 & 1.3.7
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 18:46

so everyone's comparing the same thing, this is the Eval() version I referenced above from Pulover i believe... Thanks for sharing the alternatives everyone, but out of principal I don't like the idea of using javascript for this...

Code: Select all

Eval(x) {                              ; expression preprocessing
   Static pi = 3.141592653589793, e = 2.718281828459045

   StringReplace x, x,`%, \, All       ; % -> \ for MOD
   x := RegExReplace(x,"\s*")          ; remove whitespace
   x := RegExReplace(x,"([a-zA-Z]\w*)([^\w\(]|$)","%$1%$2") ; var -> %var%
   Transform x, Deref, %x%             ; dereference all %var%

   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 {                              ; find innermost (..)
      If !RegExMatch(x, "(.*)\(([^\(\)]*)\)(.*)", y)
         Break
      x := y1 . Eval@(y2) . y3         ; replace "(x)" with value of x
   }
   Return Eval@(x)                     ; no more (..)
}

Eval@(x) {
   RegExMatch(x, "(.*)(\+|\#)(.*)", y) ; execute rightmost +- operator
   IfEqual y2,+,  Return Eval@(y1) + Eval@(y3)
   IfEqual y2,#,  Return Eval@(y1) - Eval@(y3)
                                       ; execute rightmost */% operator
   RegExMatch(x, "(.*)(\*|\/|\\)(.*)", y)
   IfEqual y2,*,  Return Eval@(y1) * Eval@(y3)
   IfEqual y2,/,  Return Eval@(y1) / Eval@(y3)
   IfEqual y2,\,  Return Mod(Eval@(y1),Eval@(y3))
                                       ; execute rightmost power
   StringGetPos i, x, @, R
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) ** Eval@(SubStr(x,2+i))
                                       ; execute rightmost function
   If !RegExMatch(x,".*(abs|floor|sqrt)(.*)", y)
      Return x                         ; no more function
   IfEqual y1,abs,  Return abs(  Eval@(y2))
   IfEqual y1,floor,Return floor(Eval@(y2))
   IfEqual y1,sqrt, Return sqrt( Eval@(y2))
}
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 18:49

Why dont You want to use js for this task?
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 19:06

well there is another interpreter invoked somehow, that I don't fully understand, and for no real benefit

I mean sure if the native way took a measurable amount of time in practice that was not required with the javascript approach I might make a different argument, but I don't see that... I also wouldn't know what operating systems supported that method identically without doing a little research that I wouldn't have to do otherwise, so thats a drawback.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Math evaluation is incorrect using calc function.

09 Jul 2020, 19:26

Code: Select all

doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
msgbox % doc.parentWindow.eval(5**2)
teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: Math evaluation is incorrect using calc function.

10 Jul 2020, 00:51

@malcev
It's better like this:

Code: Select all

MsgBox % 5**2
:lol:
But it is not an evaluation.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Math evaluation is incorrect using calc function.

10 Jul 2020, 02:18

You are right. :))))

Code: Select all

js := new JsRT.Edge
MsgBox % js.Eval("5**2")


/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }

    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}

class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}

_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}

_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}


/*
 *  JsRT for AutoHotkey v1.1
 *
 *  Utilizes the JavaScript engine that comes with IE11.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class JsRT extends ActiveScript._base
{
    __New()
    {
        throw Exception("This class is abstract. Use JsRT.IE or JSRT.Edge instead.", -1)
    }
    
    class IE extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "jscript9", "ptr")
                throw Exception("Failed to load jscript9.dll", -1)
            if DllCall("jscript9\JsCreateRuntime", "int", 0, "int", -1
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("jscript9\JsCreateContext", "ptr", runtime, "ptr", 0, "ptr*", context)
            this._Initialize("jscript9", runtime, context)
        }
    }
    
    class Edge extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "chakra", "ptr")
                throw Exception("Failed to load chakra.dll", -1)
            if DllCall("chakra\JsCreateRuntime", "int", 0
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("chakra\JsCreateContext", "ptr", runtime, "ptr*", context)
            this._Initialize("chakra", runtime, context)
        }
        
        ProjectWinRTNamespace(namespace)
        {
            return DllCall("chakra\JsProjectWinRTNamespace", "wstr", namespace)
        }
    }
    
    _Initialize(dll, runtime, context)
    {
        this._dll := dll
        this._runtime := runtime
        this._context := context
        DllCall(dll "\JsSetCurrentContext", "ptr", context)
        DllCall(dll "\JsGetGlobalObject", "ptr*", globalObject)
        this._dsp := this._JsToVt(globalObject)
    }
    
    __Delete()
    {
        this._dsp := ""
        if dll := this._dll
        {
            DllCall(dll "\JsSetCurrentContext", "ptr", 0)
            DllCall(dll "\JsDisposeRuntime", "ptr", this._runtime)
        }
        DllCall("FreeLibrary", "ptr", this._hmod)
    }
    
    _JsToVt(valref)
    {
        VarSetCapacity(variant, 24, 0)
        DllCall(this._dll "\JsValueToVariant", "ptr", valref, "ptr", &variant)
        ref := ComObject(0x400C, &variant), val := ref[], ref[] := 0
        return val
    }
    
    _ToJs(val)
    {
        VarSetCapacity(variant, 24, 0)
        ref := ComObject(0x400C, &variant) ; VT_BYREF|VT_VARIANT
        ref[] := val
        DllCall(this._dll "\JsVariantToValue", "ptr", &variant, "ptr*", valref)
        ref[] := 0
        return valref
    }
    
    _JsEval(code)
    {
        e := DllCall(this._dll "\JsRunScript", "wstr", code, "uptr", 0, "wstr", "source.js"
            , "ptr*", result)
        if e
        {
            if DllCall(this._dll "\JsGetAndClearException", "ptr*", excp) = 0
                throw this._JsToVt(excp)
            throw Exception("JsRT error", -2, format("0x{:X}", e))
        }
        return result
    }
    
    Exec(code)
    {
        this._JsEval(code)
    }
    
    Eval(code)
    {
        return this._JsToVt(this._JsEval(code))
    }
    
    AddObject(name, obj, addMembers := false)
    {
        if addMembers
            throw Exception("AddMembers=true is not supported", -1)
        this._dsp[name] := obj
    }
}
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Math evaluation is incorrect using calc function.  Topic is solved

10 Jul 2020, 05:57

colt wrote:
09 Jul 2020, 15:32
Thanks for the input.
I did look at Eval() when I was deciding what to use. I guess I went with calc() because it is concise and I only needed math functions. I would really like to find the bug because this code is so compact. Until then I will switch to eval() to get the program running.
For the issues shown by your examples:

Code: Select all

		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 ), , 1 ) ; <<<<< limited to the first occurence <<<<<
				}
				If ( RegExMatch(SubStr(t, 2), "[(\*\*)\^\*(//)/\+\-`%]") = 0 )
				{
					calcHistory .= "`nfinal answer loop : " . t
					return t
				}
			}
		}
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: Math evaluation is incorrect using calc function.

11 Jul 2020, 13:31

@just me Thank you for taking the time to look into this. I really appreciate your efforts. This fixes the problem with the function.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], mikeyww, ReyAHK and 276 guests