AutoHotkey Community

It is currently May 27th, 2012, 12:11 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: [v2] Script Converter
PostPosted: April 3rd, 2011, 2:38 am 
Offline
User avatar

Joined: November 2nd, 2008, 4:23 pm
Posts: 2906
Location: 127.0.0.1
Convert Your Scripts to v2

This is a script for converting AutoHotkey v1 scripts to AutoHotkey v2 scripts. There are quite a few changes mainly based around the transition from traditional to expression syntax. Although at the moment `= is allowed in place of the past = for assignment, it may not be supported at some point. For this reason the converter will always change = assignment to :=.
Also, I don't know where style is going from here, or a number of possible changes that may be made (ex. deref characters). This script and its output may change greatly overtime if there is demand to keep it up.
I went for a simple approach to the conversion. I'm sure there are better, or more exact ways to convert scripts. Please let me know if you have ideas on how to improve it.
This is not complete even for the changes made thus far, so if anyone needs features I will add them on request.
Here is the main code. It runs on AHKv2 Alpha and needs format.ahk which Lexikos put in the Lib folder of the zip. To use:

  1. Run the script
  2. Chose the script to convert
  3. The new script will be in the same folder with "New" after the file name
  4. Enjoy! Image

Code:
; =============================================================================
; Script Converter
;    for converting scripts from v1 AutoHotkey to v2
; Use:
;    Run the script
;    Chose the file you want to convert in the file select dialog
;    A msgbox will popup telling you the script finished converting
;   If you gave the file MyScript.ahk, the output file will be MyScriptNew.ahk
;   Thats it, feel free to add to it and post changes here: http://www.autohotkey.com/forum/viewtopic.php?t=70266
; Uses format.ahk
; =============================================================================

Remove `=
(
#AllowSameLineComments
#MaxMem
SoundGetWaveVolume
SoundSetWaveVolume
#NoEnv
#Delimiter
SetFormat
A_FormatInteger
A_FormatFloat
)

Convert `=
(
EnvAdd,InputVar,Value,TimeUnits | *EnvAdd
EnvDiv,InputVar,Value | {1} /= {2}
EnvMult,InputVar,Valut | {1} *= {2}
EnvSub,InputVar,Value,TimeUnits | {1} -= {2}[, {3}]
IfEqual,InputVar,value | If {1} = {2}
IfNotEqual,InputVar,value | If {1} != {2}
IfGreater,InputVar,value | If {1} > {2}
IfGreaterOrEqual,InputVar,value | If {1} >= {2}
IfLess,InputVar,value | If {1} < {2}
IfLessOrEqual,InputVar,value | If {1} <= {2}
StringLen,OutputVar,InputVar | {1} := StrLen({2})
StringGetPos,OutputVar,InputVar,SearchText,Side, Offset | {1} := InStr({2}, {3}[][, false, {5}])
StringMid,OutputVar,InputVar,StartChar,Count,L | {1} := SubStr({2}, {3}[, {4}][])
StringLeft,OutputVar,InputVar,Count | {1} := SubStr({2}, 1, {3})
StringRight,OutputVar,InputVar,Count | {1} := SubStr({2}, -{3}+1, {3})
StringTrimLeft,OutputVar,InputVar,Count | {1} := SubStr({2}, 1, -{3})
StringTrimRight,OutputVar,InputVar,Count | {1} := SubStr({2}, {3}+1)
WinGetActiveStats,TitleVar,WidthVar,HeightVar,XVar,YVar | *ActiveStats
WinGetActiveTitle,OutputVar | WinGetTitle, {1}, A
DriveSpaceFree,OutputVar,PathVar | DriveGet, {1}, SpaceFree, {2}
)

Directives `=
(Join`r`n
#Warn UseUnsetLocal
#Warn UseUnsetGlobal
)

SubStrFunction `=
(Join`r`n

; This function may be removed if StartingPos is always > 0.
CheckStartingPos(p) {
   return p - (p <= 0)
}

)

; =============================================================================
; Main Part of program
;   Many changes can be made without altering this
; =============================================================================
if !Args
{
   FileSelectFile, FN,, %A_MyDocuments%
   If !FN
      ExitApp
   FNOut := SubStr(FN, 1, StrLen(FN)-4) . "_new.ahk"
}
else If Args.MaxIndex() = 1 ; Allow a command line param for the file name ex. Run Convert.ahk "MyInputFile.ahk"
{
   FN := Trim(Args[1])
   FNOut := SubStr(FN, 1, StrLen(FN)-4) . "_new.ahk"
}
else if Mod(Args.MaxIndex(), 2) = 0 ; Parse arguments with linux like syntax, ex. Run Convert.ahk --input "Inputfile.ahk" -o "OutputFile.ahk"
{
   for i, P in Args
   {
      If Mod(i,2) ; Odd parameter, identifier
      {
         If (P = "-o") || (P = "--output")
            Mode := 1
         else If (P = "-i") || (P = "--input")
            Mode := 2
         else
            Mode := 0
      }
      else if Mode != 0
      {
         If Mode = 1
            FNOut := Trim(P)
         else if Mode = 2
            FN := Trim(P)
      }
   }
}


If !FN
{
   Msgbox, 48, Conversion Error, The commandline parameters passed are invalid.  Please make sure they are correct and try again.  Now exiting.
   ExitApp
}
If !FNOut
   FNOut := SubStr(FN, 1, StrLen(FN)-4) . "_new.ahk"


Output := FileOpen(FNOut, "w")
Loop, Read, %FN%
{
   Skip := false
   Line := A_LoopReadLine
   FirstChar := SubStr( Trim(Line), 1, 1 )
   FirstTwo := SubStr(LTrim(Line), 1, 2)
   CommandMatch := -1

   If (Trim(Line) == "") || ( FirstChar == ";" )
   {
      ; Do nothing, but we still want to add the line to the output file
   }
   
   else if FirstTwo == "/*"
      InCommentBlock := true
   
   else if FirstTwo == "*/"
      InCommentBlock := false
   
   else if InCommentBlock
   {
      ; Do nothing, but skip all the following
   }
   
   else If InStr(Line, "SendMode") && InStr(Line, "Input")
      Skip := true
   
   else if ( FirstChar == "(" )
   {
      If RegExMatch(Line, "i)join(.+?)(LTrim|RTrim|Comment|%|,|``)?", Join)
         JoinBy := Join1
      else
         JoinBy := "``n"
      Cont := 1
      If InStr(LastLine, ":= """"")
      {
         Output.Seek(-4, 1) ; Remove the newline characters and double quotes
      }
      else
      {
         Output.Seek(-2, 1)
         Output.Write(" % ")
      }
      continue ; Don't add to the output file
   }

   else if ( FirstChar == ")" )
   {
      Cont := 0
      continue
   }

   else if Cont
   {
      Line := ToExp(Line . JoinBy)
      If Cont > 1
         Line := ". " . Line
      Cont++
   }
   
   ; Replace = with := expression equivilents
   else If RegExMatch(Line, "i)^([\s]*[a-z_][a-z_0-9]*[\s]*)=([^;]*)", Equation) ; Thanks Lexikos
      Line := RTrim(Equation1) . " := " . ToExp(Equation2)
   
   else If RegExMatch(Line, "i)^(\s*if\s+[a-z_][a-z_0-9]*[\s]*)=([^{;]*)(\s*{?)", Equation)
      Line := format_v("{variable} = {equation}{otb}", {variable: RTrim(Equation1), equation: ToExp(Equation2), otb: Equation3} )
   
   else ; Command replacing
   ; To add commands to be checked for, modify the list at the top of this file
   {
      CommandMatch := 0
      Line := RegExReplace(Line, ",\s+", ",")
      FirstDelim := RegExMatch(Line, "[,\s]")
      Command := Trim( SubStr(Line, 1, FirstDelim-1) )
      Params := SubStr(Line, FirstDelim+1)
      ; Now we format the parameters into their v2 equivilents
      Loop, Parse, Convert, `n
      {
         StringSplit, Part, A_LoopField, |
         ListDelim := RegExMatch(Part1, "[,\s]")
         ListCommand := Trim( SubStr(Part1, 1, ListDelim-1) )
         If (ListCommand = Command)
         {
            CommandMatch := 1
            ListParams := RTrim( SubStr(Part1, ListDelim+1) )
            ListParam := Array()
            Param := Array() ; Parameters in expression form
            Loop, Parse, ListParams, `,
               ListParam[A_Index] := A_LoopField
            Loop, Parse, Params, `,
               If InStr(ListParam[A_Index], "var")
                  Param[A_Index] := A_LoopField
               else
                  Param[A_Index] := ToExp(A_LoopField)
            Part2 := Trim(Part2)
            If ( SubStr(Part2, 1, 1) == "*" )
            {
               FuncName := SubStr(Part2, 2)
               If IsFunc(FuncName)
                  Line := %FuncName%(Param)
            }
            else
            {
               If ParamDif := (ListParam.MaxIndex() - Param.MaxIndex() )
                  ; Remove all unused optional parameters
                  Part2 := RegExReplace(Part2, "\[[^\]]*\]", "", Count, ParamDif, 1)
               StringReplace, Part2, Part2, [,, All
               StringReplace, Part2, Part2, ],, All
               Line := format_v(Part2, Param)
            }
         }
      }
   }
   
   ; Remove lines we can't use
   If CommandMatch = 0 && !InCommentBlock
      Loop, Parse, Remove, `n, `r
         If InStr(A_LoopReadLine, A_LoopField)
            Skip := true
   
   ; TEMPORARY
   If !FoundSubStr && !InCommentBlock && InStr(Line, "SubStr")
   {
      FoundSubStr := true
      Line .= " `; WARNING: SubStr conversion may change in the near future"
   }
   
   ; Put the directives after the first non-comment line
   If !FoundNonComment && !InCommentBlock && A_Index != 1 && FirstChar != ";" && FirstTwo != "*/"
   {
      Output.Write(Directives . "`r`n")
      FoundNonComment := true
   }
   

   If Skip
      Line := format_v("; REMOVED: {line}", {line: Line})
   Output.Write(Line . "`r`n")
   LastLine := Line
}

; The following will be uncommented at a a later time
;If FoundSubStr
;   Output.Write(SubStrFunction)

Output.Close()
If !Args.MaxIndex()
   MsgBox, Done!
ExitApp

; =============================================================================
; Convert traditional statements to expressions
;    Don't pass whole commands, instead pass one parameter at a time
; =============================================================================
ToExp(Text)
{
   static qu := """" ; Constant for double quotes
   Text := Trim(Text, " `t")
   If Text = ""
      TOut := (qu . qu) ; Two double quotes
   else if InStr(Text, "%")
   {
      TOut := ""
      Loop % StrLen(Text)
      {
         Symbol := Chr(NumGet(Text, (A_Index-1)*2, "UChar"))
         If Symbol == "%"
         {
            If (DeRef := !DeRef) && (A_Index != 1)
               TOut .= qu . " . "
            else If (!DeRef) && (A_Index != StrLen(Text))
               TOut .= " . " . qu
         }
         else
         {
            If A_Index = 1
               TOut .= qu
            TOut .= Symbol
         }
      }
      If Symbol != "%"
         TOut .= (qu) ; One double quote
   }
   else if type(Text+0) != "String"
   {
      TOut := Text+0
   }
   else
   {
      StringReplace, TOut, Text, % qu, % qu . qu, All
      TOut := qu . TOut . qu
   }
   return (TOut)
}

; =============================================================================
; Formatting functions
;    They all accept an array of parameters and return command(s) in text form
;    These are only called in one place in the script and are called dynamicly
; =============================================================================
ActiveStats(p) {
   If p[1]
      Out .= format_v("WinGetTitle, {1}, A", p)
   Count := p.MaxIndex()
   loop 5
      p[A_Index] := ( p[A_Index] ? " " . p[A_Index] : "" )
   if Count > 1 ; Width and/or Height and/or X and/or Y but not Title
      Out .= (p[1] ? "`r`n" : "") . format_v("WinGetPos{2},{3},{4},{5}, A", p)
   return Out   
}

EnvAdd(p) {
   If p[3]
      return format_v("{1} := DateAdd({1}, {2}, {3})", p)
   else
      return format_v("{1} += {2}", p)
}

Click here to see an example conversion

_________________
aboutscriptappsscripts
Any code ⇈ above ⇈ requires AutoHotkey_L to run


Last edited by Frankie on April 7th, 2011, 8:13 pm, edited 7 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 3:05 am 
Offline

Joined: September 15th, 2006, 10:25 am
Posts: 567
you are fast! Within a couple of days we have a converter. Good work.
On mobile, so will have not looked at the code in detail, will later for suggestions (and maybe learn a bit ;))
happy weekend.

_________________
If i've seen further it is by standing on the shoulders of giants

my site | ~shajul | WYSIWYG BBCode Editor


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 9:46 am 
Offline
User avatar

Joined: April 4th, 2009, 8:19 pm
Posts: 1143
Location: Croatia
Haven't tested it yet, but I'm very happy to see you did a converter. Thanks! :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 12:38 pm 
Offline

Joined: January 10th, 2009, 2:57 pm
Posts: 19
Great work Frankie.

I can see that this is going to get a lot of use.

Since I've got about a thousand scripts, I can see the need for a bulk convert feature.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 1:50 pm 
Offline

Joined: October 13th, 2009, 10:09 pm
Posts: 1389
Very nice, please keep working on it. Especially those changes that won't produce an immediate error (like negative index offset in SubStr()) need to be changed automatically, because those won't be noticed immediately, but will produce bugs in the scripts.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 8:02 pm 
Offline

Joined: February 17th, 2008, 5:01 pm
Posts: 303
Many thanks for this, Frankie, I'm sure that this will prove invaluable as people make the transition.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 3rd, 2011, 9:05 pm 
Offline
User avatar

Joined: September 5th, 2009, 2:06 pm
Posts: 1718
Location: Somewhere near you
shajul wrote:
you are fast!

Yes he is! :D

_________________
Image
The quick onyx goblin jumps over the lazy dwarf


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2011, 2:30 am 
Offline
User avatar

Joined: November 2nd, 2008, 4:23 pm
Posts: 2906
Location: 127.0.0.1
Thanks for all the comments. Just some small changes today. I added support for WinGetActiveStats which splits into up-to two commands depending on what parameters are supplied.

[Edit: Ready for Change]I'll try to find time to work on SubStr which is used in a lot of cases, but can you give an example that would break a script?

[Edit: Fixed]The biggest problem I'm having with converting scripts is continuation sections. Theres no set way there going to go in the future and I can't figure out expression continuation sections for the life of me... Any help with that is more than appreciated.

[Edit: Done]I'll add the rest of the commands listed for removal here soon.

[Edit: Added]Batch conversion would be a good feature to have. If I find the time (might be a while) I'll make a function version of this script.

_________________
aboutscriptappsscripts
Any code ⇈ above ⇈ requires AutoHotkey_L to run


Last edited by Frankie on April 6th, 2011, 11:45 pm, edited 3 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2011, 10:03 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
Frankie wrote:
I'll try to find time to work on SubStr which is used in a lot of cases, but can you give an example that would break a script?
SubStr hasn't been changed yet, but it will probably be changed so that e.g. a StartingPos of -1 returns the last character. All that would need to be done is subtract 1 from StartingPos if it is <= 0. However, this would be impossible to detect in 100% of cases. Perhaps the converter should point out any potential (not necessarily confirmed) issues that it detects but can't resolve automatically.
Quote:
I can't figure out expression continuation sections for the life of me...
"Expression" continuation sections are the same as all continuation sections. They are processed at such an early stage that it doesn't matter whether you're using them with commands, expressions, hotstrings, or even directives. Maybe you meant continuation lines. In that case, you don't need to know whether it's really an expression:
Code:
MsgBox a
+ b

I haven't actually tried your script yet, but I see a few errors in the conversion example. Firstly, EnvAdd requires a timestamp in a particular format when operating in date mode - "March 15" is invalid. Secondly, "weeks" is not a supported time unit. Finally (and most importantly), the conversion output is incorrect: EnvAdd has been replaced with DateAdd() and += no longer supports date math.

As you may not know, EnvAdd and related treat an empty variable as 0. Since += was equivalent to EnvAdd when it stood alone on a line, it also had this behaviour in those cases. However, it did not in other cases, such as x := (y += 1), and never does in v2. The easiest way to (mostly) overcome this might be to add #Warn UseUnsetLocal and #Warn UseUnsetGlobal to converted scripts.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2011, 10:53 am 
Offline

Joined: October 2nd, 2009, 12:43 pm
Posts: 283
Code:
SubStr(String, StartingPos)

converted:

temp_var:=StartingPos
SubStr(String, (temp_var<=0)?(temp_var-1):(temp_var))


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2011, 5:21 pm 
Offline

Joined: October 13th, 2009, 10:09 pm
Posts: 1389
I think it would be nice if you could add a tag comment to the changed lines of code where appropriate. This would allow for manual inspection afterwards.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 5th, 2011, 12:25 am 
Offline
User avatar

Joined: November 2nd, 2008, 4:23 pm
Posts: 2906
Location: 127.0.0.1
I wrote:
I'll add the rest of the commands listed for removal here soon.
Chris / The Plan wrote:
Repeat / EndRepeat
RightClick / LeftClick
LeftClickDrag / RightClickDrag
HideAutoItWin
These apear to be non-existent. Correct me if I'm wrong. The remaining have been added.

Lexikos wrote:
"Expression" continuation sections are the same as all continuation sections...Maybe you meant continuation lines
Ah...I was making it more complicated than it had to be. Thanks for pointing that out. Continuation sectons will now be replaced by multi-line expressions. This is true in the case of assignment, as well as commands that use them. The use in commands is mainly due to ease of implementation. I don't know if traditional continuation sections would be preferred in commands or not.

Quote:
but I see a few errors in the conversion example... "March 15" is invalid. Secondly, "weeks" is not a supported time unit
I don't aim to correct incorrect programs; which is what the test program was. I admittedly haven't used dates much with AutoHotkey.

Quote:
Finally (and most importantly), the conversion output is incorrect: EnvAdd has been replaced with DateAdd() and += no longer supports date math.
Fixed. There's no DateAdd docs, so I guessed at it and it seems to work. Let me know if it's wrong.

Quote:
SubStr hasn't been changed yet, but it will probably be changed
Until it changes, I'll just leave a comment in the output script. And thanks flak, I would like to avoid filling the output script with ternary comparisons. Making the scripts correct is more important though...

Quote:
The easiest way to (mostly) overcome this might be to add #Warn UseUnsetLocal and #Warn UseUnsetGlobal to converted scripts.
Added, I'll consider adding a comment to them that they can be removed once the script is tested and working.

fragman wrote:
I think it would be nice if you could add a tag comment to the changed lines of code where appropriate. This would allow for manual inspection afterwards.
Anything in specific? I comment the removals and now SubStr on the first occurrence (For warnings I only want them shown on one line to not annoy).

To All: For a good sense of how this is progressing click the link at the bottom of the first post. I'll also like it here: Click here to see an example conversion

_________________
aboutscriptappsscripts
Any code ⇈ above ⇈ requires AutoHotkey_L to run


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 5th, 2011, 6:53 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
flak wrote:
temp_var:=StartingPos
SubStr(String, (temp_var<=0)?(temp_var-1):(temp_var))
It can be shorter and probably more efficient if you rely on the fact that <= returns boolean 0 or 1.
Code:
SubStr(String, (_tmp := StartingPos) - (_tmp <= 0))
; Note: temp var is only strictly necessary if StartingPos is an expression with side-effects.


Frankie wrote:
These apear to be non-existent.
They are obsolete AutoIt2 commands which were omitted from the help file and have been removed in v2.
Quote:
There's no DateAdd docs, so I guessed at it and it seems to work.
As I mentioned in the v2 thread, it's the same as EnvAdd except that the first parameter is input-only and all parameters (including TimeUnits) are required.
Quote:
I would like to avoid filling the output script with ternary comparisons.
An alternative might be to do something like the below, advise the author of the change to SubStr and let them review the code to determine if any change to their code is necessary:
Code:
Result := SubStr(String, CheckStartingPos(StartingPos))
...
; This function may be removed if StartingPos is always > 0.
CheckStartingPos(p) {
    return p - (p <= 0)
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 5th, 2011, 9:20 am 
Offline

Joined: October 13th, 2009, 10:09 pm
Posts: 1389
I like the proposed change with the function. This would allow to look up the occurrences and replace them easily with a regex where necessary.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 6th, 2011, 11:58 pm 
Offline
User avatar

Joined: November 2nd, 2008, 4:23 pm
Posts: 2906
Location: 127.0.0.1
DriveSpaceFree is now replaced with DriveGet, which I missed before.

I added #Warn directives to the top of output scripts. I might change this later.

[Edit: The command line syntax has changed, see next page]
As it was requested, I added the possibility for batch conversion. The way to do it is, write a AutoHotkey script that Runs Convert.ahk (as I call it) in a loop. Here is an example that loops through the folder the script is run from. Simply copy and paste the script from folder to folder and run it to convert all files.
Code:
Loop, *.ahk
   If A_LoopFileName != "Convert.ahk" && A_LoopFileName != A_ScriptName
      RunWait, Convert.ahk "%A_LoopFileName%"


Another note, files are now named Script_New.ahk instead of ScriptNew.ahk. The underscore is new.

For SubStr, I set up the code to add the function. It's currently not active, as in it's commented out. When SubStr changes, I'll change the converter as well.

For now I'm just going to try this on various scripts and see what doesn't work. I think it's mostly complete for the changes in the alpha but let me know if I missed anything.

_________________
aboutscriptappsscripts
Any code ⇈ above ⇈ requires AutoHotkey_L to run


Last edited by Frankie on April 7th, 2011, 8:17 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: fincs, Yahoo [Bot] and 11 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group