Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

LowLevel & dynamic code


  • Please log in to reply
173 replies to this topic
Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006

This makes easier my decision to stop development on LowLevel indefinitely,

I changed my mind. ;)

1.0.1 - January 18, 2009
Numerous changes have been made to make this release compatible with AutoHotkey pre-v1.0.48 beta, breaking compatibility with older versions of AutoHotkey. Some features are no longer available due to changes in how expressions work.

LowLevel.ahk
[*:12im70yd]Added __cacheEnable, __getTokenValue and __mcodeptr.
[*:12im70yd]Disabled __expr. Code.ahk
[*:12im70yd]code_arg_deref no longer supports function derefs.
[*:12im70yd]code_finalize now fails if code_arg was used to add an expression, and the arg's postfix array has not been set up.

See the first post for updated links. See the included documentation for details about the new functions.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

__expr is not supported

It is a pity. __expr is the only low level function I use every day...

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Indeed, __expr() was the main reason I was using LowLevel, because I needed dynamic (user-defined) functions with a dynamic (user-defined) number of parameters and AHK didn't seem to support that.

Is there an alternate way to do that, or is it no longer possible?

ZipCorruptionAlert
  • Guests
  • Last active:
  • Joined: --
The zip you provided for AutoHotkey AutoHotkey pre-v1.0.48 beta is corrupt.

Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006
Sorry, I think I created a new (empty) file by mistake. After re-uploading (er, uploading), downloading the file seems to give a corrupt zip if the name contains dots. :? I've renamed and corrected the links.

Is there an alternate way to do that, or is it no longer possible?

The magic of __expr was that AutoHotkey did not parse the expression fully until it was evaluated. Since that is inefficient, it now parses and tokenizes each expression at load time. For dynamically calling functions, the array of tokens can be created directly without much trouble, but a full expression parser would be much more work.

AutoHotkey_L has some improvements to dynamic function calling which I'm hoping Chris will integrate.

Edit:

; [color=red]Requires AutoHotkey pre-v1.0.48 beta[/color]

Call(FunctionName, p1="__deFault__", p2="__deFault__", p3="__deFault__", p4="__deFault__", p5="__deFault__", p6="__deFault__", p7="__deFault__", p8="__deFault__", p9="__deFault__", p10="__deFault__")
{
    return Call_(FunctionName, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
}

Call_(FunctionName, ByRef p1="__deFault__", ByRef p2="__deFault__", ByRef p3="__deFault__", ByRef p4="__deFault__", ByRef p5="__deFault__", ByRef p6="__deFault__", ByRef p7="__deFault__", ByRef p8="__deFault__", ByRef p9="__deFault__", ByRef p10="__deFault__")
{
    static pn:="1,2,3,4,5,6,7,8,9,10", line
        , SYM_INVALID:=56, SYM_VAR:=3, SYM_FUNC:=55, SYM_OPERAND:=4, VAR_ALIAS:=0
    if !arg
        LowLevel_init(), arg := NumGet(NumGet(__findLabel("Call_expression_marker"),4),4)
    if !(func := __findFunc(FunctionName))
        return "", ErrorLevel:=1
    if NumGet(func+16) > 10 ; Give up, requires too many parameters.
        return "", ErrorLevel:=2
    
    ; Get pointer to alias var for use in the loop.
    this_param_var := __getVar(this_param)

    ; postfix must fit one ExprTokenType for each param, followed by a SYM_FUNC token
    ; representing a function call, a SYM_INVALID token terminating the expression and
    ; a DerefType holding a pointer to the function to call.
    VarSetCapacity(postfix, 204, 0) ; 16*(10+2) + 12*1
    this_postfix := &postfix
    
    pc := 0
    Loop, Parse, pn, `,
    {
        ; Ignore any parameters beyond the function's capacity.
        if (pc >= NumGet(func+12))
            break
        
        if (p%A_LoopField% == "__deFault__")
        {
            ; If this parameter is required, just pass empty string.
            if (pc < NumGet(func+16))
                p%A_LoopField% =
            else
                break
        }
        
        var := __getVar(p%A_LoopField%)
        ; Resolve ByRef/alias.
        if (NumGet(var+23,0,"char") = VAR_ALIAS)
            var := NumGet(var+12)
        
        ; Put param var into postfix array.
        NumPut(var, this_postfix+0), NumPut(SYM_VAR, this_postfix+8)
        
        ; Move to next token in postfix array, increment param count.
        this_postfix += 16
        pc++
    }
    
    ; Set up the DerefType at the end of the postfix array.
    func_deref := this_postfix+32
    NumPut(func, func_deref+4)
    NumPut(pc, func_deref+9, 0, "char")
    
    ; Set up SYM_FUNC token.
    NumPut(func_deref, this_postfix+0)
    NumPut(SYM_FUNC, this_postfix+8)
    this_postfix += 16
    
    ; Terminate postfix array.
    NumPut(SYM_INVALID, this_postfix+8)
    
    ; Set postfix array of the first arg of 'return' below.
    NumPut(&postfix, arg+12)
    
    Call_expression_marker:
    return, ("") ; Parentheses to force is_expression=true.
}


bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Wow; so if I'm understanding that correctly (and I definitely could be wrong) it will allow a dynamic function call by specifying the function name as the first parameter and any of 10 function params following it? If so, that should do pretty much take care of my problem.

Thanks for the response! (and explanation of the __expr removal)

  • Guests
  • Last active:
  • Joined: --
Where did you get the source code for the beta?

Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006
Yes.

It works by manually tokenizing a function-call expression. Each token has a symbol defining what the token does, and depending on the symbol, a value. Tokens must be in the order they will be evaluated - i.e. "postfix" order.
[*:2cdu7wtl]SYM_VAR and other operand tokens are pushed onto the stack.
[*:2cdu7wtl]SYM_FUNC causes a function to be called using parameters popped off the stack. ExprTokenType->deref (uint at offset 0) points to a DerefType structure containing the function to call and number of parameters to pass. (I omitted this field from the LowLevel documentation because it wasn't useful in previous versions, but it is now.)
[*:2cdu7wtl]SYM_INVALID marks the end of the expression.The actual values for the SYM_' constants are defined in the LowLevel documentation, under Enumerations -> SymbolType.

The expression could be visualised as p1 p2 p3 %FunctionName%(,,), except that %FunctionName% is resolved before the expression is evaluated. A script parsing a more complex expression would need to take into account precedence rules. For instance, a+b*c becomes a b c * +. DynCalc (and the AutoHotkey source code) went a long way in helping me to understand this process.

Where did you get the source code for the beta?

I asked for it.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Wow, I'm very impressed! :)

Thanks a ton for this function. I can finally upgrade and enjoy the reported substantial speed increase of the beta and still use my scripts' numerous dynamic function calls (albeit in a slightly different way)

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Apr 30 2013 09:57 PM
  • Joined: 04 Dec 2006
@Lexikos,


is there a reason the pre-parsing can't be exposed to allow creation of an expression token array, which can then be evaluated ?

(have not looked deeply at the new code, but was thinking about the eval issue with the pre-processing.)


hope this makes sense, and is not too ignorant of a question.

Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006

allow creation of an expression token array, which can then be evaluated ?

I already demonstrated it with Call() a few posts back.

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Apr 30 2013 09:57 PM
  • Joined: 04 Dec 2006

allow creation of an expression token array, which can then be evaluated ?

I already demonstrated it with Call() a few posts back.


if am understanding correctly, if an eval function could be easily added into AHK with a 'preproccess this' call inserted before the _expr logic.

Am I tracking correctly ?

Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006
If you had a "preprocess this" function to call, then yes, it would be easy to support __expr. However, "preprocess this" would need to fully parse the expression, which is far from simple and would hardly be less work than a standalone expression evaluator.

Perhaps it would not be so bad to have a built-in function using Script::AddLine (since that is what resolves variable/function derefs, and because it may also support commands), with the caveat that each expression would consume memory that cannot be freed until the process terminates. I'm not likely to work on such a feature as there are some usages where this caveat is unacceptable.

Proper dynamic script evaluation is in my "some day" list. It would likely involve a separate build of AutoHotkey (from shared source code, using conditional compilation) which by necessity would have some important memory optimisations disabled. Until then, dynamic function calls and/or temporary scripts should suffice.

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Apr 30 2013 09:57 PM
  • Joined: 04 Dec 2006
haven't had time to get into details or look at new code, but seems good guess should be easy for chris to open the preprocessing to outside call and returned tthe oken list, ie, to include an eval() in AHK.

but, maybe am missing something.

Lexikos
  • Administrators
  • 9391 posts
  • Last active:
  • Joined: 17 Oct 2006

but, maybe am missing something.

See the second paragraph in my previous post.