/thirdParty/Eval.ahk

https://github.com/camerb/AHKs · AutoHotKey · 287 lines · 183 code · 20 blank · 84 comment · 3 complexity · ebb78850763b8e2295086f8c088ce892 MD5 · raw file

  1. ;?add 2010 modified by Tuncay to make it stdlib conform.
  2. ;?add Outcommented lines from autoexecution section.
  3. ;?add Added the prefix Eval_ to all functions now.
  4. ;?add Eval1() becomes Eval_1() and Fib() becomes Eval_Fib().
  5. ;?add Originally by Laszlo at http://www.autohotkey.com/forum/viewtopic.php?t=17058
  6. ; MONSTER Version 1.1 (needs AHK 1.0.46.12+)
  7. ; EVALUATE ARITHMETIC EXPRESSIONS containing HEX, Binary ('1001), scientific numbers (1.2e+5)
  8. ; (..); variables, constants: e, pi, inch, foot, mile, ounce, pint, gallon, oz, lb;
  9. ; (? :); logicals ||; &&; relationals =,<>; <,>,<=,>=; user operators GCD,MIN,MAX,Choose;
  10. ; |; ^; &; <<, >>; +, -; *, /, \ (or % = mod); ** (or @ = power); !,~;
  11. ; Functions Abs|Ceil|Exp|Floor|Log|Ln|Round|Sqrt|Sin|Cos|Tan|ASin|ACos|ATan|SGN|Fib|fac
  12. ; User defined functions: f(x) := expr;
  13. ; Output: $x,$h: Hex; $b{W}: W-bit binary; ${k},${k}e,${k}g: k-digit decimal (FixP/Sci), Default $6g
  14. ; "Assignments;" can preceed an expression: a:=1; b:=2; a+b
  15. ;?out-begin
  16. /*
  17. #SingleInstance Force
  18. #NoEnv
  19. SetBatchLines -1
  20. Process Priority,,High
  21. xe := 2.718281828459045, xpi := 3.141592653589793 ; referenced as "e", "pi"
  22. xinch := 2.54, xfoot := 30.48, xmile := 1.609344 ; [cm], [cm], [Km]
  23. xounce := 0.02841, xpint := 0.5682, xgallon := 4.54609 ; liters
  24. xoz := 28.35, xlb := 453.59237 ; gramms
  25. */
  26. ;?out-end
  27. /* -test cases
  28. MsgBox % Eval("$2E 1e3 -50.0e+0 + 100.e-1") ; 9.60E+002
  29. MsgBox % Eval("fact(x) := x < 2 ? 1 : x*fact(x-1); fact(5)") ; 120
  30. MsgBox % Eval("f(ab):=sqrt(ab)/ab; y:=f(2); ff(y):=y*(y-1)/2/x; x := 2; y+ff(3)/f(16)") ; 6.70711
  31. MsgBox % Eval("x := qq:1; x := 5*x; y := x+1") ; 6 [if y empty, x := 1...]
  32. MsgBox % Eval("x:=-!0; x<0 ? 2*x : sqrt(x)") ; -2
  33. MsgBox % Eval("tan(atan(atan(tan(1))))-exp(sqrt(1))") ; -1.71828
  34. MsgBox % Eval("---2+++9 + ~-2 --1 -2*-3") ; 15
  35. MsgBox % Eval("x1:=1; f1:=sin(x1)/x1; y:=2; f2:=sin(y)/y; f1/f2") ; 1.85082
  36. MsgBox % Eval("Round(fac(10)/fac(5)**2) - (10 choose 5) + Fib(8)") ; 21
  37. MsgBox % Eval("1 min-1 min-2 min 2") ; -2
  38. MsgBox % Eval("(-1>>1<=9 && 3>2)<<2>>1") ; 2
  39. MsgBox % Eval("(1 = 1) + (2<>3 || 2 < 1) + (9>=-1 && 3>2)") ; 3
  40. MsgBox % Eval("$b6 -21/3") ; 111001
  41. MsgBox % Eval("$b ('1001 << 5) | '01000") ; 100101000
  42. MsgBox % Eval("$0 194*lb/1000") ; 88 Kg
  43. MsgBox % Eval("$x ~0xfffffff0 & 7 | 0x100 << 2") ; 0x407
  44. MsgBox % Eval("- 1 * (+pi -((3%5))) +pi+ 1-2 + e-ROUND(abs(sqrt(floor(2)))**2)-e+pi $9") ; 3.141592654
  45. MsgBox % Eval("(20+4 GCD abs(2**4)) + (9 GCD (6 CHOOSE 2))") ; 11
  46. t := A_TickCount
  47. Loop 1000
  48. r := Eval("x:=" A_Index/1000 ";atan(x)-exp(sqrt(x))") ; simulated plot
  49. t := A_TickCount - t
  50. MsgBox Result = %r%`nTime = %t% ; -1.93288. ~380 ms
  51. */
  52. ;?out-begin
  53. /*
  54. ^#-:: ; Replace selection or `expression with result
  55. ^#=:: ; Append result to selection or `expression
  56. ClipBoard =
  57. SendInput ^c ; copy selection
  58. ClipWait 0.5
  59. If (ErrorLevel) {
  60. SendInput +{HOME}^c ; copy, keep selection to overwrite (^x for some apps)
  61. ClipWait 1
  62. IfEqual ErrorLevel,1, Return
  63. If RegExMatch(ClipBoard, "(.*)(``)(.*)", y)
  64. SendInput % "{RAW}" y1 . (A_ThisHotKey="^#=" ? y3 . " = " : "") . Eval(y3)
  65. } Else
  66. SendInput % "{RAW}" . (A_ThisHotKey="^#=" ? ClipBoard . " = " : "") . Eval(ClipBoard)
  67. Return
  68. */
  69. ;?out-end
  70. Eval(x) { ; non-recursive PRE/POST PROCESSING: I/O forms, numbers, ops, ";"
  71. Local FORM, FormF, FormI, i, W, y, y1, y2, y3, y4
  72. FormI := A_FormatInteger, FormF := A_FormatFloat
  73. SetFormat Integer, D ; decimal intermediate results!
  74. RegExMatch(x, "\$(b|h|x|)(\d*[eEgG]?)", y)
  75. FORM := y1, W := y2 ; HeX, Bin, .{digits} output format
  76. SetFormat FLOAT, 0.16e ; Full intermediate float precision
  77. StringReplace x, x, %y% ; remove $..
  78. Loop
  79. If RegExMatch(x, "i)(.*)(0x[a-f\d]*)(.*)", y)
  80. x := y1 . y2+0 . y3 ; convert hex numbers to decimal
  81. Else Break
  82. Loop
  83. If RegExMatch(x, "(.*)'([01]*)(.*)", y)
  84. x := y1 . Eval_FromBin(y2) . y3 ; convert binary numbers to decimal: sign = first bit
  85. Else Break
  86. x := RegExReplace(x,"(^|[^.\d])(\d+)(e|E)","$1$2.$3") ; add missing '.' before E (1e3 -> 1.e3)
  87. ; literal scientific numbers between ‘ and ’ chars
  88. x := RegExReplace(x,"(\d*\.\d*|\d)([eE][+-]?\d+)","‘$1$2’")
  89. StringReplace x, x,`%, \, All ; % -> \ (= MOD)
  90. StringReplace x, x, **,@, All ; ** -> @ for easier process
  91. StringReplace x, x, +, ą, All ; ą is addition
  92. x := RegExReplace(x,"(‘[^’]*)ą","$1+") ; ...not inside literal numbers
  93. StringReplace x, x, -, Ź, All ; Ź is subtraction
  94. x := RegExReplace(x,"(‘[^’]*)Ź","$1-") ; ...not inside literal numbers
  95. Loop Parse, x, `;
  96. y := Eval_1(A_LoopField) ; work on pre-processed sub expressions
  97. ; return result of last sub-expression (numeric)
  98. If FORM = b ; convert output to binary
  99. y := W ? Eval_ToBinW(Round(y),W) : Eval_ToBin(Round(y))
  100. Else If (FORM="h" or FORM="x") {
  101. SetFormat Integer, Hex ; convert output to hex
  102. y := Round(y) + 0
  103. }
  104. Else {
  105. W := W="" ? "0.6g" : "0." . W ; Set output form, Default = 6 decimal places
  106. SetFormat FLOAT, %W%
  107. y += 0.0
  108. }
  109. SetFormat Integer, %FormI% ; restore original formats
  110. SetFormat FLOAT, %FormF%
  111. Return y
  112. }
  113. Eval_1(x) { ; recursive PREPROCESSING of :=, vars, (..) [decimal, no ";"]
  114. Local i, y, y1, y2, y3
  115. ; save function definition: f(x) := expr
  116. If RegExMatch(x, "(\S*?)\((.*?)\)\s*:=\s*(.*)", y) {
  117. f%y1%__X := y2, f%y1%__F := y3
  118. Return
  119. }
  120. ; execute leftmost ":=" operator of a := b := ...
  121. If RegExMatch(x, "(\S*?)\s*:=\s*(.*)", y) {
  122. y := "x" . y1 ; user vars internally start with x to avoid name conflicts
  123. Return %y% := Eval_1(y2)
  124. }
  125. ; here: no variable to the left of last ":="
  126. x := RegExReplace(x,"([\)’.\w]\s+|[\)’])([a-z_A-Z]+)","$1Ť$2ť") ; op -> Ťopť
  127. x := RegExReplace(x,"\s+") ; remove spaces, tabs, newlines
  128. x := RegExReplace(x,"([a-z_A-Z]\w*)\(","'$1'(") ; func( -> 'func'( to avoid atan|tan conflicts
  129. x := RegExReplace(x,"([a-z_A-Z]\w*)([^\w'ť’]|$)","%x$1%$2") ; VAR -> %xVAR%
  130. x := RegExReplace(x,"(‘[^’]*)%x[eE]%","$1e") ; in numbers %xe% -> e
  131. x := RegExReplace(x,"‘|’") ; no more need for number markers
  132. Transform x, Deref, %x% ; dereference all right-hand-side %var%-s
  133. Loop { ; find last innermost (..)
  134. If RegExMatch(x, "(.*)\(([^\(\)]*)\)(.*)", y)
  135. x := y1 . Eval_@(y2) . y3 ; replace (x) with value of x
  136. Else Break
  137. }
  138. Return Eval_@(x)
  139. }
  140. Eval_@(x) { ; EVALUATE PRE-PROCESSED EXPRESSIONS [decimal, NO space, vars, (..), ";", ":="]
  141. Local i, y, y1, y2, y3, y4
  142. If x is number ; no more operators left
  143. Return x
  144. ; execute rightmost ?,: operator
  145. RegExMatch(x, "(.*)(\?|:)(.*)", y)
  146. IfEqual y2,?, Return Eval_@(y1) ? Eval_@(y3) : ""
  147. IfEqual y2,:, Return ((y := Eval_@(y1)) = "" ? Eval_@(y3) : y)
  148. StringGetPos i, x, ||, R ; execute rightmost || operator
  149. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) || Eval_@(SubStr(x,3+i))
  150. StringGetPos i, x, &&, R ; execute rightmost && operator
  151. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) && Eval_@(SubStr(x,3+i))
  152. ; execute rightmost =, <> operator
  153. RegExMatch(x, "(.*)(?<![\<\>])(\<\>|=)(.*)", y)
  154. IfEqual y2,=, Return Eval_@(y1) = Eval_@(y3)
  155. IfEqual y2,<>, Return Eval_@(y1) <> Eval_@(y3)
  156. ; execute rightmost <,>,<=,>= operator
  157. RegExMatch(x, "(.*)(?<![\<\>])(\<=?|\>=?)(?![\<\>])(.*)", y)
  158. IfEqual y2,<, Return Eval_@(y1) < Eval_@(y3)
  159. IfEqual y2,>, Return Eval_@(y1) > Eval_@(y3)
  160. IfEqual y2,<=, Return Eval_@(y1) <= Eval_@(y3)
  161. IfEqual y2,>=, Return Eval_@(y1) >= Eval_@(y3)
  162. ; execute rightmost user operator (low precedence)
  163. RegExMatch(x, "i)(.*)Ť(.*?)ť(.*)", y)
  164. IfEqual y2,choose,Return Eval_Choose(Eval_@(y1),Eval_@(y3))
  165. IfEqual y2,Gcd, Return Eval_GCD( Eval_@(y1),Eval_@(y3))
  166. IfEqual y2,Min, Return (y1:=Eval_@(y1)) < (y3:=Eval_@(y3)) ? y1 : y3
  167. IfEqual y2,Max, Return (y1:=Eval_@(y1)) > (y3:=Eval_@(y3)) ? y1 : y3
  168. StringGetPos i, x, |, R ; execute rightmost | operator
  169. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) | Eval_@(SubStr(x,2+i))
  170. StringGetPos i, x, ^, R ; execute rightmost ^ operator
  171. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) ^ Eval_@(SubStr(x,2+i))
  172. StringGetPos i, x, &, R ; execute rightmost & operator
  173. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) & Eval_@(SubStr(x,2+i))
  174. ; execute rightmost <<, >> operator
  175. RegExMatch(x, "(.*)(\<\<|\>\>)(.*)", y)
  176. IfEqual y2,<<, Return Eval_@(y1) << Eval_@(y3)
  177. IfEqual y2,>>, Return Eval_@(y1) >> Eval_@(y3)
  178. ; execute rightmost +- (not unary) operator
  179. RegExMatch(x, "(.*[^!\~ąŹ\@\*/\\])(ą|Ź)(.*)", y) ; lower precedence ops already handled
  180. IfEqual y2,ą, Return Eval_@(y1) + Eval_@(y3)
  181. IfEqual y2,Ź, Return Eval_@(y1) - Eval_@(y3)
  182. ; execute rightmost */% operator
  183. RegExMatch(x, "(.*)(\*|/|\\)(.*)", y)
  184. IfEqual y2,*, Return Eval_@(y1) * Eval_@(y3)
  185. IfEqual y2,/, Return Eval_@(y1) / Eval_@(y3)
  186. IfEqual y2,\, Return Mod(Eval_@(y1),Eval_@(y3))
  187. ; execute rightmost power
  188. StringGetPos i, x, @, R
  189. IfGreaterOrEqual i,0, Return Eval_@(SubStr(x,1,i)) ** Eval_@(SubStr(x,2+i))
  190. ; execute rightmost function, unary operator
  191. If !RegExMatch(x,"(.*)(!|ą|Ź|~|'(.*)')(.*)", y)
  192. Return x ; no more function (y1 <> "" only at multiple unaries: --+-)
  193. IfEqual y2,!,Return Eval_@(y1 . !y4) ; unary !
  194. IfEqual y2,ą,Return Eval_@(y1 . y4) ; unary +
  195. IfEqual y2,Ź,Return Eval_@(y1 . -y4) ; unary - (they behave like functions)
  196. IfEqual y2,~,Return Eval_@(y1 . ~y4) ; unary ~
  197. If IsLabel(y3)
  198. GoTo %y3% ; built-in functions are executed last: y4 is number
  199. Return Eval_@(y1 . Eval_1(RegExReplace(f%y3%__F, f%y3%__X, y4))) ; user defined function
  200. Abs:
  201. Return Eval_@(y1 . Abs(y4))
  202. Ceil:
  203. Return Eval_@(y1 . Ceil(y4))
  204. Exp:
  205. Return Eval_@(y1 . Exp(y4))
  206. Floor:
  207. Return Eval_@(y1 . Floor(y4))
  208. Log:
  209. Return Eval_@(y1 . Log(y4))
  210. Ln:
  211. Return Eval_@(y1 . Ln(y4))
  212. Round:
  213. Return Eval_@(y1 . Round(y4))
  214. Sqrt:
  215. Return Eval_@(y1 . Sqrt(y4))
  216. Sin:
  217. Return Eval_@(y1 . Sin(y4))
  218. Cos:
  219. Return Eval_@(y1 . Cos(y4))
  220. Tan:
  221. Return Eval_@(y1 . Tan(y4))
  222. ASin:
  223. Return Eval_@(y1 . ASin(y4))
  224. ACos:
  225. Return Eval_@(y1 . ACos(y4))
  226. ATan:
  227. Return Eval_@(y1 . ATan(y4))
  228. Sgn:
  229. Return Eval_@(y1 . (y4>0)) ; Sign of x = (x>0)-(x<0)
  230. Fib:
  231. Return Eval_@(y1 . Eval_Fib(y4))
  232. Fac:
  233. Return Eval_@(y1 . Eval_Fac(y4))
  234. }
  235. Eval_ToBin(n) { ; Binary representation of n. 1st bit is SIGN: -8 -> 1000, -1 -> 1, 0 -> 0, 8 -> 01000
  236. Return n=0||n=-1 ? -n : Eval_ToBin(n>>1) . n&1
  237. }
  238. Eval_ToBinW(n,W=8) { ; LS W-bits of Binary representation of n
  239. Loop %W% ; Recursive (slower): Return W=1 ? n&1 : ToBinW(n>>1,W-1) . n&1
  240. b := n&1 . b, n >>= 1
  241. Return b
  242. }
  243. Eval_FromBin(bits) { ; Number converted from the binary "bits" string, 1st bit is SIGN
  244. n = 0
  245. Loop Parse, bits
  246. n += n + A_LoopField
  247. Return n - (SubStr(bits,1,1)<<StrLen(bits))
  248. }
  249. Eval_GCD(a,b) { ; Euclidean GCD
  250. Return b=0 ? Abs(a) : Eval_GCD(b, mod(a,b))
  251. }
  252. Eval_Choose(n,k) { ; Binomial coefficient
  253. p := 1, i := 0, k := k < n-k ? k : n-k
  254. Loop %k% ; Recursive (slower): Return k = 0 ? 1 : Choose(n-1,k-1)*n//k
  255. p *= (n-i)/(k-i), i+=1 ; FOR INTEGERS: p *= n-i, p //= ++i
  256. Return Round(p)
  257. }
  258. Eval_Fib(n) { ; n-th Fibonacci number (n < 0 OK, iterative to avoid globals)
  259. a := 0, b := 1
  260. Loop % abs(n)-1
  261. c := b, b += a, a := c
  262. Return n=0 ? 0 : n>0 || n&1 ? b : -b
  263. }
  264. Eval_fac(n) { ; n!
  265. Return n<2 ? 1 : n*Eval_fac(n-1)
  266. }