Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

expression and variable?


  • Please log in to reply
22 replies to this topic
vital
  • Guests
  • Last active:
  • Joined: --

#2::
oldstr=(10*2)a5-16/8
StringReplace,newstr,oldstr,a,+,all
value:=newstr
tooltip,the values is %value%
return



displays "the value is (10*2)+5-16/8",but not "the value is 23.00000",why?

AHKnow*
  • Guests
  • Last active:
  • Joined: --
Dude,

Don't you want...

#2::
oldstr := (10*2)a5-16/8
StringReplace,newstr,oldstr,a,+,all
value := newstr
tooltip,the values is %value%
return

It looks like you but := in the wrong place.

  • Guests
  • Last active:
  • Joined: --

Dude,

Don't you want...

#2::
oldstr:=(10*2)a5-16/8
StringReplace,newstr,oldstr,a,+,all
value = newstr
tooltip,the values is %newstr%
return

It looks like you but := in the wrong place.


displays "the values is 20"

the right is 10*2+5-16/8=23

Like this too...

#2::
oldstr := (10*2)a5-16/8
StringReplace,newstr,oldstr,a,+,all
value := newstr
tooltip,the values is %value%
return


displays "the values is 20"

AHKnow*
  • Guests
  • Last active:
  • Joined: --
Hey guest, I'm just helping correct the AutoHotkey. The other correction was me too, forgot to login again.

I'll leave the math to the mathematicians 8)

vital
  • Members
  • 19 posts
  • Last active: Nov 29 2010 12:02 PM
  • Joined: 05 Oct 2005

oldstr:=(10*2)a5-16/8
msgbox,%oldstr% ;oldstr's values is 10*2=20



Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
oldstr:=(10*2)a5-16/8
gives you 20, because beyond a5 it is not a valid numeric expression any more. StringReplace will not find anything to change. But in any case the whole thing is useless, because AHK has not (yet) got dynamic expression evaluation. If you have an expression in a variable, you cannot directly evaluate it. You need more complex functions, like this one

Dewi Morgan
  • Members
  • 191 posts
  • Last active: Jun 07 2015 04:02 AM
  • Joined: 03 Oct 2005
I guess if you wanted to be able to interpret any ahk expression, you could instead get it to write the variable to be displayed into an .ahk file, then load up ahk on it, and trigger the hotkey to get it to write itself back out to a file, then read in the value from the file.

Like so (I know, I know, "always check the return values of your system calls!" - so pretend I'm checking ErrorLevel here, and I just removed those lines for brevity. I'm just showing the concept, not making the whole dang thing for you!):

[edit]

#x::
ahkPath = "D:\Program Files\AutoHotkey\AutoHotkey.exe"
; tmppath is used both for a temp script and for the result.
; Strangely, quotes around tmpPath seems to break the file commands.
tmpPath = C:\ahktmp.ahk

oldStr = (10*2)a5-16/8
StringReplace newStr, oldStr, a, +, All
startTime = %A_Now%
loopCount = 1000
Loop %loopCount%
{
  value := evaluate(newStr)
}
endTime = %A_Now%
MsgBox,
(
  The value is %value%,
  calculated %loopCount% times
  from %startTime%`
  to %endTime%`
)
return

; Evaluates any dynamic expression.
; On error, returns nothing and leaves ErrorLevel set.
evaluate(string)
{
  global ahkPath
  global tmpPath
  ifExist %tmpPath%
  {
    FileDelete %tmpPath%
    if ErrorLevel
      return
  }
  FileAppend,   ; Create temp script
  (
    #NoTrayIcon
    x:=%string%
    FileDelete %tmpPath%
    FileAppend `%x`%, %tmpPath%
  ), %tmpPath%
  if ErrorLevel
    return
  RunWait %ahkPath% %tmpPath%, , Min UseErrorLevel
  if ErrorLevel
    return
  FileRead result, %tmpPath%
  if ErrorLevel
    return
  FileDelete %tmpPath%
  Return result
}

; Executes any dynamic script.
; On error, returns nothing and leaves ErrorLevel set.
; The second, optional parameter says whether to wait for it to 
; finish executing or not.
; If not, the script must delete itself, and no value is returned.
; This is the default.
; Otherwise, if the script places anything into %outputString%,
; %outputString% is be returned.
; Otherwise, ErrorLevel will be returned.
execute(string, waitForExec=0)
{
  global ahkPath
  global tmpPath
  ifExist %tmpPath%
  {
    FileDelete %tmpPath%
    if ErrorLevel
      return
  }
  FileAppend,   ; Create temp script
  (
    #NoTrayIcon
    %string%
    FileDelete %tmpPath%
    if `%outputString`%
      FileAppend `%outputString`%, %tmpPath%
    else
      FileAppend ErrorLevel, %tmpPath%
  ), %tmpPath%
  if ErrorLevel
    return
  if %waitForExec%
  {
    RunWait %ahkPath% %tmpPath%, , Min UseErrorLevel
    if ErrorLevel
      return 0
    FileRead result, %tmpPath%
    if ErrorLevel
      return 0
    FileDelete %tmpPath%
    Return result
  }
  else
    Run %ahkPath% %tmpPath%, , Min UseErrorLevel
}

Of course, that fails when you want to use variables in your expression that are used in the rest of your program. I couldn't, for example, use %tmpPath% in the expression because it just wouldn't work.

[edit]

[edit2]
Yet another hotkeyer.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Here is a puzzle: If the expression evaluates to an integer, the temporary file (which is executed by a second instance of AHK) need not delete the file and write the result into it, but could use the exit code to return the result. It should be much faster, shouldn't it? But running Dewi Morgan's simplified script the exit code version is sometimes slower, sometimes only a little faster!
tmpFile = %temp%\temp.ahk



Str = 1+2*3

loopCount = 100



startTime = %A_TickCount%

Loop %loopCount%

  value := Eval(Str)

elapsedTime := A_TickCount - startTime

MsgBox %Str% = %value%`n%loopCount% iterations take %elapsedTime% ms



startTime = %A_TickCount%

Loop %loopCount%

  value := EvalI(Str)

elapsedTime := A_TickCount - startTime

MsgBox %Str% = %value%`n%loopCount% iterations take %elapsedTime% ms

return



Eval(string)            ; Evaluates a dynamic expression.

{

  Global tmpFile

  FileDelete %tmpFile%

  FileAppend,           ; Create temp script

  (

    #NoTrayIcon

    x:=%string%

    FileDelete %tmpFile%

    FileAppend `%x`%, %tmpFile%

  ), %tmpFile%

  RunWait %tmpFile%     ; Run AHK to execute temp script

  FileRead result, %tmpFile%

  FileDelete %tmpFile%

  Return result

}



EvalI(string)           ; Evaluates Integer dynamic expression.

{

  Global tmpFile

  FileDelete %tmpFile%  ; Here will be the temp script written

  FileAppend #NoTrayIcon`nExit %string%, %tmpFile%

  RunWait %tmpFile%     ; Run AHK to execute temp script

  Return %ErrorLevel%

}
Does anyone know, why?

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Does anyone know, why?


Minor oversight:

startTime = %A_TickCount%
loopCount = 100

Loop %loopCount%
  value := Eval(Str)
elapsedTime := A_TickCount - startTime
MsgBox,
(
  The value is %value%
  calculated %loopCount% times
  in %elapsedTime% ms
)

startTime = %A_TickCount% ; <-- inserted code
Loop %loopCount%
  value := EvalI(Str)
elapsedTime := A_TickCount - startTime 

You forgot to reset startTime before the second loop.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Thanks! After fixing the start time the exit-code version is still sometimes slower.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I propose a couple of simple changes to Dewi Morgan's nice Execute function, below. I removed the error checking to make the script easier to understand. (Most of the time deleting a file or appending text to it works. I have not seen any error with them for several years, so you can use the function w/o adding back the error checking - unless you want to include the script in commercial SW.)
tmpFile = %temp%\temp.ahk



MsgBox % "Result = " Execute("ErrorLevel := 1+2*3", 1)

MsgBox % Execute("a=1`nb := a+2*3`nErrorLevel = Result = %b%", 1)

S =   ; for variable reference during execution: % -> `%

(

   a = 1

   b := a + 2*3

   ErrorLevel = Result = `%b`%

)

MsgBox % Execute(S,1)



Execute("a := 1+2*3`nMsgBox Result = %a%")



Execute(string, waitForExec=0) ; ErrorLevel is returned after executing string as AHK script

{

  global tmpFile

  FileDelete %tmpFile%

  FileAppend,              ; Create temp script

  (

    #NoTrayIcon

    FileDelete %tmpFile%

    %string%

    FileAppend `%ErrorLevel`%, %tmpFile%

  ), %tmpFile%

  if %waitForExec%

  {

    RunWait %tmpFile%

    FileRead result, %tmpFile%

    FileDelete %tmpFile%

    Return result

  }

  else

    Run %tmpFile%

}


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
An even simpler and (slightly) faster method for evaluating arbitrary dynamic expressions is with returning the result via the ClipBoard.
EvalCB(string)           ; Evaluates dynamic expression, Result -> ClipBoard

{

  FileDelete $temp$.ahk  ; Here will be the temp script written

  FileAppend #NoTrayIcon`nClipBoard:=%string%, $temp$.ahk

  RunWait $temp$.ahk     ; Run AHK to execute temp script

}
A useful application is evaluating arithmetic expressions in documents. Select it, and the Win-Alt-e HotKey puts the result after it. Like, in a week there are 7*24*60 = 10080 minutes. Here " = 10080" was written by the script.
#!e::                    ; Evaluates dynamic expression, Result -> ClipBoard

  Send ^c

  FileDelete $temp$.ahk  ; Here will be the temp script written

  FileAppend #NoTrayIcon`nClipBoard:=%ClipBoard%, $temp$.ahk

  RunWait $temp$.ahk     ; Run AHK to execute temp script

  Send {Right} = ^v

Return
If you want, save the current ClipBoardAll in the beginning and restore it at the end, but then you have ugly side effects in MS Word and WordPerfect.

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
Updated

Another method, slightly more complex, which doesn't sacrifice performance.

PROCESS_VM_OPERATION	= 0x8
PROCESS_VM_WRITE		= 0x20
		
pid := DllCall( "GetCurrentProcessId" )

VarSetCapacity( buffer, 100, 1 )
pBuffer := &buffer

script =
	( LTrim `
		#NoTrayIcon
		
		hp_parent := DllCall( "OpenProcess"
		                , "uint", %PROCESS_VM_OPERATION% | %PROCESS_VM_WRITE%
		                , "int", false
		                , "uint", %pid% )
		                
		DllCall( "WriteProcessMemory"
		       , "uint", hp_parent
		       , "uint", %pBuffer%
		       , "uint", &expression
		       , "uint", StrLen( expression )+1
		       , "uint", &written )
		Exit, *( &written )+( *( &written+1 ) << 8 )+( *( &written+2 ) << 16 )+( *( &written+3 ) << 24 )
	)

SetFormat, Float, 5.0
Random, num, 10000.0, 99999.0

temp_file = temp%num%.ahk

FileDelete, %temp_file%

expression = "1+1 = " 1+1 "``n2+2 = " 2+2 "``netc."

FileAppend, expression := %expression%`n%script%, %temp_file%

RunWait, %temp_file%

; required for binary data
buffer_len := ErrorLevel

FileDelete, %temp_file%

MsgBox, %buffer%
return


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Wow! Could you, my dear Shimanov, add comments and explanations for the mentally challenged (me)?

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Wow!


Thank you.

The design was purpose driven and the technique has been used (and demonstrated) previously; although, not for this purpose.

add comments and explanations


I have added some comments to my original post. However, the Windows API calls are documented at MSDN -- nothing up my sleeve, so to speak.

OpenProcess
WriteProcessMemory