 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Lexikos
Joined: 17 Oct 2006 Posts: 2472 Location: Australia, Qld
|
Posted: Thu Dec 06, 2007 2:13 pm Post subject: LowLevel & dynamic code |
|
|
LowLevel.ahk contains a collection of advanced functions for AutoHotkey scripts. The base functions allow a script to access the so-called "low-level" data structures which form it. Additional functions provide other functionality by accessing these data structures. See the included help file for a list of functions.
code.ahk provides functions to simplify generating code while the script is running. For a basic overview, see Generating Code in the included help file.
Requirements: AutoHotkey v1.0.47.06+
Known limitations and issues:
- User-defined functions are only accessible if referenced in script or defined after a function which is referenced in script.
- Since 'AND' and 'OR' are normally replaced at load-time with '&&' or '||', they are not supported by __expr or __ParseExpressionArg. Use '&&' and '||' instead.
- Double-derefs in __expr always resolve to a global variable.
- If a local variable is used as the base of an array for an array-creating function such as RegExMatch, the individual array variables are created local to __expr_sub - i.e. inaccessible to the script.
Download
Last edited by Lexikos on Mon Jun 09, 2008 2:50 am; edited 20 times in total |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3589 Location: Belgrade
|
Posted: Thu Dec 06, 2007 2:22 pm Post subject: |
|
|
You are crazy. _________________
 |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3589 Location: Belgrade
|
Posted: Thu Dec 06, 2007 2:33 pm Post subject: |
|
|
This acctually works
| Code: | #singleinstance, force
Gui, Add, Text, Enter AHK code
Gui, Add, Edit, w300 vCode
Gui, Add, Button, Default gOnButton, Execute
Gui, Add, Text, vRes x+50 w200
Gui, Show, autosize
__init()
return
OnButton:
Gui, Submit, NoHide
res := __expr(code)
GuiControl, , res, %res%
return
This expression doesn't work though: 1 ? 5 : 6
Hello(){
return "Hi there"
}
Msg() {
MsgBox Hi there
}
#include lowlevel.ahk |
_________________
 |
|
| Back to top |
|
 |
DerRaphael
Joined: 23 Nov 2007 Posts: 379 Location: Heidelberg, Germany
|
Posted: Thu Dec 06, 2007 4:06 pm Post subject: |
|
|
@lexikos
This is ...... actually i can't find words for it.
Let's call it great. Fantastic. Awesome.
Im still shaking my head, being so impressed.
btw i tried majkinetors lil example and it works fine (Version 1.0.47.5)
When you're still looking for a name, why not naming it:
AhkLowLevelAccess (ALLA)
AccessLowLevel (ALL)
Access_AhkLowLevel (A_ALL)
great job and many thanks for sharing it
derRaphael |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2472 Location: Australia, Qld
|
Posted: Thu Dec 06, 2007 4:10 pm Post subject: |
|
|
| majkinetor wrote: | | This expression doesn't work though: 1 ? 5 : 6 |
Works for me.
| Code: | MsgBox % __expr("1 ? 5 : 6")
| I did, however, find and fix two bugs in function deref parsing. "func0()+1" would not work when func0 has no parameters because the ')' was skipped and "+1" was seen as a parameter. "func0()" worked because after the ')' is nothing. "func1(func0())" would not work because the inner ')' was skipped and the outer ')' was miscounted as a parameter.
Btw, __expr doesn't do any expression evaluation. It constructs a Line and ArgStruct, and creates a DerefType for each var or func reference. It then assigns the Line to the __expr_sub function, and calls it.
Basically, it emulates the load-time processing of AutoHotkey.
If no var or func references are needed, it can be considerably simpler. The following is the third iteration of __expr (where the one in lowlevel.ahk is the fourth):
| Code: | MsgBox % __expr("1 ? 5 : 6")
__expr(expr)
{
static line, arg
if !VarSetCapacity(line) {
VarSetCapacity(line,32,0), VarSetCapacity(arg,12,0)
NumPut(102,line,0,"char") ; line.mActionType = ACT_RETURN
NumPut(&arg,line,4) ; line.mArg = &arg
; Give mNextLine and mPrevLine non-NULL values in case the expression
; somehow causes a critical error, in which case AutoHotkey may try
; to use these to display the error line and the lines around it.
NumPut(&line,NumPut(&line,line,16)) ; line.mNextLine = line.mPrevLine = &line
NumPut(1,arg,1,"char") ; arg.is_expression = true
cb:=RegisterCallback("__expr_last")
NumPut(&line,NumGet(cb+28),4) ; cb->Func->mJumpToLine = &line
DllCall("GlobalFree","uint",cb)
}
NumPut(1,line,1,"char") ; line.mArgc = 1
NumPut(StrLen(expr)+1,arg,2,"short") ; arg.length = StrLen(expr)+1
NumPut(&expr,arg,4) ; arg.text = &expr
ret := __expr_last()
; Reset mArgc to 0 so ListLines does not try to read 'expr', which will
; become invalid once __expr() returns.
NumPut(0,line,1,"char") ; mArgc = 0
return ret
}
__expr_last() {
} |
I will eventually write a function to evaluate commands. (This is why I wrote __MakeExpressionArg separate from __expr.) The command names must be translated to ActionType codes, and derefs in non-expression args must also be parsed.
Once that's done, a little extra work must be done to link the lines together properly and form a working dynamic script (with If, Loop, etc.)
You noticed? I lost much sleep in the aftermath of high-speed idea collision.
| DerRaphael wrote: | @lexikos
This is ...... actually i can't find words for it.
Let's call it great. Fantastic. Awesome.
Im still shaking my head, being so impressed. | I was in shock for a while after coming up with "the idea."  |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3589 Location: Belgrade
|
Posted: Thu Dec 06, 2007 4:30 pm Post subject: |
|
|
This accutally allows us to read internal function variables as this example shows (and so to change it, without calling the function):
| Code: | ppFunc := __findFunc("Module_Globals")
ppvar := __findVar("var1", ppFunc, 1)
mContents := NumGet(ppVar+0)
buf := "some new text"
DllCall("lstrcpyn", "uint", mContents, "uint", &buf, "int", StrLen(buf)+1)
Msgbox % __str( mContents , -1 )
return
Module_Globals(){
static var1 := "this is some internal function text"
}
#include lowlevel.ahk |
_________________
 |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3589 Location: Belgrade
|
Posted: Thu Dec 06, 2007 4:49 pm Post subject: |
|
|
I just benchmarked in optimal case, and it doesn't provide benefits comparing to using function call to return variable result. Check it out:
| Code: | ppFunc := __findFunc("Module_Globals")
ppvar := __findVar("var1", ppFunc, 1)
mContents := NumGet(ppVar+0)
hKernal := DllCall("LoadLibrary", "str", "Kernel32.dll")
pMulDiv := DllCall("GetProcAddress", "Uint", hKernal , "uint", 612)
t := A_TickCount
loop, 100000
s := Module_Globals("var1")
t0 := A_tickCount - t
t := A_TickCount
loop, 100000
s := DllCall(pMulDiv,"uint",mContents,"int",1,"int",1,"str")
t1 := A_tickCount - t
MsgBox %t0% %t1%
return
Module_Globals(name){
static var1 := "this is some internal function text", var2 := 123
val := %name%
return val
}
#include lowlevel.ahk |
Neverthe less, static local arrays are valuable thing.
Enough with that, I am impatiently waiting for full dynamic code. That will open metaprogramming dimension in AHK.
The only bad thing about this is that it is 100% AHK version dependent.
Perhaps Chris can use your work to implement dynamic code internaly... _________________
 |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3941 Location: Pittsburgh
|
Posted: Thu Dec 06, 2007 5:40 pm Post subject: |
|
|
Amazing! We have been waiting for these for years! Now dynamic expression evaluation works from compiled scripts, and without temporary files.
Ternary operators dont work for me, either (return blank) with the downloaded lowlevel.ahk. Also, 1 ? 2 results in 12, which could indicate that the ? is not treated properly.
As majkinetor showed, we can have a calculator with 9 AHK commands (after including lowlevel.ahk): | Code: | Gui Add, Edit, w300 vCode
Gui Add, Button, w0 Default gExec
Gui Add, Text, vRes x+0 w200, Result =
Gui Show, AutoSize, Evaluate AHK expression {Enter}
__init()
Exec:
Gui Submit, NoHide
GuiControl,,res, % "Result = " . __expr(code)
return |
The double underscore prefix looks informative for me. How much lower level can a prefix be?
Edit 20071207: Actually, 3 AHK commands are enough for a calculator. | Code: | Loop {
InputBox code,Evaluate AHK expression,% "Result = " __expr(code),,300,120,,,,,%code%
IfEqual ErrorLevel,1, Break
}
#include lowlevel.ahk | Assign it to a hotkey, and you have a fast simple, always available calculator.
Last edited by Laszlo on Sat Dec 08, 2007 2:25 am; edited 1 time in total |
|
| Back to top |
|
 |
engunneer
Joined: 30 Aug 2005 Posts: 6307 Location: Pacific Northwest, US
|
Posted: Thu Dec 06, 2007 6:26 pm Post subject: |
|
|
but can you call the script _.ahk and be stdlib compliant?
I think we need some prefix here so it can be added to the stdlib. This is obviously quite powerful.
edit:
_.ahk in my lib did not work. _________________
Unless otherwise noted, all code is untested.
Common Answers: 1.(Loops, Viruses, etc.) 2. Search 3.RTFM |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3589 Location: Belgrade
|
Posted: Fri Dec 07, 2007 12:12 am Post subject: |
|
|
I think it should be __.ahk  _________________
 |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2472 Location: Australia, Qld
|
Posted: Fri Dec 07, 2007 1:19 am Post subject: |
|
|
| Laszlo wrote: | | Ternary operators dont work for me, either (return blank) with the downloaded lowlevel.ahk. Also, 1 ? 2 results in 12, which could indicate that the ? is not treated properly. | As I pointed out, __expr doesn't evaluate the expression. It does, however, create a deref for "?"... It worked in my previous test because I forgot to call __init().
| Quote: | The double underscore prefix looks informative for me. How much lower level can a prefix be?  | Heh, that's priceless.
| engunneer wrote: | | but can you call the script _.ahk and be stdlib compliant? | As I said, the script is std-lib compliant if named ".ahk". AutoHotkey uses everything up to the first under-score (in this case nothing) as the prefix. Explorer won't let you give it "no name," but Firefox will. You can also use a command prompt (or AutoHotkey script!) to rename it. I only named the file lowlevel.ahk because Firefox would save ".ahk.html".
Edit: Script updated.
Last edited by Lexikos on Fri Dec 07, 2007 1:25 am; edited 1 time in total |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2472 Location: Australia, Qld
|
Posted: Fri Dec 07, 2007 1:30 am Post subject: |
|
|
| Moderator! wrote: | this is amazing... thanks. while this opens many new opportunites, i am worried about the internals of AHK being discussed in public. IMHO, you could have opted the "appropriate section" of our forum. please consider. thanks again for your wonderful contribution.  | Come again? AutoHotkey is open source. Anyone that understands my script could most likely understand the AutoHotkey source. Anyway, how could any of this be exploited? The "entry point" is RegisterCallback, which must be called by the script itself. As for the "appropriate section," what would that be?
| majkinetor wrote: | The only bad thing about this is that it is 100% AHK version dependent.
Perhaps Chris can use your work to implement dynamic code internaly... |
I don't imagine so. The main barrier to building in dynamic code functionality is that script objects (Line, ArgStruct, DerefType, etc.) use "persistent" memory (avoiding the overhead of dynamic memory allocations), so can't be deleted. Changing this behaviour would affect the performance (at least, memory usage) of all scripts. This isn't an issue with the script, since it allocates the structures itself. (Doing it this way in AutoHotkey itself would involve duplicating a considerable amount of code.)
Perhaps I'll take a crack at it some time, and see how it performs. For now I'm trying to focus on my many unfinished projects. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3941 Location: Pittsburgh
|
Posted: Fri Dec 07, 2007 4:00 am Post subject: |
|
|
| lexikos wrote: | | Edit: Script updated. | Thanks! The ternary op works now in the calculator example! |
|
| Back to top |
|
 |
corrupt
Joined: 29 Dec 2004 Posts: 2381
|
Posted: Fri Dec 07, 2007 7:56 am Post subject: |
|
|
Cool Thanks  |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2472 Location: Australia, Qld
|
Posted: Sat Dec 08, 2007 8:57 am Post subject: |
|
|
This morning (it's nearly 6 PM here) I started working on __findVar, with the intention of adding support for creating local variables. It got up to 55 lines before I realised what a PAIN it would be to write code to insert a local variable into the (mVar or mLazyVar) array, since I'd potentially have to reallocate mVar and/or move Vars from mLazyVar into mVar, all the while preserving alphabetical order. {gasps for air}
Luckily, I thought of a way to make __findVar smaller, more capable, and more efficient:
Instead of searching some function's local variable arrays, simply switch them with __findVar's arrays temporarily and perform a double-deref! Consequently, I have renamed __findVar to __getVarInContext.
| Quote: | __getVarInContext( sVarName [, pScopeFunc ] )
Retrieves the Var structure of a variable with the given name in the context of the specified function (or in global context if no function is specified), with the same semantics as a double-deref. pScopeFunc is a pointer to a Func, not a callback or function name.
|
| Quote: | | If a thread running __getVarInContext is interrupted between the second and last RtlMoveMemory calls and __getVarInContext is called again (causing recursion), the local variables in the first instance of __getVarInContext are overwritten. This should be very rare, considering the very small "window of inopportunity." |
I have also removed the "LocalVarsOnly" parameter, since it is no longer applicable. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|