How to use string in calculator script

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
jazzvb
Posts: 32
Joined: 14 Jan 2019, 20:10

How to use string in calculator script

Post by jazzvb » 02 Oct 2022, 07:59

Hello, on the old forum Laszlo posted an amazing script for making calculations anywhere. As I am noob 2.0 I cannot figure out how the convertions work. 8*3 I can work out, but after trying several combinations I don't know what to do when I want to convert 5 inches to cm. Nor the other way around.

What I've tried:
inch*5 (equals 0)
xinch*5 (equals 0)
5inch (equals 5)
5xinch (equals 5)
5*xinch (equals 0)
:=5*xinch (equals 0)

From cm to inches I don't even know where to start...

Kind regard,
jazz

Script:
https://www.autohotkey.com/board/topic/15675-monster-evaluate-math-expressions-in-strings/

User avatar
boiler
Posts: 16927
Joined: 21 Dec 2014, 02:44

Re: How to use string in calculator script

Post by boiler » 02 Oct 2022, 08:09

Code: Select all

MsgBox, % Eval(xinch " * 5")

It seems like he should have had it parse the variable names he set up from the string also, but apparently he didn't, so you have to use the variables concatenated to your string. Which make it really not that useful for this purpose because this does the same thing with a lot less overhead:

Code: Select all

xinch := 2.54
MsgBox, % xinch * 5

jazzvb
Posts: 32
Joined: 14 Jan 2019, 20:10

Re: How to use string in calculator script

Post by jazzvb » 02 Oct 2022, 11:13

Hey boiler, even if I copy and paste that concatenation (learnt a new word here), the result is 0. I agree, if typing that string is what you have to do to get the answer, it is too much of a hassle. Especially for AHK use. Pity, but the simple mathmatics (+ - / *) do work and are very handy.

jazzvb
Posts: 32
Joined: 14 Jan 2019, 20:10

Re: How to use string in calculator script

Post by jazzvb » 02 Oct 2022, 12:01

I searched some more and came across this script. It converts inches to cm and vice versa (change cm <--> inches and 2.54 to 0.393700787) by opening a message box. not bad actually...

Code: Select all

::i2c:: ; or ::c2i::
fileappend, %A_UserName% - %A_DD%%A_MM%%A_YYYY% - sqfcalc`n, p:/autohotkey/texts/log.txt
InputBox, UserInput, Inch, please enter inch., , 100, 280
UserInput *= 2.54
if ErrorLevel
MsgBox, CANCEL was pressed.
else
sleep 500
UserInput := Round(UserInput, 1)
StringReplace, UserInput, UserInput, ., `,
send {bs 1} %UserInput% cm
return

User avatar
boiler
Posts: 16927
Joined: 21 Dec 2014, 02:44

Re: How to use string in calculator script

Post by boiler » 02 Oct 2022, 15:12

jazzvb wrote: Hey boiler, even if I copy and paste that concatenation (learnt a new word here), the result is 0.
You must have put that line before the line where xinch is assigned its value. Put it in the right place and you’ll get 12.7.

User avatar
flyingDman
Posts: 2817
Joined: 29 Sep 2013, 19:01

Re: How to use string in calculator script

Post by flyingDman » 02 Oct 2022, 16:07

Monster is a monster but...!
If you move this part:

Code: Select all

global
xe := 2.718281828459045, xpi := 3.141592653589793      ; referenced as "e", "pi"
xinch := 2.54, xfoot := 30.48, xmile := 1.609344       ; [cm], [cm], [Km]
xounce := 0.02841, xpint := 0.5682, xgallon := 4.54609 ; liters
xoz := 28.35, xlb := 453.59237                         ; gramms
Note the global ! (or define locals first then the constants)
inside the eval() function
you can do this:

Code: Select all

MsgBox % Eval("inch*5")                                   ;12.7
MsgBox % Eval("pi*1")                                  	  ;3.14159
MsgBox % Eval("ounce*1")                                  ;0.02841
14.3 & 1.3.7

jazzvb
Posts: 32
Joined: 14 Jan 2019, 20:10

Re: How to use string in calculator script

Post by jazzvb » 04 Oct 2022, 17:25

I'm doing something wrong, because the answer is still 0. What I do is, I literally type MsgBox, % Eval(xinch " * 5") somewhere and after that I use the hotkey to get the answer.

I also don't know what I do with the global lines either.

Thank you for your time and reactions! I better stick to copy and paste scripts, I'm afraid.

User avatar
flyingDman
Posts: 2817
Joined: 29 Sep 2013, 19:01

Re: How to use string in calculator script

Post by flyingDman » 04 Oct 2022, 18:30

I assume that you have saved the Monster Script in a Lib. It makes sense to put it there. You have not explained exactly what your intended use is. Eval() is used to take a string and convert it to an expression and evaluate or "resolve" it, rather than use (variables in) expressions to resolve mathematical operations (which typically only requires built-in functions and no external functions; see: https://www.autohotkey.com/docs/commands/Math.htm). If your intent is only to convert inches to cm or cm to inches (or similar conversions), this might not be the tool for you. The following might demonstrate it:

Code: Select all

gui, font, s14
gui, add, text, 	x10 		w50					, string:
gui, add, edit, 	x+5 		w300 gcalc vMyEdit	,
gui, add, text, 	x10 	y+5	w50					, result:
gui, add, edit, 	x+5 		w300 vMyRes			,
gui, show
return

calc:
gui, submit, nohide
guicontrol,, MyRes, % eval(MyEdit)
return
;-----------------monster ----------------------
Eval(x) {                                                   ; non-recursive PRE/POST PROCESSING: I/O forms, numbers, ops, ";"
   Local FORM, FormF, FormI, i, W, y, y1, y2, y3, y4
   xe := 2.718281828459045, xpi := 3.141592653589793        ; referenced as "e", "pi"
   xinch := 2.54, xfoot := 30.48, xmile := 1.609344         ; [cm], [cm], [Km]
   xounce := 0.02841, xpint := 0.5682, xgallon := 4.54609   ; liters
   xoz := 28.35, xlb := 453.59237                           ; gramms
   FormI := A_FormatInteger, FormF := A_FormatFloat

   SetFormat Integer, D                ; decimal intermediate results!
   RegExMatch(x, "\$(b|h|x|)(\d*[eEgG]?)", y)
   FORM := y1, W := y2                 ; HeX, Bin, .{digits} output format
   SetFormat FLOAT, 0.16e              ; Full intermediate float precision
   StringReplace x, x, %y%             ; remove $..
   Loop
      If RegExMatch(x, "i)(.*)(0x[a-f\d]*)(.*)", y)
         x := y1 . y2+0 . y3           ; convert hex numbers to decimal
      Else Break
   Loop
      If RegExMatch(x, "(.*)'([01]*)(.*)", y)
         x := y1 . FromBin(y2) . y3    ; convert binary numbers to decimal: sign = first bit
      Else Break
   x := RegExReplace(x,"(^|[^.\d])(\d+)(e|E)","$1$2.$3") ; add missing '.' before E (1e3 -> 1.e3)
                                       ; literal scientific numbers between ‘ and ’ chars
   x := RegExReplace(x,"(\d*\.\d*|\d)([eE][+-]?\d+)","‘$1$2’")

   StringReplace x, x,`%, \, All       ; %  -> \ (= MOD)
   StringReplace x, x, **,@, All       ; ** -> @ for easier process
   StringReplace x, x, +, ±, All       ; ± is addition
   x := RegExReplace(x,"(‘[^’]*)±","$1+") ; ...not inside literal numbers
   StringReplace x, x, -, ¬, All       ; ¬ is subtraction
   x := RegExReplace(x,"(‘[^’]*)¬","$1-") ; ...not inside literal numbers

   Loop Parse, x, `;
      y := Eval1(A_LoopField)          ; work on pre-processed sub expressions
                                       ; return result of last sub-expression (numeric)
   If FORM = b                         ; convert output to binary
      y := W ? ToBinW(Round(y),W) : ToBin(Round(y))
   Else If (FORM="h" or FORM="x") {
      SetFormat Integer, Hex           ; convert output to hex
      y := Round(y) + 0
   }
   Else {
      W := W="" ? "0.6g" : "0." . W    ; Set output form, Default = 6 decimal places
      SetFormat FLOAT, %W%
      y += 0.0
   }
   SetFormat Integer, %FormI%          ; restore original formats
   SetFormat FLOAT,   %FormF%
   Return y
}

Eval1(x) {                             ; recursive PREPROCESSING of :=, vars, (..) [decimal, no ";"]
   Local i, y, y1, y2, y3
                                       ; save function definition: f(x) := expr
   If RegExMatch(x, "(\S*?)\((.*?)\)\s*:=\s*(.*)", y) {
      f%y1%__X := y2, f%y1%__F := y3
      Return
   }
                                       ; execute leftmost ":=" operator of a := b := ...
   If RegExMatch(x, "(\S*?)\s*:=\s*(.*)", y) {
      y := "x" . y1                    ; user vars internally start with x to avoid name conflicts
      Return %y% := Eval1(y2)
   }
                                       ; here: no variable to the left of last ":="
   x := RegExReplace(x,"([\)’.\w]\s+|[\)’])([a-z_A-Z]+)","$1«$2»")  ; op -> «op»

   x := RegExReplace(x,"\s+")          ; remove spaces, tabs, newlines

   x := RegExReplace(x,"([a-z_A-Z]\w*)\(","'$1'(") ; func( -> 'func'( to avoid atan|tan conflicts

   x := RegExReplace(x,"([a-z_A-Z]\w*)([^\w'»’]|$)","%x$1%$2") ; VAR -> %xVAR%
   x := RegExReplace(x,"(‘[^’]*)%x[eE]%","$1e") ; in numbers %xe% -> e
   x := RegExReplace(x,"‘|’")          ; no more need for number markers
   Transform x, Deref, %x%             ; dereference all right-hand-side %var%-s

   Loop {                              ; find last innermost (..)
      If RegExMatch(x, "(.*)\(([^\(\)]*)\)(.*)", y)
         x := y1 . Eval@(y2) . y3      ; replace (x) with value of x
      Else Break
   }
   Return Eval@(x)
}

Eval@(x) {                             ; EVALUATE PRE-PROCESSED EXPRESSIONS [decimal, NO space, vars, (..), ";", ":="]
   Local i, y, y1, y2, y3, y4

   If x is number                      ; no more operators left
      Return x
                                       ; execute rightmost ?,: operator
   RegExMatch(x, "(.*)(\?|:)(.*)", y)
   IfEqual y2,?,  Return Eval@(y1) ? Eval@(y3) : ""
   IfEqual y2,:,  Return ((y := Eval@(y1)) = "" ? Eval@(y3) : y)

   StringGetPos i, x, ||, R            ; execute rightmost || operator
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) || Eval@(SubStr(x,3+i))
   StringGetPos i, x, &&, R            ; execute rightmost && operator
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) && Eval@(SubStr(x,3+i))
                                       ; execute rightmost =, <> operator
   RegExMatch(x, "(.*)(?<![\<\>])(\<\>|=)(.*)", y)
   IfEqual y2,=,  Return Eval@(y1) =  Eval@(y3)
   IfEqual y2,<>, Return Eval@(y1) <> Eval@(y3)
                                       ; execute rightmost <,>,<=,>= operator
   RegExMatch(x, "(.*)(?<![\<\>])(\<=?|\>=?)(?![\<\>])(.*)", y)
   IfEqual y2,<,  Return Eval@(y1) <  Eval@(y3)
   IfEqual y2,>,  Return Eval@(y1) >  Eval@(y3)
   IfEqual y2,<=, Return Eval@(y1) <= Eval@(y3)
   IfEqual y2,>=, Return Eval@(y1) >= Eval@(y3)
                                       ; execute rightmost user operator (low precedence)
   RegExMatch(x, "i)(.*)«(.*?)»(.*)", y)
   If IsFunc(y2)
      Return %y2%(Eval@(y1),Eval@(y3)) ; predefined relational ops

   StringGetPos i, x, |, R             ; execute rightmost | operator
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) | Eval@(SubStr(x,2+i))
   StringGetPos i, x, ^, R             ; execute rightmost ^ operator
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) ^ Eval@(SubStr(x,2+i))
   StringGetPos i, x, &, R             ; execute rightmost & operator
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) & Eval@(SubStr(x,2+i))
                                       ; execute rightmost <<, >> operator
   RegExMatch(x, "(.*)(\<\<|\>\>)(.*)", y)
   IfEqual y2,<<, Return Eval@(y1) << Eval@(y3)
   IfEqual y2,>>, Return Eval@(y1) >> Eval@(y3)
                                       ; execute rightmost +- (not unary) operator
   RegExMatch(x, "(.*[^!\~±¬\@\*/\\])(±|¬)(.*)", y) ; lower precedence ops already handled
   IfEqual y2,±,  Return Eval@(y1) + Eval@(y3)
   IfEqual y2,¬,  Return Eval@(y1) - Eval@(y3)
                                       ; execute rightmost */% operator
   RegExMatch(x, "(.*)(\*|/|\\)(.*)", y)
   IfEqual y2,*,  Return Eval@(y1) * Eval@(y3)
   IfEqual y2,/,  Return Eval@(y1) / Eval@(y3)
   IfEqual y2,\,  Return Mod(Eval@(y1),Eval@(y3))
                                       ; execute rightmost power
   StringGetPos i, x, @, R
   IfGreaterOrEqual i,0, Return Eval@(SubStr(x,1,i)) ** Eval@(SubStr(x,2+i))
                                       ; execute rightmost function, unary operator
   If !RegExMatch(x,"(.*)(!|±|¬|~|'(.*)')(.*)", y)
      Return x                         ; no more function (y1 <> "" only at multiple unaries: --+-)
   IfEqual y2,!,Return Eval@(y1 . !y4) ; unary !
   IfEqual y2,±,Return Eval@(y1 .  y4) ; unary +
   IfEqual y2,¬,Return Eval@(y1 . -y4) ; unary - (they behave like functions)
   IfEqual y2,~,Return Eval@(y1 . ~y4) ; unary ~
   If IsFunc(y3)
      Return Eval@(y1 . %y3%(y4))      ; built-in and predefined functions(y4)
   Return Eval@(y1 . Eval1(RegExReplace(f%y3%__F, f%y3%__X, y4))) ; LAST: user defined functions
}

ToBin(n) {      ; Binary representation of n. 1st bit is SIGN: -8 -> 1000, -1 -> 1, 0 -> 0, 8 -> 01000
   Return n=0||n=-1 ? -n : ToBin(n>>1) . n&1
}
ToBinW(n,W=8) { ; LS W-bits of Binary representation of n
   Loop %W%     ; Recursive (slower): Return W=1 ? n&1 : ToBinW(n>>1,W-1) . n&1
      b := n&1 . b, n >>= 1
   Return b
}
FromBin(bits) { ; Number converted from the binary "bits" string, 1st bit is SIGN
   n = 0
   Loop Parse, bits
      n += n + A_LoopField
   Return n - (SubStr(bits,1,1)<<StrLen(bits))
}

Sgn(x) {
   Return (x>0)-(x<0)
}

MIN(a,b) {
   Return a<b ? a : b
}
MAX(a,b) {
   Return a<b ? b : a
}
GCD(a,b) {      ; Euclidean GCD
   Return b=0 ? Abs(a) : GCD(b, mod(a,b))
}
Choose(n,k) {   ; Binomial coefficient
   p := 1, i := 0, k := k < n-k ? k : n-k
   Loop %k%                   ; Recursive (slower): Return k = 0 ? 1 : Choose(n-1,k-1)*n//k
      p *= (n-i)/(k-i), i+=1  ; FOR INTEGERS: p *= n-i, p //= ++i
   Return Round(p)
}

Fib(n) {        ; n-th Fibonacci number (n < 0 OK, iterative to avoid globals)
   a := 0, b := 1
   Loop % abs(n)-1
      c := b, b += a, a := c
   Return n=0 ? 0 : n>0 || n&1 ? b : -b
}
fac(n) {        ; n!
   Return n<2 ? 1 : n*fac(n-1)
}
Compared to a simple "conversion" tool:

Code: Select all

inch := 2.54

gui, font, s14
gui, add, edit, w300 vMyEdit,
gui, add, edit, w300 vMyRes,
gui, add, button,gItoC,Inch to CM
gui, add, button,x+5 gCtoI,CM to Inch
gui, show
return

ItoC:
gui, submit, nohide

guicontrol,, MyRes, % MyEdit * inch
return

CtoI:
gui, submit, nohide
guicontrol,, MyRes, % MyEdit / inch
return
14.3 & 1.3.7

Post Reply

Return to “Ask for Help (v1)”