Code: Select all
#Requires AutoHotkey v2
#include <OCR>
result := OCR.FromRect(406, 261, 87, 36, "en-us")
MsgBox eval(RegExReplace(result.Text, "[^\d-+*\\]"))
; Source: https://github.com/TheArkive/eval_ahk2/blob/master/_eval.ahk
; ======================================================================================
; Usage:
;
; result := eval(math_expr, test := false)
;
; Returns the evaluated string expression as an integer or float. If the number is
; too large and AHK returns "inf", then an error is thrown.
;
; If you want to test and see if an expression is valid, then set [ test := TRUE ]:
;
; is_valid := eval(math_expr, true)
;
; If you don't want eval() to throw when a number ends up being too large, then:
;
; result := eval(math_expr, "nothrow")
;
; Be aware that in the case of using "nothrow", then the string "inf" will be
; returned, and it is up to the coder to decide how to handle that.
; ======================================================================================
eval(e,test:=false) {
nothrow := false
If test="nothrow"
nothrow := true, test:=false
e := RegExReplace(e,"(!|~)[ \t]+","$1")
If (test And Trim(e,"`t ") = "")
return false
Else If (test) {
t1 := !RegExMatch(Trim(e),"i)(^[^\d!~\-\x28 ]|! |~ |[g-wyz]+|['\" . '"\$@#%\{\}\[\]\\,;\``_])') ; only return true/false testing "e" as expression
t2 := ( !InStr(e,"++") && !InStr(e,"--"))
t3 := !RegExMatch(e,"i)(?<![a-f\dx])[a-f]")
t4a := !RegExMatch(e,"i)(?<!0)x")
t4b := !RegExMatch(e,"i)x(?![a-f\d])")
t4 := (t4a || t4b)
StrReplace(e,"?","?",,&q) ; count question marks
StrReplace(e,":",":",,&c) ; count colons
t5 := (q=c)
return (t1 && t2 && t3 && t4 && t5)
}
If RegExMatch(e,"i)(! |~ |[g-wyz]+|['\" . '"\$@#%\{\}\[\]\\,;\``_])',&m) ; check for invalid characters, non-numbers, invalid punctuation, etc.
throw Error("Syntax error.`r`n Reason: " Chr(34) m[1] Chr(34) "`r`n`r`nExpression: " e,-1,"Not a math expression.")
If ( InStr(e,"++") || InStr(e,"--") )
throw Error("Syntax error.`r`n Reason: -- and ++ are not valid.",-1,"Not a math expression.")
StrReplace(e,"?","?",,&q) ; count question marks
StrReplace(e,":",":",,&c) ; count colons
If (q!=c)
throw Error("Syntax error.`r`n Reason: ternary statement must be complete with question mark (?) and colon (:).",-1)
StrReplace(e,"(","(",,&LP), StrReplace(e,")",")",,&RP)
If (LP != RP)
throw Error("Invalid grouping with parenthesis. You must ensure the same number of ( and ) exist in the expression.`r`n`r`nExpression:`r`n " e,-1)
e := RegExReplace(e,'(?<!\d)\.','0.') ; fix instances of decimal without leading integer
While RegExMatch(e, "i)(\x28[^\x28\x29]+\x29)", &m) { ; match phrase surrounded by parenthesis, inner-most first
ans := _eval(match := m[0]) ; match and calculate result
ans := (SubStr(ans,1,1) = "-") ? " " ans : ans ; resolved sub-expr value, add space for legit negative sign, ie. " -3"
e := RegExReplace(StrReplace(e,match,ans,,,1),"(!|~) +","$1") ; perform substitution, remove resulting spaces between !/~ and resolved value
If e="inf"
Break
}
If e!="inf"
e := _eval(e)
If IsInteger(e)
return Integer(e)
Else if (e="inf") && nothrow
return e
Else if (e="inf")
throw Error("Number too large.",-1)
Else
return Float(e)
}
_eval(e) { ; support function for pure math expression without parenthesis
If IsNumber(e)
return e
If RegExMatch(e,"i)(^[^\d!~\-\x28 ]|! |~ |[g-wyz]+|['\" . '"\$@#%\{\}\[\]\\,;\``_])',&m) ; check for invalid characters, non-numbers, invalid punctuation, etc.
throw Error("Syntax error.`r`n Reason: " Chr(34) m[1] Chr(34),-1,"Not a math expression.")
Static _n := "(?:\d+\.\d+(?:e\+\d+|e\-\d+|e\d+)?|0x[\dA-F]+|\d+)" ; Regex to identify float/scientific notation, then hex, then base-10 numbers. Only positive.
Static _num := "([!~\-]*" _n ")" ; Expand number definition to include - / ~ / !
Static _ops := "(?:\*\*|\*|//|/|\+|\-|>>>|<<|>>|&&|&|\^|" ; Define list of operators, in order of prescedence.
. "\|\|" . "|" . "\|" . "|" . ">=|<=|>|<|!=|==|=|\?|:)"
new_e := "", p := 1, prev_m := ""
typ := "number", expr := _num ; Start looking for a number first.
While RegExMatch(e,"i)" expr,&_m,p) { ; Separate numbers and operators (except !/~ operators) with spaces.
mat := _m[0] ; Capture match pattern. Pattern starts with "number" / alternates with "oper".
If (typ="number") { ; Alternate the RegEx search between numbers and operators to improve grouping/spacing of the expression.
mat := RegExReplace(_m[1],"\-(\d+)","#$1") ; find "negative" values, replace "-" with "#"
typ := "oper"
expr := _ops
} Else {
typ := "number"
expr := _num
}
new_e .= ((new_e!="")?" ":"") mat
p := _m.Pos(0) + _m.Len(0)
}
e := RegExReplace(new_e," {2,}"," ") ; Replace e with spaced-out/grouped expression, and replace multiple spaces with single space.
old_e := e
Static order := "** !~ */ +- <> &^| >= == && ?:" ; Order of operations with appropriate grouping.
Static opers := StrSplit(order," ")
Static n := "#?" _n ; Basic number defiintion with # in place - for negative numbers.
For i, op in opers { ; Loop through operators in order of prescedence.
If e="inf"
return e
Switch op {
Case "**":
val2 := "", i_count := 0 ; just in case...
sub_e := "", new_sub_e := "" ; Initialize temp vars for the building of sub-expressions.
p := 1 ; Position tracking.
fail_count := 0 ; These expressions can be broken up in different sections in the main expression, so track search fails.
Static rg_ex1 := "([#!~\-]*" _n ")( *\*\* *)" ; RegEx for number and exponent (**). For 1st iteration in next WHILE loop.
Static rg_ex2 := "([#!~\-]*" _n ")( *\*\* *)?" ; RegEx for number and maybe exponent (**) for 2nd+ iteration in next WHILE loop.
rg_ex := rg_ex1
While (r := RegExMatch(e,"i)" rg_ex,&z,p)) { ; Extract expr before resolving, because this needs to be right-to-left.
(z[2] = "") ? fail_count++ : "" ; Increment fail count when "**" not found. fail_count = 1 means the end of a sub-expr, but there may be more.
p := z.Pos(0) + z.Len(0) ; Adjust search position.
sub_e .= z[0] ; Append valid match to sub_e.
If (fail_count = 1 And InStr(sub_e,"**")) { ; ****** End of exponenent expression, so evalute and replace. ******
new_sub_e := sub_e
While RegExMatch(new_sub_e,"i)([#!~]*)?(" _n ") *(\*\*) *([#!~\-]*" _n ")$",&y) { ; Get last 2 operands and operator with any unary - ! or ~.
mat := y[0] ; Capture full match.
o_op := y[1] ; Outside operators must be solved last, ie. -2 ** 3 is -(2 ** 3).
v1 := y[2] ; First operand.
v2 := StrReplace(y[4],"#","-") ; Switch "#" to "-"
v2 := _eval(v2) ; Evaluate the exponent (2nd operand), resolve all ! and ~ first. This behavior is undocumented in AHK v2.
val2 := v1 ** v2 ; Resolve sub-sub-expression.
new_sub_e := RegExReplace(new_sub_e,"\Q" mat "\E$",o_op val2) ; Ensure this substitution only happens at the end of the sub-expression.
}
RegExMatch(val2,"([\-!~]*)?(" _n ")",&y) ; Check for "-" to convert to "#".
If (IsObject(y) And InStr(y[1],"-"))
val2 := StrReplace(y[1],"-","#") y[2]
e := StrReplace(e,sub_e,new_sub_e,,,1) ; Replace only the first instance of the match. Maintain "#" sub for "-".
sub_e := "", new_sub_e := "" ; Reset temp vars / sub-expressions.
p := 1, fail_count := 0 ; Reset postion tracking and fail_count. Continue looping for another exponent sub-expression.
} Else If (fail_count > 1) ; No more exponent expressions to evaluate, so Break.
Break
(A_Index = 1) ? rg_ex := rg_ex2 : "" ; Switch to new search right before 2nd iteration.
}
Case "!~":
While (r := RegExMatch(e,"i)(!|\~)" n,&z)) { ; Find "inner most" expression and solve first.
_op := z[1]
_mat := z[0]
v1 := StrReplace(SubStr(_mat,2),"#","-") ; Omit regex stored operator (! or ~) and convert "#" to "-".
If (_op = "!")
val2 := !v1
Else If (_op = "~") {
If !IsInteger(v1)
throw Error("Bitwise NOT (~) operator against non-integer value.`r`n Invalid operation: ~" v1,-1,"Bitwise operation with non-integer.")
v1 := Integer(v1)
val2 := ~v1
} Else
throw Error("Unexpected error in NOT (! / ~) expression.",-1,"First char is not ! or ~.`r`n`r`n Sub-Expression: " _mat)
e := StrReplace(e,_mat,StrReplace(val2,"-","#"),,,1) ; Substitute resolved value in main expression.
e := RegExReplace(e,"\-(\d+)","#$1")
e := RegExReplace(e,"\-#","") ; The only time a double negative "--" won't throw an error, so "##" will cancel itself out.
}
Default: ; basic left-to-right operations that need to be grouped together
Switch op {
Case "*/": op_reg := "\*|//|/"
Case "+-": op_reg := "\+|\-"
Case "<>": op_reg := ">>>|<<|>>"
Case "&^|": op_reg := "\&|\^|\|"
Case ">=": op_reg := ">\=|<\=|>|<"
Case "==": op_reg := "!=|==|="
Case "&&": op_reg := "&&" . "|" . "\|\|" ; && then ||
Case "?:":
If !(q := InStr(e,"?"))
Continue
c := InStr(e,":")
expr := StrReplace(SubStr(e,1,q-1),"#","-")
expr := _eval(expr)
res_A := SubStr(e,q+1,c-q-1)
res_B := SubStr(e,c+1)
e := (expr) ? Trim(res_A) : Trim(res_B)
Continue
}
While (r := RegExMatch(e,"i)(" n ") +(" op_reg ") +(" n ")",&z)) {
o := z[2]
v1 := StrReplace(z[1],"#","-"), v2 := StrReplace(z[3],"#","-")
; =========================================================
; capture operator-specific errors
; =========================================================
If (o = "<<" Or o = ">>") And (!IsInteger(v1) Or !IsInteger(v2) Or v2<0) ; check for invalid expressions
throw Error("Invalid expression.`r`n Expr: " v1 " " o " " v2,-1,"Bit shift with non-integers.")
If (o = "/" Or o = "//") And v2=0
throw Error("Invalid expression.`r`n Expr: " v1 " " o " " v2,-1,"Divide by zero.")
If (o = "//") And (!IsInteger(v1) Or !IsInteger(v2))
throw Error("Invalid expression.`r`n Expr: " v1 " " o " " v2,-1,"Floor division with non-integer divisor.")
If (o = "&" Or o = "^" Or o = "|") And (!IsInteger(v1) Or !IsInteger(v2))
throw Error("Invalid expression.`r`n Expr: " v1 " " o " " v2,-1,"Bitwise operation with non-integers.")
(IsFloat(v1)) ? v1 := Float(v1) : (IsInteger(v1)) ? v1 := Integer(v1) : ""
(IsFloat(v2)) ? v2 := Float(v2) : (IsInteger(v2)) ? v2 := Integer(v2) : ""
Switch o {
Case "*": val2 := v1 * v2
Case "//": val2 := v1 // v2
Case "/": val2 := v1 / v2
Case "+": val2 := v1 + v2
Case "-": val2 := v1 - v2
Case ">>>": val2 := v1 >>> v2
Case "<<": val2 := v1 << v2
Case ">>": val2 := v1 >> v2
Case "&": val2 := v1 & v2
Case "^": val2 := v1 ^ v2
Case "|": val2 := v1 | v2
Case ">=": val2 := v1 >= v2
Case "<=": val2 := v1 <= v2
Case ">": val2 := v1 > v2
Case "<": val2 := v1 < v2
Case "!=": val2 := v1 != v2
Case "==": val2 := v1 == v2
Case "=": val2 := v1 = v2
Case "&&": val2 := v1 && v2
Case "||": val2 := v1 || v2
}
e := StrReplace(e,z[0],StrReplace(val2,"-","#"),,,1)
}
r := 0 ; disable substitution before next iteration in FOR loop, because these subs were already done
}
If IsNumber(StrReplace(e,"#","-"))
Break
}
e := StrReplace(e,"#","-")
If IsNumber(StrReplace(e,"#","-")) {
final := StrReplace(e,"#","-")
If IsInteger(final)
return Integer(final)
Else If IsFloat(final)
return Float(final)
Else
throw Error("fix this type: " Type(final),-1) ; this isn't supposed to be here, but just in case there's some weird type conflict, please tell me and post example.
} Else {
return e
}
}