AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

LowLevel & dynamic code
Goto page Previous  1, 2, 3, 4, 5  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sun Dec 09, 2007 3:54 am    Post subject: Reply with quote

Back to the technical stuff:
One peculiarity: __expr() accepts lists of expressions, but they get evaluated from right to left:
Code:
__expr("d:=b+3, b:=7")
returns 10, but only at the second call. The first call with the same argument returns blank.
The following expression needs 4 calls to evaluate to 13:
Code:
__expr("x+1, x:=y+1, y:=z+1, z:=10")

If we write the expressions in left-to-right order, it evaluates to 10 (the leftmost term), and it will remain always the same:
Code:
__expr("z:=10, y:=z+1, x:=y+1,  x+1")
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2558
Location: Australia, Qld

PostPosted: Sun Dec 09, 2007 5:25 am    Post subject: Reply with quote

Laszlo wrote:
Back to the technical stuff:
One peculiarity: __expr() accepts lists of expressions, but they get evaluated from right to left:

Nope. __expr accepts a single expression.
Quote:
Code:
__expr("d:=b+3, b:=7")
returns 10, but only at the second call.

That is exactly how it should work. Try this:
Code:
MsgBox % ( d:=b+3, b:=7 ) ; b is assigned after b+3 is evaluated, so d = ""
MsgBox % ( d:=b+3, b:=7 )

As I said, __expr does not evaluate the expression. It simply constructs a line of code with:
  • mActionType = ACT_RETURN.
  • mArgc = 1
  • mArg = an ArgStruct with
    • type = ARG_TYPE_NORMAL
    • is_expression = TRUE
    • text = expr (and length = StrLen(expr)) after processing 'single-quotes'
    • deref = an array of DerefType (var and func derefs.)
This line replaces the contents of __expr_sub, which is then called to get AutoHotkey itself to evaluate the expression.


I've added two new functions:
I wrote:
__listLabels( [ UserLabelsOnly ] )
    Returns a `n-delimited list of all labels in the script, including hotkey and hotstring labels. If UserLabelsOnly=True, labels in the same file as (the definition of) __listLabels are omitted.
__getFirstLabel()
    Retrieves a pointer to the first Label in the script's linked list of labels.
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 422
Location: Galil, Israel

PostPosted: Sun Dec 09, 2007 5:49 am    Post subject: Reply with quote

interesting stuff, like x := a_hour returns "a_hour"...


as does fun(a_hour)... if fun(x){msgbox % x }
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2558
Location: Australia, Qld

PostPosted: Sun Dec 09, 2007 6:04 am    Post subject: Reply with quote

Joy2DWorld wrote:
interesting stuff, like x := a_hour returns "a_hour"...

Oh, I forgot to mention that. Since double-dereferencing doesn't work with built-in variables, neither does __getVar/__expr. The only way I can think of to do it is have a hard-coded list of all built-in variables. A function could then search through the derefs in code for the built-in variables.

Actually, true and false are considered built-in variables...

Edit: Built-in variables are now supported by __expr via __getVarInContext and __getBuiltInVar.


Last edited by Lexikos on Sun Dec 09, 2007 10:33 am; edited 1 time in total
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 422
Location: Galil, Israel

PostPosted: Sun Dec 09, 2007 6:22 am    Post subject: Reply with quote

it's cool,
Quote:
x := %a_hour%
a little freaky though...
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 422
Location: Galil, Israel

PostPosted: Sun Dec 09, 2007 6:29 am    Post subject: Reply with quote

recursive seems to bomb out...


eval_("eval_test()")


eval_test(){

msgbox % eval_("1+1")

}



ps: suggest, deserves lib "Eval_"
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2558
Location: Australia, Qld

PostPosted: Sun Dec 09, 2007 6:56 am    Post subject: Reply with quote

Joy2DWorld wrote:
it's cool,
Quote:
x := %a_hour%
a little freaky though...
Most text that is not a valid deref or operator will appear as literal. This is because the script does less error-checking than AutoHotkey normally would. For instance, {} aren't normally valid in expressions, but are interpreted as literal strings when __MakeExpressionArg (__expr) forces it into the expression:
Code:
__init()
MsgBox % __expr("x := {" A_Hour "}")

Also, double-derefs always resolve to a global variable. I've added this to the known limitations.
Joy2DWorld wrote:
recursive seems to bomb out...
Thanks, I hadn't thought of that. It's most likely because I optimized by creating the Line only once, rather than for each call.

Edit: __expr now supports recursion.

Edit2: Added two new functions and __expr support for built-in variables.
I wrote:
__getBuiltInVar( sVarName )
    Retrieves a pointer to the built-in Var with the given name.
__findLabel( sLabel )
    Retrieve a pointer to the specified Label.

Joy2DWorld wrote:
ps: suggest, deserves lib "Eval_"
Given that (I think) __expr now uses every __function (directly or indirectly) except __listLabels and __listFuncs, I couldn't really make a separate library for it. Shocked
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Sun Dec 09, 2007 11:18 am    Post subject: Reply with quote

Thx for some explanations and good addons.

So, Call module is obsolete now. _expr makes no difference between normal and dynamic call while using Call, u must take care of strings.
_________________
Back to top
View user's profile Send private message MSN Messenger
Joy2DWorld



Joined: 04 Dec 2006
Posts: 422
Location: Galil, Israel

PostPosted: Sun Dec 09, 2007 8:08 pm    Post subject: Reply with quote

tiny suggest:

(eval_ naming)

include auto init within eval_ funct. (ie. simple function eval_(xxx) works without need for any prep.)

eg:
Code:

; Eval.ahk
; tiny example mods by JD2W
;   Various low-level functions for interacting with the internals of AutoHotkey.
;   Based on v1.0.47.04.
;
; DISCLAIMER: MAY CRASH OTHER VERSIONS OF AUTOHOTKEY.
;

; Allow auto-inclusion when named "lowlevel.ahk".

; MUST BE CALLED AT LEAST ONCE for Eval_getVar and Eval_static to work.
Eval_init() {
   Global Eval_init_done
   
   if Eval_init_done
      return
   Eval_init_done = 1

   
    ; Because Eval_mcode makes Eval_getVar "built-in",  Eval_getFirstFunc
    ; (which makes certain assumptions about the order of functions)
    ; must be called at least once before Eval_mcode, or it may
    ; not be able to find Eval_getVar and Eval_static later on.
    Eval_getFirstFunc()
   
    Eval_mcode("Eval_getVar","8B4C24088B0933C08379080375028B018B4C2404998901895104C3")
    Eval_mcode("Eval_static","8B4424088B008378080375068B0080480D04C3")
}

; Replaces a script function with machine code.
Eval_mcode(FuncName, Hex)
{
    if !(pFunc := Eval_getFuncUDF(FuncName)) or !(pbin := DllCall("GlobalAlloc","uint",0,"uint",StrLen(Hex)//2))
        return 0
    Loop % StrLen(Hex)//2
        NumPut("0x" . SubStr(Hex,2*A_Index-1,2), pbin-1, A_Index, "char")
    NumPut(pbin,pFunc+4), NumPut(1,pFunc+49,0,"char")
    return pbin
}

Eval_static(var) {
    ; MCODE: Makes the specified Var static.
    ; Example usage:
    ;   Eval_static(array%i%)
   
    ; Note that Eval_static could be implemented in script using Eval_getVar and
    ; NumPut. For two reasons I chose to implement it as machine code:
    ;   - The machine code of Eval_static is shorter than that of Eval_getVar.
    ;   - Eval_static may be called in high frequency (i.e. for arrays),
    ;     so it is particularly beneficial for it to not spam ListLines.
}

Eval_getVar(var) {
    ; MCODE: Returns a pointer to the specified Var object, or 0 for built-in vars.
    ; Example usage:
    ;   NumGet(Eval_getVar(%name%)+13,0,"char")  ; Var.mAttrib
    ;   NumGet(Eval_getVar(%name%)+13,0,"char") & 1  ; is ClipboardAll blob
    ;   NumGet(Eval_getVar(%name%)+13,0,"char") & 4  ; is static
    ;   NumGet(Eval_getVar(%name%)+12,0,"char")  ; Var.mHowAllocated (zero if never init'd)
}

; Note that since the purpose of Eval_getVar() is to retrieve a Var pointer from
; the expression token which AutoHotkey passes it as a "built-in" function,
;   - DllCall can not be used to call the machine code, and
;   - we can't simply initialize the machine code from within Eval_getVar since
;     it'd then be accessing the parameter 'var', not the variable which was
;     passed. Making var ByRef would require Eval_mcode to use Eval_findFunc,
;     which is less efficient (and spams ListLines more) than Eval_getFuncUDF.

; Eval_getVar benchmarks at 0.000015s in pure script mode, or 0.000002s in
; "built-in" mode. However, script mode entirely fills ListLines, whereas
; the built-in mode does not log ANY lines.

/* Script version:
Eval_getVar(ByRef var) {
    Static pThisFunc
    if !pThisFunc && !(pThisFunc := Eval_findFunc(A_ThisFunc))
        return  ; silently fail
    return NumGet(NumGet(NumGet(pThisFunc+8)+0)+4)  ; pGetVar->mParam[0].var->mAliasFor
}
; NOTE: In the following scenario, the script version of Eval_getVar gets
;       a pointer to 'z', while the built-in version gets 'x' itself.
;   
;   f(z)
;   f(ByRef x) {
;       y := Eval_getVar(x)
;   }
*/

Eval_getGlobalVar(Eval_gGV_sVarName)
{
    global
    return Eval_getVar(%Eval_gGV_sVarName%)
}

Eval_getBuiltInVar(sVarName)
{
    static pDerefs, DerefCount
    if !pDerefs ;= label->mJumpToLine->mArg[0]->deref
    {
        pDerefs := NumGet(NumGet(NumGet(Eval_findLabel("Eval_gBIV_marker"),4),4),8)
        Loop
            if ! NumGet(pDerefs+(A_Index-1)*12) {
                DerefCount := A_Index-1
                break
            }
    }
    low := 0
    high := DerefCount - 1
    Loop {
        if (low > high)
            break
        mid := (low+high)//2
        i := DllCall("shlwapi\StrCmpNIA","uint",NumGet(pDerefs+mid*12),"str",sVarName,"int",NumGet(pDerefs+mid*12,10,"ushort"))
        if i > 0
            high := mid - 1
        else if i < 0
            low := mid + 1
        else
            return NumGet(pDerefs+mid*12,4)
    }
    return 0
Eval_gBIV_marker:
    return % 0,
    ( Join`s
A_AhkPath A_AhkVersion A_AppData A_AppDataCommon A_AutoTrim A_BatchLines
A_CaretX A_CaretY A_ComputerName A_ControlDelay A_Cursor A_DD A_DDD A_DDDD
A_DefaultMouseSpeed A_Desktop A_DesktopCommon A_DetectHiddenText
A_DetectHiddenWindows A_EndChar A_EventInfo A_ExitReason A_FormatFloat
A_FormatInteger A_Gui A_GuiControl A_GuiControlEvent A_GuiEvent A_GuiHeight
A_GuiWidth A_GuiX A_GuiY A_Hour A_IconFile A_IconHidden A_IconNumber A_IconTip
A_Index A_IPAddress1 A_IPAddress2 A_IPAddress3 A_IPAddress4 A_IsAdmin
A_IsSuspended A_KeyDelay A_Language A_LastError A_LineFile A_LineNumber
A_LoopField A_LoopFileAttrib A_LoopFileDir A_LoopFileExt A_LoopFileFullPath
A_LoopFileLongPath A_LoopFileName A_LoopFileShortName A_LoopFileShortPath
A_LoopFileSize A_LoopFileSizeKb A_LoopFileSizeMb A_LoopFileTimeAccessed
A_LoopFileTimeCreated A_LoopFileTimeModified A_LoopReadLine A_MDay A_Min A_MM
A_MMM A_MMMM A_Mon A_MouseDelay A_MSec A_MyDocuments A_Now A_NowUtc
A_NumBatchLines A_OSType A_OSVersion A_PriorHotkey A_ProgramFiles A_Programs
A_ProgramsCommon A_ScreenHeight A_ScreenWidth A_ScriptDir A_ScriptFullPath
A_ScriptName A_Sec A_Space A_StartMenu A_StartMenuCommon A_Startup
A_StartupCommon A_StringCaseSense A_Tab A_Temp A_ThisFunc A_ThisHotkey
A_ThisLabel A_ThisMenu A_ThisMenuItem A_ThisMenuItemPos A_TickCount A_TimeIdle
A_TimeIdlePhysical A_TimeSincePriorHotkey A_TimeSinceThisHotkey
A_TitleMatchMode A_TitleMatchModeSpeed A_UserName A_WDay A_WinDelay A_WinDir
A_WorkingDir A_YDay A_Year A_YWeek A_YYYY Clipboard ClipboardAll ComSpec false
ProgramFiles true
    )
}

; Executes a double-deref in the context of the specified function.
; Use Eval_findFunc() to get a pointer to a Func for use with pScopeFunc.
Eval_getVarInContext(sVarName, pScopeFunc=0)
{
    static pThisFunc
    if pVar:=Eval_getBuiltInVar(sVarName)
        return pVar
    if !pScopeFunc
        return Eval_getGlobalVar(sVarName)
    if !pThisFunc && !(pThisFunc := Eval_getFuncUDF(A_ThisFunc))
        return
   
    ; Copy assume-local/global mode. Since it only affects double-derefs,
    ; it doesn't need to be restored to its previous value later.
    NumPut(NumGet(pScopeFunc+48,0,"char"),pThisFunc+48,0,"char")
   
    ; Back up this function's properties,
    VarSetCapacity(ThisFuncProps, 20)
    , DllCall("RtlMoveMemory","uint",&ThisFuncProps,"uint",pThisFunc+20,"uint",20)
    ; then overwrite them with the other function's properties:
    ;   mVar, mLazyVar, mVarCount, mVarCountMax, mLazyVarCount
    , DllCall("RtlMoveMemory","uint",pThisFunc+20,"uint",pScopeFunc+20,"uint",20)
   
; WARNING:
    ; If the thread is interrupted at this point and Eval_getVarInContext is called
    ; again, the wrong set of local variables will be backed up and restored, so
    ; the second instance of Eval_getVarInContext will overwrite the local vars
    ; of the first instance. Merging the lines (with ", ") prevents AutoHotkey
    ; from checking for messages, thus reducing the risk of interruption.
    ; (This would not work fully if Eval_getVar were implemented in script.)
   
    ; Now resolve %sVarName% in the scope of the other func!
    , pVar := Eval_getVar(%sVarName%)
   
    ; Update pScopeFunc's properties.
    , DllCall("RtlMoveMemory","uint",pScopeFunc+20,"uint",pThisFunc+20,"uint",20)
   
    ; Restore this function's properties.
    , DllCall("RtlMoveMemory","uint",pThisFunc+20,"uint",&ThisFuncProps,"uint",20)
   
    return pVar
}

; Lists all accessible functions.
Eval_listFuncs(UserFunctionsOnly=False)
{
    ; if UserFunctionsOnly, also exclude functions in THIS file.
    this_file_index := NumGet(NumGet(Eval_getFuncUDF(A_ThisFunc)+4),2,"ushort")
   
    if !(pFunc := Eval_getFirstFunc())
        return
    Loop {
        if !UserFunctionsOnly || !NumGet(pFunc+49,0,"char") && NumGet(NumGet(pFunc+4),2,"ushort") != this_file_index
            list .= Eval_str(NumGet(pFunc+0)) "`n"
        if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
            break
    }
    return SubStr(list,1,-1)
}

; Func *findFunc( FuncName, BuiltIn )
;   FuncName    name of the function
;   BuiltIn     true to search only for built-in functions
;               false to search only for user-defined functions
;               empty to search for any type of function.
Eval_findFunc(FuncName, BuiltIn="")
{
    ; Do not change the order here (see the "combination" method in the comment below.)
    if ((pFunc:=Eval_getFuncUDF(FuncName)) && !BuiltIn)
        return pFunc
    if !(pFunc:=Eval_getFirstFunc())
        return 0
    Loop {  ; Note: ! is used here to ensure both values are true boolean 1 or 0.
        if (BuiltIn = "" or (!NumGet(pFunc+49,0,"uchar") = !BuiltIn))
            if (Eval_str(NumGet(pFunc+0)) = FuncName) ; pFunc->mName
                return pFunc
        if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
            break
    }
    return 0
}
; Possible methods for finding a Func:
; - RegisterCallback & NumGet. This approach is likely the most efficient,
;   and is also used as an entry point (by Eval_getFirstLine()).
;   However, RegisterCallback fails for built-in functions or user-defined
;   functions which have ByRef parameters.
; - Search for function "derefs" in each script line. This does not work for
;   functions which are not referenced anywhere in the script.
; - Search through the linked list of functions. This is reliable as long as
;   we have a pointer to the first function. This is usually the first function
;   defined in the script or its explicit #includes, if any.
; - A combination of all of the above:
;    +  Though RegisterCallback does not work for built-in functions, calling
;       RegisterCallback for a built-in function will cause it's Func object
;       to be created if it doesn't already exist. Calling Eval_getFuncUDF() ensures
;       that the built-in function we are looking for is in the linked list.
;    +  If Eval_getFuncUDF() succeeds AND we are not specifically looking for a
;       built-in function, no more work is necessary.
;   The first time Eval_getFuncUDF fails:
;    +  Because searching through derefs is likely to take longer than walking
;       the linked list of functions, we search through derefs only to find a
;       pointer to the first function in the list. (See Eval_getFirstFunc()).
;   For all calls of Eval_findFunc() where Eval_getFuncUDF() fails:
;    +  We walk the linked list to find the function.


; Func *getFirstFunc()
;   Gets the first Func in the linked list of functions, excluding unreferenced
;   functions which are not preceded by any referenced functions.
Eval_getFirstFunc()
{
    static pFirstFunc
    if !pFirstFunc {
        if !(pLine := Eval_getFirstLine())
            return 0
        Loop {
            Loop % NumGet(pLine+1,0,"uchar") { ; pLine->mArgc
                pArg := NumGet(pLine+4) + (A_Index-1)*12  ; pLine->mArg[A_Index-1]
                if (NumGet(pArg+0,0,"uchar") != 0) ; pArg->type != ARG_TYPE_NORMAL
                    continue ; arg has no derefs (only a Var*)
                Loop {
                    pDeref := NumGet(pArg+8) + (A_Index-1)*12  ; pArg->deref[A_Index-1]
                    if (!NumGet(pDeref+0)) ; pDeref->marker (NULL terminates list)
                        break
                    if (NumGet(pDeref+8,0,"uchar")) ; pDeref->is_function
                    {
                        ; The first function is either the first defined function,
                        ; or if no explicitly #included UDFs exist, the first
                        ; built-in function referenced in code.
                        pFunc := NumGet(pDeref+4)
                        if (NumGet(pFunc+49,0,"uchar")) { ; pFunc->mIsBuiltIn
                            if !pFirstBIF
                                pFirstBIF := pFunc
                        } else { ; UDF
                            pFuncLine := NumGet(pFunc+4)
                            FuncLine := NumGet(pFuncLine+8)
                            FuncFile := NumGet(pFuncLine+2,0,"ushort")
                            if !pFirstFunc or (FuncFile < FirstFuncFile || (FuncFile = FirstFuncFile && FuncLine < FirstFuncLine))
                                pFirstFunc:=pFunc, FirstFuncLine:=FuncLine, FirstFuncFile:=FuncFile
                        }
                    }
                }
            }
            if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
                break
        }
        if pFirstBIF
        {   ; Usually the first UDF will be before the first BIF, but not if
            ; only auto-included/stdlib UDFs exist *AND* the first BIF is
            ; referenced in code before the first UDF.
            if pFirstFunc
            {   ; Look for the BIF using the UDF as a starting point.
                pFunc := pFirstFunc
                Loop {
                    if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
                        break
                    ; If the BIF is found, the UDF must precede it in the list.
                    if (pFunc = pFirstBIF)
                        return pFirstFunc
                }
            ; If we got here, the BIF was not found, so is probably the first Func.
            }
            pFirstFunc := pFirstBIF
            return pFirstFunc
        }
    }
    return pFirstFunc
}

Eval_listLabels(UserLabelsOnly=False)
{
    this_file_index := NumGet(NumGet(Eval_getFuncUDF(A_ThisFunc)+4),2,"ushort")
    if pLabel := Eval_getFirstLabel()
        Loop {
            if !UserLabelsOnly || NumGet(NumGet(pLabel+4),2,"ushort") != this_file_index
                list .= Eval_str(NumGet(pLabel+0)) "`n"
            if ! pLabel := NumGet(pLabel+12)
                break
        }
    return SubStr(list,1,-1)
}

Eval_findLabel(sLabel)
{
    if pLabel := Eval_getFirstLabel()
        Loop {
            if Eval_str(NumGet(pLabel+0)) = sLabel
                return pLabel
            if ! pLabel := NumGet(pLabel+12)
                return 0
        }
}

Eval_getFirstLabel()
{
    static pFirstLabel
    if !pFirstLabel {
        if !(pLine := NumGet(Eval_getFuncUDF(A_ThisFunc)+4))
            return 0
        Loop {
            act := NumGet(pLine+0,0,"char")
            if (act = 96 || act = 95) ; ACT_GOSUB || ACT_GOTO
                break
            if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
                return 0
        }
        pFirstLabel := NumGet(pLine+24)
        Loop {
            if ! pPrevLabel:=NumGet(pFirstLabel+8)
                break
            pFirstLabel := pPrevLabel
        }
    }
    return pFirstLabel
    ; Since Labels are in a doubly-linked list, we can find the first label
    ; by getting the Label associated with the goto line below.
    Eval_getFirstLabel_HookLabel:
    goto Eval_getFirstLabel_HookLabel
}

; Line *getFirstLine()
Eval_getFirstLine()
{
    static pFirstLine
    if (pFirstLine = "") {
        if pThisFunc := Eval_getFuncUDF(A_ThisFunc) {
            if pFirstLine := NumGet(pThisFunc+4) ; mJumpToLine
            Loop {
                if !(pLine:=NumGet(pFirstLine+16)) ; mPrevLine
                    break
                pFirstLine := pLine
            }
        }
    }
    return pFirstLine
}

; Func *getFuncUDF( FuncName )
;   Gets a pointer to a named user-defined function. Because RegisterCallback
;   doesn't support functions with ByRef parameters, neither does this function.
Eval_getFuncUDF(FuncName) {
    if pCb := RegisterCallback(FuncName) {
        func := NumGet(pCb+28)
        DllCall("GlobalFree","uint",pCb)
    }
    return func
}

Eval_str(addr,len=-1) {
    if len<0
        return DllCall("MulDiv","uint",addr,"int",1,"int",1,"str")
    VarSetCapacity(str,len), DllCall("lstrcpyn","str",str,"uint",addr,"int",len+1)
    return str
}

Eval_(expr, pScopeFunc=0)
{
   global Eval_init_done
    static pFunc, pThisFunc
    if !Eval_init_done
   Eval_init()

   
    if !pFunc
        pFunc:=Eval_getFuncUDF("Eval_sub"), pThisFunc:=Eval_getFuncUDF(A_ThisFunc)
   
    if ! pArg:=Eval_MakeExpressionArg(expr, pScopeFunc)
        return
   
    nInst := NumGet(pThisFunc+40)
    ; Using Eval_static ensures the Line is never deleted. Attempting to view a
    ;   a deleted line with ListLines would crash the script.
    ; Using a static *array* allows recursion of Eval_expr.
    ; Using nInst (recursion depth) as an array index preserves memory by
    ;   only allocating up to the maximum number of resursive instances, while
    ;   also ensuring that no other instance is using the same block of memory.
    VarSetCapacity(Line%nInst%,32,0), Eval_static(Line%nInst%), pLine:=&Line%nInst%

    NumPut(pArg,NumPut(1,NumPut(102,pLine+0,0,"char"),0,"char"),2), NumPut(pLine,NumPut(pLine,pLine+16))
    NumPut(pLine,pFunc+4)
    , ret := Eval_sub()
    , NumPut(0,pLine+1,0,"char"), DllCall("GlobalFree","uint",pArg)
    return ret
}
Eval_sub() {
    global
    ; Contents replaced at run-time by Eval_expr.
}


eval_test(this){

   msgbox % this

}


Eval_MakeExpressionArg(expr, pScopeFunc=0)
{
    static OPERAND_TERMINATORS = "<>=/|^,:"" `t*&~!()+-"
   
    ; VALIDATION AND SINGLE-QUOTES
    i = 0
    open_parens = 0
    Loop {
        if (c:=SubStr(expr,++i,1)) = ""
            break
        ; Ensure open-" has matching close-"
        if c = "
        {
            if ! i:=InStr(expr,"""",1,i+1)
                return 0, ErrorLevel:="Missing close-quote ("")"
        }
        ; Allow ' in place of ", and ensure it has a matching close-'
        else if c = '
        {
            if ! j:=InStr(expr,"'",1,i+1)
                return 0, ErrorLevel:="Missing close-quote (')"
            literal := SubStr(expr,i+1,j-i-1)
            StringReplace, literal, literal, ", "", UseErrorLevel
            expr := SubStr(expr,1,i-1) . """" . literal . """" . SubStr(expr,j+1)
            i := j + ErrorLevel
        }
        ; Ensure parentheses are balanced.
        else if c = (
            open_parens += 1
        else if c = )
            open_parens -= 1
    }
    if open_parens > 0
        return 0, ErrorLevel:="Missing "")"""
    if open_parens < 0
        return 0, ErrorLevel:="Missing ""("""

    ; PARSE DEREFS
    num_derefs = 0
    pos = 1
    Loop {
        ; includes \. to avoid interpreting the '0e1' in '1.0e1' as a var.
        if ! pos:=RegExMatch(expr, """.*?""|[\w#@$?\[\]\.%]+", word, pos)
            break
        marker := pos-1
        pos += StrLen(word)
        if SubStr(word,1,1)="""" ; skip quoted literal strings
            continue
        if InStr(word,".") or word+0!="" ; number or error
            continue
        if word = ?
            continue
        param_count = 0
        if is_func:=SubStr(expr,pos,1)="("
        {
            if InStr(word, "%")
                return 0, Errorlevel:="Dynamic function calls are not supported."
            if ! var_or_func:=Eval_findFunc(word)
                return 0, ErrorLevel:="Call to nonexistent function """ word """."
            i := pos
            open_parens = 1
            Loop {
                if (c:=SubStr(expr,++i,1))=""
                    break
                if (param_count=0 && !(c=" "||c="`t"||c=")"))
                    param_count = 1
                if c = ,
                    param_count += open_parens=1 ? 1 : 0
                else if c = (
                    open_parens += 1
                else if c = )
                    if --open_parens = 0
                        break
            }
            if (param_count < NumGet(var_or_func+16))
                return 0, ErrorLevel:="Too few parameters passed to function """ word """."
            if (param_count > NumGet(var_or_func+12))
                return 0, ErrorLevel:="Too many parameters passed to function """ word """."
        }
        else
        {
            if i:=InStr(full_word:=word, "%") {
                original_marker := marker
                Loop {
                    if !(j:=InStr(full_word, "%", 1, i+1)) or j-i<2
                        return 0, ErrorLevel:="Invalid deref """ full_word """."
                    word_len := StrLen( word := SubStr(full_word, i, j-i+1) )
                    marker := original_marker + i-1
                    if var_or_func:=Eval_getVarInContext(SubStr(word,2,-1), pScopeFunc)
                        gosub Eval_MakeExpressionArg_AddDeref
                    if ! i:=InStr(full_word, "%", 1, j+1)
                        break
                }
                continue
            }
            var_or_func := Eval_getVarInContext(word, pScopeFunc)
        }
       
        if var_or_func
        {
            word_len := StrLen(word)
            gosub Eval_MakeExpressionArg_AddDeref
        }
    }

    ; Allocate: sizeof(ArgStruct) + StrLen(expr)+1 + (num_derefs+1)*sizeof(DerefType)
    pArg := DllCall("GlobalAlloc","uint",0x40,"uint",25+StrLen(expr)+num_derefs*12)
    DllCall("lstrcpy","uint",pArg+12,"str",expr)
    NumPut(pDerefs:=pArg+13+StrLen(expr),NumPut(pArg+12,NumPut(StrLen(expr),NumPut(1,NumPut(0,pArg+0,0,"char"),0,"char"),0,"short")))
    Loop, %num_derefs% {
        DllCall("RtlMoveMemory","uint",pDeref:=pDerefs+A_Index*12-12,"uint",&deref%A_Index%,"uint",12)
        NumPut(pArg+12+NumGet(pDeref+0),pDeref+0) ; deref.marker += base_address_of_text
    }
    return pArg, ErrorLevel=0
   
    Eval_MakeExpressionArg_AddDeref:
        num_derefs += 1
        VarSetCapacity(deref%num_derefs%,12)
        NumPut(word_len,NumPut(param_count,NumPut(is_func,NumPut(var_or_func,NumPut(marker,deref%num_derefs%)),0,"char"),0,"char"),0,"short")
        ; Note: marker must be adjusted later: pos += base_address_of_text
    return
}

; WHAT IS NORMALLY DONE AT LOAD-TIME:
;   - Replacing 'AND' and 'OR' with '&&' and '||'.
;   - Ensuring parentheses are balanced.    -- DONE
;   - Ensuring literal quoted strings have a closing quote mark.    -- DONE
;   - Validating parameter count in function calls. -- DONE
;   - Validating ByRef parameters in function calls.
;     This is also done at run-time, so need not be done by us.




@lexikos,


damned nice stuff!
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Mon Dec 10, 2007 4:07 am    Post subject: Reply with quote

lexikos wrote:
calling __init from __expr is not a problem.
If you have several __expr calls, do this many __init’s slow things down or cause other problems? It looks cleaner to leave __init outside, doesn’t it?
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2558
Location: Australia, Qld

PostPosted: Mon Dec 10, 2007 4:09 am    Post subject: Reply with quote

Laszlo wrote:
lexikos wrote:
calling __init from __expr is not a problem.
If you have several __expr calls, do this many __init’s slow things down or cause other problems? It looks cleaner to leave __init outside, isn’t it.
I have added a static variable to __init so it only does something on the first call. Previously, calling __init a second time would have "leaked" the previous binary contents of __getVar and __static. Additionally, __expr will only call __init the first time it is called.
Code:
    static pFunc, pThisFunc
    if !pFunc
        pFunc:=__getFuncUDF("__expr_sub"), pThisFunc:=__getFuncUDF(A_ThisFunc), __init()

Smile
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Mon Dec 10, 2007 4:11 am    Post subject: Reply with quote

Very Happy
Back to top
View user's profile Send private message
Moderator!
Guest





PostPosted: Tue Dec 11, 2007 4:01 am    Post subject: Reply with quote

Note from Moderator! : 40 posts from this topic have been split into a new topic in General Chat - http://www.autohotkey.com/forum/viewtopic.php?t=26471
Back to top
SKAN



Joined: 26 Dec 2005
Posts: 5890

PostPosted: Tue Dec 11, 2007 6:44 am    Post subject: Reply with quote

Dear lexikos, Smile

Is is possible to ascertain the memory used by a script ?
Altleast a delimited list of memvar with which VarSetCapacity( ?, -1 ) can be executed upon ?

Smile
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2558
Location: Australia, Qld

PostPosted: Tue Dec 11, 2007 8:10 am    Post subject: Reply with quote

Skan wrote:
Is is possible to ascertain the memory used by a script ?
Altleast a delimited list of memvar with which VarSetCapacity( ?, -1 ) can be executed upon ?
I suppose you mean the total amount of memory used by all variables? The following demonstrates how to enumerate local vars and get various properties, including mCapacity:
Code:
LowLevel_init()
f(123)
f(x){
    pFunc := __findFunc("f")
    pVars := NumGet(pFunc+20)   ; mVars
    Loop % NumGet(pFunc+28)     ; mVarCount
    {
        pVar := NumGet(pVars + (A_Index-1)*4)   ; mVars[A_Index-1]
        text .= __str(NumGet(pvar+16))          ; mName
            . "[" NumGet(pvar+4) " of " NumGet(pvar+8) "]: "   ; mLength, mCapacity
            . RegExReplace(__str(NumGet(pvar+0)),"`n","``n") "`n"   ; mContents
    }
    MsgBox %text%
}
The func need not be running for it to work, but all non-static local variables should be blank in that case.

See lowlevel_outline.txt for an outline of the Var structure.

It doesn't seem possible to list all global variables directly, but it can be done with the help of corrupt's GetAhkStats or similar.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4, 5  Next
Page 2 of 5

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group