Jump to content

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

ExprEval() - Evaluate Expressions!


  • Please log in to reply
7 replies to this topic
Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010

Evaluates a large subset of AutoHotkey expressions, including function calls, parenthesis, and nearly every operator. Includes a function to compile expressions to a postfix list of tokens, which is then evaluated by the ExprEval() function.

NOTE: Does not support AHK_L object references, ternary operators, or boolean short-circuiting.

Basic Usage:
 

Var = 5
MsgBox % ExprEval(ExprCompile("-Sin(2**Var++)*57.0376+0x20"))

The above works, but if you are going to run an expression many times, store the compiled expression, to make execution faster:
 

Var = 5

CompiledExpression := ExprCompile("-Sin(2**Var++)*57.0376+0x20")
MsgBox % ExprEval(CompiledExpression) ;if this line needs to be executed many times, it will be much faster than the first example

Here are some examples:
 

;Example 1: Calling functions, operator precedence, variable dereferencing, variable assignment

ExprInit() ;initialize the Compile() and Eval() functions

SomeText := "The original content of ""SomeText"".", SomeVar := "Test", Test := "World" ;initialize some variables that will be used in the expression to be evaluated
Expression := "ToolTip(SomeText . ""`n"" . (2 * 2 + 3)), Sleep(2000), ToolTip(SomeText := ""Hello, "" . `%SomeVar`% . ""!`nThis expression is being run dynamically!`nThe new content of """"SomeText"""".""), Sleep(3000), ToolTip()" ;the expression to be evaluated

;compile the expression. Takes about 1.842536 ms for the above expression
CompiledExpression := ExprCompile(Expression)
;evaluate the compiled expression. Takes about 0.616279 ms for the above expression (approximately 28 times slower than actual AHK code)
Result := ExprEval(CompiledExpression)

StringReplace, Result, Result, % Chr(1), `n, All ;format output list for viewing
MsgBox Original expression:`n`n%Expression%`n`nCompiled expression:`n`n%CompiledExpression%`n`nEvaluated::`n`n%Result%

;function called by the evaluated expression
ToolTip(Text = "",X = "",Y = "",WhichToolTip = 1)
{
ToolTip,%Text%,%X%,%Y%,%WhichToolTip%
Return,"The return value of ToolTip()."
}

;function called by the evaluated expression
Sleep(Delay)
{
Sleep,%Delay%
Return,"The return value of Sleep()."
}
;Example 2: Unary minuses, decimal numbers, single and multiline comments

ExprInit() ;initialize the Compile() and Eval() functions

Expression := "2**(4*-1+10)/Floor(-1.3) /* This is a multiline comment */,SomeText:=(2<<1)*-3/Sin(4),1+5*3 `;this is a single line comment`n . SomeText" ;the expression to be evaluated

;compile the expression. Takes about 1.813918 ms for the above expression
CompiledExpression := ExprCompile(Expression)
;evaluate the compiled expression. Takes about 0.616279 ms for the above expression (approximately 28 times slower than actual AHK code)
Result := ExprEval(CompiledExpression)

StringReplace, Result, Result, % Chr(1), `n, All ;format output list for viewing
MsgBox Original expression:`n`n%Expression%`n`nCompiled expression:`n`n%CompiledExpression%`n`nEvaluated::`n`n%Result%

The library also includes these same examples at the top:

Download

Currently looking for ways to implement the ternary operator and short-circuiting. Object access will most likely not be implemented, as it would not be possible to keep AHK Basic compatibility.

One thing that may be confusing is that if you wish to take advantage of the escape sequences feature, you must escape something twice. That is to say:
 

Expression := "ToolTip(""Hello, `nWorld!"")"

Needs to be written as:
 

Expression := "ToolTip(""Hello, ``nWorld!"")"


tomoe_uehara
  • Members
  • 2166 posts
  • Last active: Jun 11 2015 05:33 PM
  • Joined: 05 Sep 2009
Monster: evaluate math expressions in strings
It uses Eval() name too

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Sorry, that was a typo in the description. It was actually ExprEval().

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
New version!

Now with improved error checking, support for up to and including 10 function arguments, support for prefix increment (++Var) as well as (Var++), improved performance of Shunting Yard algorithm.

  • Guests
  • Last active:
  • Joined: --
Would love to use this lib, but did a simple test and ran into a problem. I must be missing something.

The following simple example works fine.
#include Dynamic Expressions.ahk
ExprInit()
Var1 := 12345
Var2 := 54321
Expression := "Var1 = Var2"
if (ExprEval(ExprCompile(Expression)))
   MsgBox True
else
   MsgBox False
But when I went to put it in a loop to test performance like so ...
#include Dynamic Expressions.ahk
ExprInit()
Var1 := 12345
Var2 := 54321
Expression := "Var1 = Var2"
ExpectedValue := "False"
loop, 50
{
   if (ExprEval(ExprCompile(Expression)))
      Value := "True"
   else
      Value := "False"
   if (Value <> ExpectedValue)
   {
      MsgBox ERROR-Expected %ExpectedValue% and got %Value% on interation %A_Index%
      break
   }
}
MsgBox Done
It continuely fails on the second iteration of the loop.

Any help/guidance would be appreciated.

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Sorry, that was a bug (redownload for the fix). Accidentally forgot to clear the stack after evaluation.

I noticed that in your code, the expression is being compiled during each and every run of the loop. This is usually not necessary, and performance can be improved a lot if the expression is precompiled:

ExprInit()
Var1 := 12345
Var2 := 54321
Expression := "Var1 = Var2"
CompiledExpression := ExprCompile(Expression) ;compiled outside of loop, so doesn't need to be done inside it
Loop
{
 if (ExprEval(CompiledExpression))
  MsgBox True
 else
  MsgBox False
}

Compilation is rather slow compared to evaluation.

gpel
  • Members
  • 3 posts
  • Last active: May 02 2011 08:28 PM
  • Joined: 07 Sep 2010
Thanks for the update. Seems to work fine now. I did notice that using != for not equals doesn't seem to work. This isn't really a big deal because using <> does seem to work, but thought you might want to take a look. I'll keep testing.

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Fixed. Forgot to add the != operator, as I'm always using <> myself.