AHK v1 to AHK v2 converter

Discuss the future of the AutoHotkey language
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

AHK v1 to AHK v2 converter

06 Sep 2017, 20:11

See here for first release.
AHK v1 to AHK v2 converter (initial work) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 08#p201908

==================================================

This will become a script that converts AHK v1.1 code to AHK v2 code.

For the time being, it will be a script that converts AHK v1.1 code to AHK v1.1 code that is more forwards compatible, i.e. more AHK v2-ready.

I will eventually make it able to convert from AHK v1.1 to AHK v2, however I need AHK v2 to be more finalised before it's worth finishing/publishing stage 2.

Note: Currently this handles:
- var = string -> var := "string"
- var = num -> var := num
It can also identify where continuation sections begin/end.
I will add in further 'var = ' to 'var := ' support, and also various conversions such as StringReplace to StrReplace.

I would suggest that you compare any before/after code with a utility such as WinMerge to confirm that any changes were the right ones, and I would recommend backing up your original code.

I may also add in some code, which can be turned on/off to:
- confirm each line matches certain standards (including: ensure commands have/don't have commas between the command name and the first parameter)
- convert some parameters to force expression mode, e.g. 'Cmd, %var%' to 'Cmd, % var'
- clean up NumPut e.g. ensure 4 parameters
- clean up DllCall lines (this could be quite challenging)
- indent code (I finally got some working code for this yesterday, that correctly converted an entire script)

Some things may be trickier, and so I may provide no or limited support for them, e.g. converting StringSplit to StrSplit, or handling 'return' lines with multiple parameters, or handling the removal of SetFormat. [I will return with a list of the more difficult conversions that would need to be done manually.]

I may also provide support to convert code so that it can use my 'AHK v2 functions for AHK v1' library.

I may also provide support for converting existing functions to variant functions, e.g. SubStr lines to 'JEE_SubStr' lines, providing an AHK v2-style 'SubStr' in AHK v1. Perhaps I will call that particular function one of: 'AHKv2_SubStr' / 'Ahk2_SubStr' / 'SubStr2' / 'JEE_SubStr2', I'm not sure, although I tend to prefer that any nonstandard function have a prefix of some sort to avoid confusion with standard functions.

However, some of these things may present difficulties, so there may be limited modes that convert some matching lines (safer conversions), but that instead notify you of potential other matching lines (riskier conversions) (perhaps by appending comments to those lines).

Btw I've already done all the serious work for this, it's more getting it ready for public viewing that is the issue. Also, of course I need AHK v2 to be more finalised, I've already had to do a lot of 'unconverting' and major rewrites to conversion scripts as the AHK v2 specs have changed, so I've decided to put AHK v1 -> AHK v2 conversion on hold, and focus on AHK v1 command style to AHK v1 expression style, and related script checking and script tidying.

Code: Select all

;AHK v1 command style to AHK v1 expression style
;make AHK v1 more ready for conversion

;q::
;vText := JEE_GetSelectedText()
;ControlGet, vText, Selected,, Edit1, A
vPath := A_ScriptDir "\AutoHotkey.ahk"
FileRead, vText, % vPath
vTextOrig := vText
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
vText := StrReplace(vText, "`r`n", "`n")
oArrayOrig := StrSplit(vText, "`n")
oArrayNew := StrSplit(vText, "`n")
vIsConSec := 0
vLastCodeLine := 0
vConSecPfx := "[CONSEC] "
vConSecSfx := " `;[CONSEC]"
vConSecPfx := ""
vConSecSfx := ""

Loop, % oArrayNew.Length()
{
	vTemp := oArrayNew[A_Index]
	if (SubStr(vTemp, 1, 1) = ";")
		vCode := "", vComments := vTemp
	else if (vPos := InStr(vTemp, " `;"))
		vCode := SubStr(vTemp, 1, vPos-1), vComments := SubStr(vTemp, vPos)
	else
		vCode := vTemp, vComments := ""
	vCode2 := LTrim(vCode)
	vWhitespace := SubStr(vCode, 1, StrLen(vCode)-StrLen(vCode2))
	;==============================
	;HANDLE CONTINUATION SECTIONS
	if (SubStr(vCode2, 1, 1) = "(") && !InStr(vCode2, ")")
	{
		vIsConSec := 1
		if !vLastCodeLine || !(vLastCodeLine < A_Index)
		{
			MsgBox, % "error: " vLastCodeLine
			return
		}
		Loop, % A_Index - vLastCodeLine
			oArrayNew[vLastCodeLine-1+A_Index] := vConSecPfx oArrayOrig[vLastCodeLine-1+A_Index] vConSecSfx
	}
	if vIsConSec && !(vConSecPfx vConSecSfx = "")
		oArrayNew[A_Index] := vConSecPfx vTemp vConSecSfx
	if (SubStr(vCode2, 1, 1) = ")")
		vIsConSec := 0
	if vIsConSec
		continue
	;==============================
	vCode2Orig := vCode2
	;var = string -> var := "string"
	if RegExMatch(vCode2, "^[A-Za-z]+ = [A-Za-z]+$")
		vCode2 := RegExReplace(vCode2, "^([A-Za-z]+) = ([A-Za-z]+)$", "$1 := " Chr(34) "$2" Chr(34))
	;var = num -> var := num
	else if RegExMatch(vCode2, "^[A-Za-z]+ = \d+$")
		vCode2 := RegExReplace(vCode2, "^([A-Za-z]+) = (\d+)$", "$1 := $2")

	if !(vCode2 = vCode2Orig)
		oArrayNew[A_Index] := vWhitespace vCode2 vComments
	if !(vCode2 = "")
		vLastCodeLine := A_Index
}

Loop, % oArrayNew.Length()
	vOutput .= (A_Index=1?"":"`r`n") oArrayNew[A_Index]
;Clipboard := vOutput
;JEE_WinMergeCompareStrings("BEFORE`r`n" vTextOrig, "AFTER`r`n" vOutput)
return
Last edited by jeeswg on 21 Feb 2018, 18:49, edited 4 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: AHK v1 to AHK v2 converter (initial work)

07 Sep 2017, 02:07

Thanks for your effors jeeswg, I will test this. :thumbup:
@ guest3456, hello :wave: , is your converter up to date, I remember it was quite good, but there has been many changes since that thread was active.

Cheers.
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: AHK v1 to AHK v2 converter (initial work)

07 Sep 2017, 08:45

Helgef wrote: @ guest3456, hello :wave: , is your converter up to date, I remember it was quite good, but there has been many changes since that thread was active.
not up to date unfortunately, but the framework should be solid to build upon and update, pull requests are welcome :)

User avatar
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
Location: Middle of the round cube
Contact:

Re: AHK v1 to AHK v2 converter (initial work)

25 Sep 2017, 13:36

jeeswg wrote:

Code: Select all


;JEE_WinMergeCompareStrings("BEFORE`r`n" vTextOrig, "AFTER`r`n" vOutput)

Mind sharing this function?

I ran the script on one of my scripts and it changed nothing. Any idea why? What exactly is it supposed to change?

guest3456 has GUI set up nice already. And his script saves the new code as A_ScriptName "_v2new.ahk"
try it and see
...
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: AHK v1 to AHK v2 converter (initial work)

25 Sep 2017, 15:06

The script is currently very limited, see the first post, it just handles some 'var = value' lines. Hence 'initial work' in the thread title.

If rapid progress is made with the development of AHK v2, this will become a priority, otherwise, I will post some updates at some point.

Recently I've been working on some scripts that tidy instances of DllCall, and that identify where functions and their parameters start and end. I've also been investigating continuation sections as part of an automated indentation function. Plus I wrote the basis of a WinMerge alternative.

While I've been having a lot of success with parsing, I've recently discovered the problem of handling all notation to do with objects.

indent code (automated code indentation) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=37270

It's quite easy to parse instances of 'one line one function' / 'one line one command', and these are the bulk of conversion, I'll share some code fairly soon, but functions within functions, back-to-back commands, and multi-line functions/commands, that may include indentation, comments and the use of continuation sections are harder.

Code: Select all

JEE_WinMergeCompareStrings(vText1, vText2, vOpt:="", vPathExe:="", vDirOut:="")
{
	global vPathWinMerge
	static vGetExePathWinMerge := Func("JEE_GetExePathWinMerge")
	;local oFile,vPathTemp1,vPathTemp2
	if (vText1 == vText2)
	{
		MsgBox, % "same (case sensitive)"
		return
	}
	;if (vText1 = vText2)
	;	MsgBox, % "same (case insensitive)"
	if !FileExist(vPathExe)
	&& !FileExist(vPathExe := vPathWinMerge)
	&& (!vGetExePathWinMerge || !FileExist(vPathExe := %vGetExePathWinMerge%()))
	{
		MsgBox, % "error: WinMerge not found"
		return
	}
	if (vDirOut = "")
		vDirOut := A_ScriptDir "\Temp"
	if !FileExist(vDirOut)
		FileCreateDir, % vDirOut
	Loop, 2
	{
		vPathTemp%A_Index% := vDirOut "\z wc" A_Index ".txt"
		oFile := FileOpen(vDirOut "\z wc" A_Index ".txt", "w")
		oFile.Length := 0 ;empty file
		oFile.Encoding := "UTF-8"
		oFile.Write(Chr(0xFEFF) vText%A_Index%)
		oFile.Close()
	}
	;/s single instance
	;/wl opens the left side as read-only
	;/wr opens the right side as read-only
	if InStr(vOpt, "ro")
		Run, "%vPathExe%" /s /wl /wr "%vPathTemp1%" "%vPathTemp2%"
	else
		Run, "%vPathExe%" /s "%vPathTemp1%" "%vPathTemp2%"
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: AHK v1 to AHK v2 converter (initial work)

21 Feb 2018, 18:37

- I have a first release if anybody wants to play around with it.
- The script is written for AHK v1, a main script, and a library that comes with it. It also needs the 2 libraries from here, the main functions library and expansion pack:
commands as functions (AHK v2 functions for AHK v1) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 55#p197355
[EDIT:] Changed the hotkeys from ^q and ^w to ^q (the same as before) and ^#w.

Code: Select all

;==================================================

;an AHK v1 script
;AHK v1 to v2 converter by jeeswg
;[first released: 2018-02-21]

;from:
;AHK v1 to AHK v2 converter (initial work) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?t=36754
;ahk v1 converter.ahk [this script]
;ahk v1 converter lib.ahk [additional library]

;from:
;commands as functions (AHK v2 functions for AHK v1) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=29689
;JEEAhk1FC.ahk [commands as functions]
;JEEAhk1FC+.ahk [auxiliary functions expansion pack]

;==================================================

;MAIN SECTION 1 - INTRODUCTION
;MAIN SECTION 2 - OPTIONS
;MAIN SECTION 3 - INITIALISE
;MAIN SECTION 4 - HOTKEY 1
;MAIN SECTION 5 - HOTKEY 2

;==================================================

;MAIN SECTION 1 - INTRODUCTION

;==================================================

;NOTES: HOW IT WORKS

;- Ctrl+Q to convert the selected text
;(and output changes to a log file, if that option is set to on)
;- Ctrl+W to check the selected text for nonstandard lines
;(i.e. lines that probably require conversion)

;- there are 3 command line parameters (paths) that can be used:
;PathIn / PathOut / PathOutLog
;here's an example script:

/*
q:: ;convert script
vPath := A_ScriptDir "\ahk v1 converter.ahk"
vPathIn := A_ScriptFullPath
SplitPath, vPathIn, vName, vDir, vExt, vNameNoExt, vDrive
vNow := A_Now
vPathOut := vDir "\" vNameNoExt "_v2 " vNow ".ahk"
vPathOutLog := vDir "\" vNameNoExt " CvtLog " vNow ".txt"
RunWait, "%A_AhkPath%" "%vPath%" "%vPathIn%" "%vPathOut%" "%vPathOutLog%"
Run, notepad.exe "%vPathOut%"
Run, notepad.exe "%vPathOutLog%"
return
*/

;- there are various options that can be set in the
;'MAIN SECTION 2 - OPTIONS' section
;- also, there is an #Include line, you can make various
;changes to the settings, by creating a file with AutoHotkey code
;with that particular filename

;==================================================

;NOTES: TYPES OF LINE TO CONVERT

;assignment
;var = value

;control flow statements
;- if + operators: between/contains/in/is type
;- if + operators: > < >= <= = <> !=
;- Loop
;- (others)

;commands

;directives
;- #IfWinXXX (#IfWin(Not)Active/#IfWin(Not)Exist)
;- (others)

;==================================================

;NOTES: MANUAL CONVERSION

;note: need to be converted manually:
;based on:
;AHK v1 to AHK v2 conversion tips/changes summary - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=36787
;functions: InStr/SubStr/RegExMatch/RegExReplace
;SetFormat
;Return: the last parameter is returned (rather than the first parameter)
;ternary operator: must specify the alternative result, see below for alternatives
;SendMessage: the reply is retrieved as the return value of the function, not as the content of ErrorLevel
;MsgBox: the choice is retrieved as the return value of the function, not via IfMsgBox
;if lines
;changes to continuation sections
;command line parameters retrieved via A_Args array not variables %0% %1% %2% etc
;var++, now fails if variable is blank, workaround: var := (var = "") ? 1 : var+1
;variable names/function names cannot start with a digit
;variable names can only contain letters/digits/underscore (and non-ASCII characters)
;deref operator removed: vNum := *var -> vNum := NumGet(var, 0, "UChar")
;old-style File/Reg loops removed
;bitwise-NOT always 64-bit, a two-way compatible workaround is: ~var -> var ^ -1
;oFile.__Handle renamed to oFile.Handle
;functions: LV_XXX/SB_XXX/TV_XXX
;functions: Asc/ComObjEnwrap/ComObjMissing/ComObjParameter/ComObjUnwrap/ObjInsert/ObjRemove
;methods: Insert/Remove
;Transform: some subcommands have no AHK v2 equivalent
;if var is type - 'type' now expects a variable, not hardcoded text
;OnExit command

;have no equivalent:
;between (use <= and/or >=)
;contains/in (may be implemented in AHK v2 in future)
;Gui/GuiControl/GuiControlGet/Menu (use objects instead)
;IfMsgBox
;Progress/SplashImage/SplashTextOff/SplashTextOn

;current two-way compatibility obstacles:
;A_AllowMainWindow/A_IconHidden/A_IconTip
;double quotes: "" v. `" [although Chr(34) is a workaround]
;Loop

;key unresolved issues:
;ControlClick/ControlGetPos/ControlMove [in AHK v2, they use client coordinates, this would break scripts, unless 'A_CoordModeControl' existed]
;PixelGetColor/PixelSearch [line cannot be converted without the use of an auxiliary function RGB to BGR function, or a 'BGR' mode]

;==================================================

;NOTES: NOT REPLACED

;operators
;not -> !
;OR -> ||
;AND -> &&

;invalid ternary operators
;(cond) ? action1 -> (cond) ? action1 : action2 ['binary ternary operator' (which causes a silent error) to ternary operator]

;parameters
;identify parameters that look numeric, but that should be wrapped in double quotes e.g. 000 but should be "000"

;functions
;to tidy functions use the DllCall script corrector instead
;identify = instead of := in custom function definitions
;check for ambiguity: InStr/SubStr/RegExMatch/RegExReplace
;cannot use "" for output variable parameters e.g. RegExMatch/StrReplace/RegExReplace

;problem lines
;- IfXXX and a 'command' on one line (these are currently ignored i.e. not converted)
;you can have two 'commands' on one line, if the first 'command' is one of these eight:
;IfEqual|IfGreater|IfGreaterOrEqual|IfLess|IfLessOrEqual|IfNotEqual
;IfInString|IfNotInString
;- if var contains ,,
;- using commas in the last parameter
;e.g. FormatTime has 3 parameters, so the script can't convert this, which it sees as 5 parameters:
;FormatTime, vDate,, dd,MM,yyyy
;- FileAppend with an output path of * or ** could potentially be a problem

;potential source of bugs
;a continuation section start/end not correctly identified
;a comment section start/end not correctly identified
;code split over multiple lines

;warning
;this script doesn't necessarily preserve whitespace between parameters

;==================================================

;NOTES: FUNCTIONS USED

;functions used/mentioned in script:
;JEE_ArrayToCmd
;JEE_ArrayToFunc
;JEE_ColRGBIsBGR
;JEE_CmdParamClean
;JEE_CmdPrepare
;JEE_Cvt
;JEE_CvtCmdGetList
;JEE_CvtJoin
;JEE_ExpTrim
;JEE_GetSelectedText
;JEE_ObjList
;JEE_ObjPopBlank
;JEE_ObjShuffleFrom
;JEE_StrCount
;JEE_StrJoin
;JEE_StrUnused
;JEE_WinMergeCompareStrings

;two-way compatible functions to replace/add to AHK functionality
;- operators:
;JEE_Between
;JEE_MC
;JEE_MX
;JEE_StrIsType
;- raw read/write:
;JEE_ClipboardFromFile
;JEE_ClipboardToFile
;JEE_FileReadBin
;JEE_FileWriteBin
;- other:
;JEE_ComObjMissing
;JEE_InStr
;JEE_RegExMatch
;JEE_RegExReplace
;JEE_SubStr

;functions mentioned in script (not included):
;JEE_InputBox [different parameter order: Text, DEFAULT, Title, Options][same as InputBox but with 'AX_' variables to specify a font]
;JEE_MsgBox [same as MsgBox but with 'AX_' variables to specify a font]

;functions mentioned in script (not implemented):
;JEE_Progress
;JEE_SplashImage

;behind the scenes:
;use '% ""' with JEE_ArrayToFunc to ensure empty string cf. blank parameter

;==================================================

;NOTES: 'UGLY' CONVERSIONS

;there are some commands that *can* be converted automatically,
;but it may be better to convert these manually,
;by default such are conversions are not done
;vDoFileReadLine := 0 ;use FileRead and StrSplit
;vDoGetKeyState := 0 ;GetKeyState command to GetKeyState function
;vDoStringSplit := 0 ;StringSplit to Loop

;FileReadLine, OutputVar, % Filename, LineNum
;OutputVar := StrSplit(FileRead(Filename), "`n", "`r")[LineNum] ;file read line

;GetKeyState, OutputVar, % KeyName, % Mode
;OutputVar := {0:"U",1:"D"}[GetKeyState(KeyName, Mode)]

;StringSplit, OutputArray, InputVar, `n, `r
;Loop, Parse, InputVar, `n, `r ;string split
;	OutputArray%A_Index% := A_LoopField, OutputArray0 := A_Index

;==================================================

;MAIN SECTION 2 - OPTIONS

;INITIALISE: OPTIONS

;special options:
vDoJeeCustom := 0

;hotkeys
vHotkeyConvert := "^q" ;for SubConvert
vHotkeyCheckLines := "^#w" ;for SubCheckLines

;dir to output changelogs for converted scripts
vDirLog := A_Desktop "\z ahk convert"

;block conversions
;e.g. block conversions that would sometimes require auxiliary functions:
;vListBlock := "ControlClick,ControlGetPos,ControlMove"
;vListBlock := "InputBox"
vListBlock := ""

;comment out redundant lines (redundant commands/directives)
vListCommentOut := "#NoEnv,AutoTrim,SetBatchLines"
vListCommentOut := ""

;AHK v1 incompatible conversions:
;current two-way compatibility problems:
;0: keep it AHK v1 compatible
;1: do the less compatible conversion 'AHK v2-ready'
;note: currently, the converter does not make Loop 'AHK v2-ready'
;note: currently, the converter always converts to Chr(34), not "" or `"
vDoLoop := 0 ;convert Loop parameters to make them AHK v2-ready
vDoLoopForceExp := 0 ;convert Loop parameters to force-expression style (otherwise leave unchanged): Loop, Parse, vText, % ","
vDoLoopTidy := 1 ;e.g. %var% to % var
vDoDoubleQuotes := 1 ;[NOT IMPLEMENTED] Chr(34): "" v. `"

;do the more backwards compatible conversion
;0: convert to the function with the more similar name (do the less compatible conversion)
vDoStringLower := 0 ;use Format instead of StrLower
vDoStringUpper := 0 ;use Format instead of StrUpper
vDoWinGetID := 0 ;use WinExist instead of WinGetID

;alternative functions
;0: use built-in functions
vDoJeeMsgBox := 0 ;to JEE_MsgBox (big font)
vDoJeeInputBox := 0 ;to JEE_InputBox (big font, different param order)

;allow 'ugly' conversions:
;can be converted automatically, but better converted manually:
;0: leave lines unconverted
vDoFileReadLine := 0 ;use FileRead and StrSplit
vDoGetKeyState := 0 ;GetKeyState command to GetKeyState function
vDoStringSplit := 0 ;StringSplit to Loop
vDoWinGetControlList := 0 ;[NOT IMPLEMENTED] use Loop to create a pseudo-array from the array
vDoWinGetControlListHwnd := 0 ;[NOT IMPLEMENTED] use Loop to create a pseudo-array from the array
vDoWinGetList := 0 ;[NOT IMPLEMENTED] use Loop to create a pseudo-array from the array

;auxiliary functions:
;allow conversions that require auxiliary functions:
;0: leave lines unconverted
vDoJeeBetween := 0 ;to JEE_Between
vDoJeeContains := 0 ;to JEE_MC (note: doesn't handle ,,)
vDoJeeIn := 0 ;to JEE_MX (note: doesn't handle ,,)
vDoJeeIsType := 0 ;to JEE_StrIsType
vDoJeeStringRight := 0 ;to JEE_SubStr
vDoJeeFileReadBin := 0 ;to JEE_FileReadBin
vDoJeeProgress := 0 ;[NOT IMPLEMENTED] to JEE_Progress
vDoJeeSplashImage := 0 ;[NOT IMPLEMENTED] to JEE_SplashImage
vDoJeeSplashText := 0 ;[NOT IMPLEMENTED] to JEE_Progress

;further auxiliary functions:
;JEE_ClipboardFromFile / JEE_ClipboardToFile ;work on both AHK v1/v2
;JEE_ComObjMissing ;works on both AHK v1/v2
;JEE_FileReadBin / JEE_FileWriteBin ;work on both AHK v1/v2
;JEE_InStr ;works like AHK v2 version on both AHK v1/v2
;JEE_RegExMatch / JEE_RegExReplace ;work like AHK v2 version on both AHK v1/v2

;further options
;0: add commas
;1: remove commas
vOptRemCommaCmd := 0 ;remove commas from commands
vOptRemCommaDrv := 1 ;remove commas from directives: '#Directive, ' -> '#Directive '
vOptRemCommaCFS := 1 ;remove commas from control flow statements (does not affect Loop)
vOptRemCommaLoop := 0 ;remove commas from Loop
vDoIfWinXXXDrv := 1 ;convert #IfWinXXX directives
vOptIndent := "`t" ;used for indentation if StringSplit is converted to a loop
vOptTidyC := 0 ;tidy commands
vOptLog := 1 ;write log file to log folder
vOptLogOmitCaseChange := 0 ;don't record info on line conversions that are simply a change of case
vOptRunLogLimit := 6 ;open log file if at least this many lines are changed
vOptRunLogAllow := 0 ;allow opening log file after conversion
vOptVarValueSimple := 1 ;convert var = value (simple instances)
vOptVarValueAll := 1 ;convert var = value (all instances)
vOptVarDoCmdNml := 1 ;convert commands that don't have a return value
vOptVarDoCmdRtn := 1 ;convert commands that do have a return value
vOptExcludeLinesInCommentSections := 0 ;correct code lines within comment sections (between /* and */)
vOptExcludeLinesThatContainString := 0
vOptExcludeString := ";[DO NOT CONVERT]"

;conversion mode:
;0 ;no changes: identify lines that are not AHK v2-ready
;11 ;AHK v1 (tidy script)
;12 ;AHK v1 to AHK v1 (convert)
;21 ;AHK v2 to AHK v1 ('unconvert') [NOT IMPLEMENTED]
;22 ;AHK v2 (tidy script) [NOT IMPLEMENTED]
vOptMode := 12

;==================================================

;MAIN SECTION 3 - INITIALISE

;==================================================

;INITIALISE: MAIN SCRIPT SETTINGS

#SingleInstance force
ListLines, Off
#KeyHistory 0
Menu, Tray, Click, 1
#NoEnv
AutoTrim, Off
#UseHook

SplitPath, A_ScriptName,,,, vScriptNameNoExt
Menu, Tray, Tip, % vScriptNameNoExt

#Include *i %A_ScriptDir%\Lib\ahk v1 converter lib.ahk
#Include *i %A_ScriptDir%\Lib\JEEAhk1FC.ahk
#Include *i %A_ScriptDir%\Lib\JEEAhk1FC+.ahk

;==================================================

;INITIALISE: COMMAND-LINE PARAMETERS (PATHS)

;vOptSpecial ;specify 'jee' for special JEE options (or set the contents of the #Include file (see lower down) to specify other options)
;vPathIn ;input file: script to be converted
;vPathOut ;output file: converted script output destination
;vPathOutLog ;output file: log of changes made

;MsgBox, % DllCall("kernel32\GetCommandLineW", Str)
vArgCount = %0%
if IsObject(A_Args)
{
	vArgCount := A_Args.Length()
	if vArgCount
		vIsCdLn := 1
	vPathIn := A_Args[1]
	vPathOut := A_Args[2]
	vPathOutLog := A_Args[3]
}
else if vArgCount
{
	vIsCdLn := 1
	vPathIn = %1%
	vPathOut = %2%
	vPathOutLog = %3%
}

if vIsCdLn
{
	if !FileExist(vPathIn)
	{
		MsgBox, % "error: input file not found:`r`n" vPathIn
		ExitApp
	}
	if FileExist(vPathOut)
	&& !(vPathOut = "*")
	&& !(vPathOut = "**")
	{
		MsgBox, % "error: output file already exists:`r`n" vPathOut
		ExitApp
	}
}

;==================================================

;INITIALISE: HOTKEYS

if !vIsCdLn
{
	if !(vHotkeyConvert = "")
		Hotkey, % vHotkeyConvert, SubConvert
	if !(vHotkeyCheckLines = "")
		Hotkey, % vHotkeyCheckLines, SubCheckLines
}

;==================================================

;INITIALISE: PREPARE VARIABLES/LISTS

vText := JEE_CvtCmdGetList()

;the double quote character
;vDQ := Chr(34)
vMode := vOptMode
;commands that require special handling:
vListCmd1X := "EnvAdd|EnvDiv|EnvMult|EnvSub|EnvUpdate|FileAppend|FileRead|FileReadLine|FileSetAttrib|GetKeyState|IfEqual|IfExist|IfGreater|IfGreaterOrEqual|IfInString|IfLess|IfLessOrEqual|IfNotEqual|IfNotExist|IfNotInString|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Input|InputBox|Menu|MsgBox|PixelGetColor|PixelSearch|Random|RegDelete|RegRead|RegWrite|SetEnv|Sort|SoundGetWaveVolume|SoundSetWaveVolume|StringGetPos|StringLeft|StringLower|StringMid|StringReplace|StringRight|StringSplit|StringTrimLeft|StringTrimRight|StringUpper|SysGet|Transform|TrayTip|WinGetActiveStats|WinGetActiveTitle|WinMove|WinSetTitle" ;doesn't include: "Control|ControlGet|Drive|DriveGet|Process|WinGet|WinSet"
;commands that can specified with no parameters in AHK v1 and AHK v2:
vListCmd1NP := "Click|ClipWait|ControlClick|ControlFocus|ControlGetPos|ControlMove|Critical|Edit|Exit|ExitApp|FileEncoding|FileRecycleEmpty|FileSetTime|Input|KeyHistory|ListHotkeys|ListLines|ListVars|MouseClick|MouseGetPos|MsgBox|Pause|Random|RegDelete|RegWrite|Reload|RunAs|SetCapsLockState|SetKeyDelay|SetNumLockState|SetScrollLockState|SetTimer|SoundBeep|StatusBarWait|Suspend|ToolTip|TrayTip|WinActivate|WinClose|WinGetPos|WinHide|WinKill|WinMaximize|WinMinimize|WinMinimizeAll|WinMinimizeAllUndo|WinMove|WinRestore|WinShow|WinWaitActive|WinWaitClose|WinWaitNotActive"
;commands that can be 0-parameter in AHK v1, but not in AHK v2:
;ControlSetText|EnvUpdate|FileAppend|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|OnExit|Progress|SplashImage|SplashTextOff|SplashTextOn|WinSetTitle

oParams := {}
Loop, Parse, vText, `n, `r
{
	if (A_LoopField = "[ahk2 functions]")
		break
	oTemp := StrSplit(A_LoopField, "=")
	oParams[oTemp.1] := oTemp.2
}

vListFnc2 := "", oFnc2 := {}
vListCmd1 := "", oCmd1 := {}
vXtra := "", oXtra := {}
Loop, Parse, vText, `n, `r
{
	vTemp := A_LoopField
	oTemp := StrSplit(A_LoopField, "=")
	oParams[oTemp.1] := oTemp.2
	if (SubStr(vTemp, 1, 1) = "[")
		vTempVar := ""
	if (SubStr(vTemp, 1, 16) = "[ahk2 functions]")
		vTempVar := "vListFnc2"
	else if (SubStr(vTemp, 1, 15) = "[ahk1 commands]")
		vTempVar := "vListCmd1"
	else if (SubStr(vTemp, 1, 12) = "[ahk1toahk2]")
		vTempVar := "vXtra"
	else if (vTempVar) && (oTemp.1)
	{
		%vTempVar% .= oTemp.1 "|"
		if (vTempVar = "vListFnc2")
			oFnc2[oTemp.1] := oTemp.2
		if (vTempVar = "vListCmd1")
			oCmd1[oTemp.1] := oTemp.2
		if (vTempVar = "vXtra")
		{
			oXtra[oTemp.1] := StrSplit(oTemp.2, ",")
			if (oXtra[oTemp.1].1 ~= "[URP]$")
				vXtra .= oTemp.1 "|"
		}
	}
}
oTemp := ""
vListFnc2 := RTrim(vListFnc2, "|")
vListCmd1 := RTrim(vListCmd1, "|")
vXtra := RTrim(vXtra, "|")
;MsgBox, % Clipboard := JEE_ObjList(oFnc2) ;e.g. key: FormatTime, value: SS
;MsgBox, % Clipboard := JEE_ObjList(oCmd1) ;e.g. key: FormatTime, value: OSS
;MsgBox, % Clipboard := JEE_ObjList(oXtra) ;e.g. key: FileSelectFolder, value: ["R", "DirSelect"]

;list commands/control flow statements/directives
;but not functions/methods/operators/variables
vListCFS := "break,continue,else,Gosub,Goto,if,Loop,return,while"
vListCFS .= ",catch,finally,for,throw,try,until"

vListCmd := "GetKeyState"
vListCmd .= ",BlockInput,Click,ClipWait,ControlClick,ControlFocus,ControlGetFocus,ControlGetPos,ControlGetText,ControlMove,ControlSend,ControlSendRaw,ControlSetText,CoordMode,Critical,DetectHiddenText,DetectHiddenWindows,Edit,EnvGet,EnvSet,Exit,ExitApp,FileAppend,FileCopy,FileCreateShortcut,FileDelete,FileGetAttrib,FileGetShortcut,FileGetSize,FileGetTime,FileGetVersion,FileInstall,FileMove,FileRead,FileRecycle,FileRecycleEmpty,FileSetAttrib,FileSetTime,FormatTime,GroupActivate,GroupAdd,GroupClose,GroupDeactivate,Hotkey,ImageSearch,IniDelete,IniRead,IniWrite,Input,InputBox,KeyHistory,KeyWait,ListHotkeys,ListLines,ListVars,Menu,MouseClick,MouseClickDrag,MouseGetPos,MouseMove,MsgBox,OutputDebug,Pause,PixelGetColor,PixelSearch,PostMessage,Random,RegDelete,RegRead,RegWrite,Reload,Run,RunAs,RunWait,Send,SendEvent,SendInput,SendMessage,SendMode,SendPlay,SendRaw,SetCapsLockState,SetControlDelay,SetDefaultMouseSpeed,SetKeyDelay,SetMouseDelay,SetNumLockState,SetScrollLockState,SetStoreCapsLockMode,SetTimer,SetTitleMatchMode,SetWinDelay,SetWorkingDir,Shutdown,Sleep,Sort,SoundBeep,SoundGet,SoundPlay,SoundSet,SplitPath,StatusBarGetText,StatusBarWait,StringCaseSense,Suspend,SysGet,Thread,ToolTip,TrayTip,WinActivate,WinActivateBottom,WinClose,WinGetClass,WinGetPos,WinGetText,WinGetTitle,WinHide,WinKill,WinMaximize,WinMinimize,WinMinimizeAll,WinMinimizeAllUndo,WinMove,WinRestore,WinSetTitle,WinShow,WinWait,WinWaitActive,WinWaitClose,WinWaitNotActive"
vListCmd .= ",AutoTrim,Control,ControlGet,Drive,DriveGet,DriveSpaceFree,EnvAdd,EnvDiv,EnvMult,EnvSub,EnvUpdate,FileCopyDir,FileCreateDir,FileMoveDir,FileReadLine,FileRemoveDir,FileSelectFile,FileSelectFolder,Gui,GuiControl,GuiControlGet,IfEqual,IfExist,IfGreater,IfGreaterOrEqual,IfInString,IfLess,IfLessOrEqual,IfMsgBox,IfNotEqual,IfNotExist,IfNotInString,IfWinActive,IfWinExist,IfWinNotActive,IfWinNotExist,Process,Progress,SetBatchLines,SetEnv,SetFormat,SoundGetWaveVolume,SoundSetWaveVolume,SplashImage,SplashTextOff,SplashTextOn,StringGetPos,StringLeft,StringLen,StringLower,StringMid,StringReplace,StringRight,StringSplit,StringTrimLeft,StringTrimRight,StringUpper,Transform,UrlDownloadToFile,WinGet,WinGetActiveStats,WinGetActiveTitle,WinMenuSelectItem,WinSet"
vListCmd .= "FileEncoding,SendLevel,SetRegView"

vListDrv := "#ClipboardTimeout,#ErrorStdOut,#HotkeyInterval,#HotkeyModifierTimeout,#Hotstring,#IfWinActive,#IfWinExist,#IfWinNotActive,#IfWinNotExist,#Include,#IncludeAgain,#InstallKeybdHook,#InstallMouseHook,#KeyHistory,#MaxHotkeysPerInterval,#MaxThreads,#MaxThreadsBuffer,#MaxThreadsPerHotkey,#NoTrayIcon,#Persistent,#SingleInstance,#UseHook,#WinActivateForce"
vListDrv .= ",#AllowSameLineComments,#CommentFlag,#Delimiter,#DerefChar,#EscapeChar,#LTrim,#MaxMem,#NoEnv"
vListDrv .= ",#If,#IfTimeout,#InputLevel,#MenuMaskKey,#Warn"

vListCFS := StrReplace(vListCFS, ",", "|")
vListCmd := StrReplace(vListCmd, ",", "|")
vListDrv := StrReplace(vListDrv, ",", "|")
vListAll := vListCFS "|" vListCmd "|" vListDrv
vListBlock := StrReplace(vListBlock, ",", "|")
vListCommentOut := StrReplace(vListCommentOut, ",", "|")

oName := {}
Loop, Parse, vListAll, |
{
	oName[A_LoopField] := A_LoopField
}
;MsgBox, % Clipboard := JEE_ObjList(oName)

;notes on valid variable names:

;Concepts and Conventions
;https://autohotkey.com/docs/Concepts.htm#variables
;Maximum length: 253 characters.
;Allowed characters: Letters, numbers, non-ASCII characters, and the following symbols: _ # @ $

;Regular Expressions (RegEx) - Quick Reference
;https://autohotkey.com/docs/misc/RegEx-QuickRef.htm#subpat
;To use the parentheses without the side-effect of capturing a subpattern, specify ?: as the first two characters inside the parentheses; for example: (?:.*)

;RegEx for a variable name:
;vVar := "[_A-Za-z]\w*" ;an underscore/letter followed by 0 or more underscores/letters/digits
;vVar := "[_A-Za-z]\w{0,252}" ;more precise: initial character, then 0-252 characters
vVar := "(?:[_A-Za-z]|[^[:ascii:]])(?:\w|[^[:ascii:]]){0,252}" ;if allow non-ASCII characters

vIsInit := 1

if vIsCdLn
{
	Gosub SubConvert
	ExitApp
}
return

;==================================================

;MAIN SECTION 4 - HOTKEY 1

;==================================================

SubConvert: ;convert selected code
if !vIsInit
{
	MsgBox, % "error: script not yet initialised"
	return
}

#Include *i %A_ScriptDir%\ahk converter options.txt
;#Include *i %A_ScriptDir%\%A_ScriptNameNoExt% Options.txt ;no such variable as 'A_ScriptNameNoExt'
;e.g. included file could consist of:
;vDoJeeCustom := 1

;STAGE - INITIALISE
if vDoJeeCustom ;JEE custom settings
{
	vDoJeeMsgBox := 1, vDoJeeInputBox := 1
	vDoJeeStringRight := 1
	vDoJeeFileReadBin := 1
	vDoStringSplit := 1
	vDoIfWinXXXDrv := 1
	vOptRemCommaLoop := 0
	vDoLoopTidy := 1
	vDoLoopForceExp := 0
	vDoLoop := 0, vDoDoubleQuotes := 0 ;AHK v1 incompatible

	vOptExcludeLinesThatContainString := 1
	vOptExcludeString := ";[DO NOT CONVERT]"

	vOptVarValueSimple := 1, vOptVarValueAll := 1
	vOptVarDoCmdNml := 1, vOptVarDoCmdRtn := 1
	vDoJeeBetween := 1, vDoJeeIsType := 1
	vDoJeeContains := 1, vDoJeeIn := 1
	;vOptLogOmitCaseChange := 1
}

WinGetTitle, vWinTitle, A
if !vIsCdLn
	vText := JEE_GetSelectedText()
	;ControlGet, vText, Selected,, Edit1, A
else
	FileRead, vText, % vPathIn

oChange := {}
VarSetCapacity(vListChange, 1000000*2)
if vIsCdLn
	vListChange := vPathIn "`r`n"
else
	vListChange := vWinTitle "`r`n"

vTextOrig := vText
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
vText := StrReplace(vText, "`r`n", "`n")
oArrayOrig := StrSplit(vText, "`n")
oArrayNew := StrSplit(vText, "`n")
vIsConSec := 0
vLastCodeLine := 0
vConSecPfx := ""
vConSecSfx := ""
vUnused := JEE_StrUnused(1, vText)

;if vDoWinGetID applied, change definition, from:
;WinGet-ID=XSR,WinGetID
;to:
;WinGet-ID=XSR,WinExist
oXtra["WinGet-ID"].2 := vDoWinGetID ? "WinExist" : "WinGetID"

;STAGE - CHECK EACH LINE
Loop, % oArrayNew.Length()
{
	vTemp := oArrayNew[A_Index]
	if (SubStr(vTemp, 1, 1) = ";")
		vTempCode := "", vComments := vTemp
	else if (vPos := RegExMatch(vTemp, "[ `t]+;"))
		vTempCode := SubStr(vTemp, 1, vPos-1), vComments := SubStr(vTemp, vPos)
	else
		vTempCode := vTemp, vComments := ""
	vCode := LTrim(vTempCode)
	vWhitespace := SubStr(vTempCode, 1, StrLen(vTempCode)-StrLen(vCode))
	;==============================
	;HANDLE CONTINUATION SECTIONS
	if (SubStr(vCode, 1, 1) = "(") && !InStr(vCode, ")")
	{
		vIsConSec := 1
		if !vLastCodeLine || !(vLastCodeLine < A_Index)
		{
			MsgBox, % "error: " vLastCodeLine " " A_Index
			return
		}
		Loop, % A_Index - vLastCodeLine
			oArrayNew[vLastCodeLine-1+A_Index] := vConSecPfx oArrayOrig[vLastCodeLine-1+A_Index] vConSecSfx
	}
	if vIsConSec && !(vConSecPfx vConSecSfx = "")
		oArrayNew[A_Index] := vConSecPfx vTemp vConSecSfx
	if (SubStr(vCode, 1, 1) = ")")
		vIsConSec := 0
	if vIsConSec
		continue
	if !(vCode = "")
		vLastCodeLine := A_Index
	;==============================
	vCodeOrig := vCode
	;==============================
	if !vOptExcludeLinesInCommentSections
		if (SubStr(vCode, 1, 2) = "*/")
			vIsComment := 0
		else if (SubStr(vCode, 1, 2) = "/*")
			vIsComment := 1
	if vIsComment
		continue
	if vOptExcludeLinesThatContainString
	&& InStr(vComments, vOptExcludeString)
		continue
	;==============================
	;STAGE - COMMANDS - TIDY PARAMETERS (NO CONVERSION TAKES PLACES)
	;off by default
	if vOptTidyC
	&& (vCode ~= "^\w+, ")
	&& !(vCode ~= "Click, ") ;exclude Click because of the nonstandard parameters it uses
	{
		vCodeX := JEE_CmdPrepare(vCode, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		vCode := oTemp.1
		vParams := oCmd1[oTemp.1]
		Loop, % oTemp.Length() - 1
		{
			vTemp2 := Trim(oTemp[A_Index+1])
			vTemp2X := vTemp2
			if !(SubStr(vTemp2, 1, 2) = "% ")
			&& (vTemp2 ~= "%") && !(SubStr(vParams, A_Index, 1) ~= "[OI]")
				vTemp2 := JEE_Cvt(vTemp2)
			if (vTemp2 = "")
				vCode .= ","
			else if !(vTemp2 = vTemp2X)
				vCode .= ", % " vTemp2
			else
				vCode .= ", " vTemp2
		}
	}
	;==============================
	;STAGE - VAR = VALUE -> VAR := VALUE (ASSIGNMENT)
	if !(vCode = vCodeOrig)
		Sleep, 0

	;var = string -> var := "string"
	else if (vOptVarValueSimple || vOptVarValueAll)
	&& RegExMatch(vCode, vRx := "^(" vVar ") = ([A-Za-z]+)$")
		vCode := RegExReplace(vCode, vRx, "$1 := " Chr(34) "$2" Chr(34))
	;var = % value -> var := value
	else if (vOptVarValueSimple || vOptVarValueAll)
	&& RegExMatch(vCode, vRx := "^(" vVar ") ?= ?%[ `t]+(.*)$")
		vCode := RegExReplace(vCode, vRx, "$1 := $2")

	else if vOptVarValueAll
	{
		;var = 0 -> var := 0
		if RegExMatch(vCode, vRx := "^(" vVar ") = 0$")
			vCode := RegExReplace(vCode, vRx, "$1 := 0")
		;var = 123 -> var := 123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = ([1-9]\d*)$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")
		;var = 0123 -> var := "0123" (num with leading zeros)
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (0\d*)$")
			vCode := RegExReplace(vCode, vRx, "$1 := " Chr(34) "$2" Chr(34))
		;var = 123.123 -> var := 123.123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = ([1-9]\d*\.\d*[1-9])$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")
		;var = 0123.123 -> var := "0123.123" (num with leading/trailing zeros)
		;var = 123.1230 -> var := "123.1230" (num with leading/trailing zeros)
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (\d+)\.(\d+)$")
			vCode := RegExReplace(vCode, vRx, "$1 := " Chr(34) "$2.$3" Chr(34))
		;var = 0x123 -> var := 0x123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (0x[0-9A-Fa-f]+)$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")

		;var = -123 -> var := -123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (-[1-9]\d*)$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")
		;var = -0123 -> var := "-0123" (num with leading zeros)
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (-0\d*)$")
			vCode := RegExReplace(vCode, vRx, "$1 := " Chr(34) "$2" Chr(34))
		;var = -123.123 -> var := -123.123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (-[1-9]\d*\.\d*[1-9])$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")
		;var = -0123.123 -> var := "-0123.123" (num with leading/trailing zeros)
		;var = -123.1230 -> var := "-123.1230" (num with leading/trailing zeros)
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (-\d+\.\d+)$")
			vCode := RegExReplace(vCode, vRx, "$1 := " Chr(34) "$2" Chr(34))
		;var = -0x123 -> var := -0x123
		else if RegExMatch(vCode, vRx := "^(" vVar ") = (-0x[0-9A-Fa-f]+)$")
			vCode := RegExReplace(vCode, vRx, "$1 := $2")

		;var = string -> var := "string"
		else if RegExMatch(vCode, vRx := "^(" vVar ") =( +)([A-Za-z ,'_]+)$")
			vCode := RegExReplace(vCode, vRx, "$1 :=" "$2" Chr(34) "$3" Chr(34))
		else if RegExMatch(vCode, vRx := "^(" vVar ") =( +)([\w ,']+)$")
			vCode := RegExReplace(vCode, vRx, "$1 :=" "$2" Chr(34) "$3" Chr(34))
		else if RegExMatch(vCode, vRx := "^(" vVar ") =( +)([\w ,':\\.]+)$")
			vCode := RegExReplace(vCode, vRx, "$1 :=" "$2" Chr(34) "$3" Chr(34))
		else if RegExMatch(vCode, vRx := "^(" vVar ") =( +)([^%``""]+)$")
			vCode := RegExReplace(vCode, vRx, "$1 :=" "$2" Chr(34) "$3" Chr(34))
		else if RegExMatch(vCode, vRx := "O)^(" vVar ") =( +)([^``""]+)$", o)
			vCode := o.1 " :=" o.2 JEE_Cvt(o.3)
		else if RegExMatch(vCode, vRx := "O)^(" vVar ") =( +)([^``]+)$", o)
			vCode := o.1 " :=" o.2 JEE_Cvt(o.3)
		else if RegExMatch(vCode, vRx := "O)^(" vVar ") =( +)(.+)$", o)
		&& !RegExMatch(vCode, "``[;,%]")
			vCode := o.1 " :=" o.2 JEE_Cvt(o.3)
		else if RegExMatch(vCode, vRx := "O)^(" vVar ") =( +)(.+)$", o)
			vCode := o.1 " :=" o.2 JEE_Cvt(o.3)
	}
	;==============================
	;STAGE - +=/-= (DATEADD/DATEDIFF) (INCREMENT/DECREMENT)
	;off always
	;commented out (via '&& 0') because of false positives
	;DateAdd(DateTime, Time, TimeUnits)
	if RegExMatch(vCode, vRx := "^" vVar " \+= ")
	&& !InStr(vCode, "``")
	&& 0
	{
		vCodeX := "Cmd, " StrReplace(vCode, " += ", ", ", "", 1)
		vCodeX := JEE_CmdPrepare(vCodeX, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		if (oTemp.Length() = 4)
			vCode := Trim(oTemp.2) " := DateAdd(" Trim(oTemp.2) ", " JEE_Cvt(oTemp.3, "E") ", " JEE_Cvt(oTemp.4) ")"
	}
	;commented out (via '&& 0') because of false positives
	;DateDiff
	else if RegExMatch(vCode, vRx := "^" vVar " -= ")
	&& !InStr(vCode, "``")
	&& 0
	{
		vCodeX := "Cmd, " StrReplace(vCode, " -= ", ", ", "", 1)
		vCodeX := JEE_CmdPrepare(vCodeX, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		if (oTemp.Length() = 4)
			vCode := Trim(oTemp.2) " := DateDiff(" Trim(oTemp.2) ", " JEE_Cvt(oTemp.3, "E") ", " JEE_Cvt(oTemp.4) ")"
	}
	;==============================
	;STAGE - IF (CONTROL FLOW STATEMENT)
	;STAGE - IF VAR OPERATOR VALUE
	;STAGE - IF VAR BETWEEN/CONTAINS/IN/IS TYPE
	;if var = string
	;if var = %var%
	if RegExMatch(vCode, vRx := "iO)^if[ `t]+(" vVar ")[ `t]*(>|<|>=|<=|=|<>|!=)[ `t]*(.*)$", o)
	{
		vCode := "if (" o.1 " " o.2 " " JEE_Cvt(o.3) ")"
	}

	;between
	;if var [not] between %lbound% and %ubound%
	else if vDoJeeBetween
	&& RegExMatch(vCode, vRx := "iO)^if (" vVar ")( not|) between (.*?) and (.*)$", o)
	{
		vPfx := (o.2 = "") ? "" : "!"
		v3 := JEE_Cvt(o.3), v4 := JEE_Cvt(o.4)
		if vDoJeeBetween
			vCode := "if " vPfx "JEE_Between(" o.1 ", " v3 ", " v4 ")"
		else if !vPfx
			vCode := "if (" o.1 " >= " v3 ") && (" o.1 " <= " v4 ")"
		else
			vCode := "if (" o.1 " < " v3 ") || (" o.1 " > " v4 ")"
	}

	;type
	;is type
	;if var is [not] type
	else if vDoJeeIsType
	&& RegExMatch(vCode, vRx := "iO)^if (" vVar ") is( not|) (.*)$", o)
	{
		vPfx := (o.2 = "") ? "" : "!"
		vCode := "if " vPfx "JEE_StrIsType(" o.1 ", " JEE_Cvt(o.3) ")"
	}

	;in/contains
	;conditional on: vDoJeeIn / vDoJeeContains
	;if var [not] contains %list%
	;if var [not] in %list%
	else if RegExMatch(vCode, vRx := "iO)^if[ `t]+(" vVar ")([ `t]+not|)[ `t]+(in|contains)[ `t]+(.*)$", o)
	&& !InStr(vCode, ",,")
	&& ((o.3 = "in") && vDoJeeIn || (o.3 = "contains") && vDoJeeContains)
	{
		vPfx := (o.2 = "") ? "" : "!"
		vFunc := (o.3 = "in") ? "JEE_MX" : "JEE_MC"

		;trying to convert to InStr where possible, is not necessarily a good idea,
		;e.g. there might be a force expression containing variables, that may or may not contain literal commas intended as delimiters
		;if (o.3 = "contains") && !InStr(o.4, ",")
		;	vFunc := "InStr"

		vCode := "if " vPfx vFunc "(" o.1 ", " JEE_Cvt(o.4, "S2") ")"
	}
	;==============================
	;STAGE - CONTROL FLOW STATEMENTS WITH PARENTHESES
	;e.g. 'if(' -> 'if ('
	if RegExMatch(vCode, "iO)^(" vListCFS ")[ `t]*(\(.*)", o)
	{
		vMatch := oName[o.1] " " o.2
	}
	;==============================
	;STAGE - COMMANDS - ADD COMMAS (AND CASE CORRECT COMMAND/CONTROL FLOW STATEMENT/DIRECTIVE NAMES)
	;note: if the script does 'continue' at any point, these changes are ignored
	;add initial commas to commands/control flow statements/directives
	;the converter expects the commas,
	;for conversion later,
	;they are optionally removed at the end
	if RegExMatch(vCode, vRx := "iO)^(" vListAll ")[ `t]+(?!:=)", o)
	{
		if (oName[o.1] ~= "^(" vListCFS ")$")
			vCode := oName[o.1] SubStr(vCode, StrLen(o.1)+1)
		else
			vCode := oName[o.1] ", " LTrim(SubStr(vCode, StrLen(o.1)+1))
	}
	else if RegExMatch(vCode, vRx := "iO)^(" vListAll "),", o)
	{
		vCode := oName[o.1] SubStr(vCode, StrLen(o.1)+1)
	}
	else if RegExMatch(vCode, vRx := "iO)^(" vListAll ")$", o)
	{
		vCode := oName[o.1]
	}
	;==============================
	;STAGE - AHK v1 TO AHK v2
	if (vCode ~= "^\w+,[ ,]")
	&& (vOptMode = 12)
	{
		vCodeX := JEE_CmdPrepare(vCode, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
	}

	if !(vListCommentOut = "")
	&& (vCode ~= "^(" vListCommentOut ")( |,|$)")
	{
		vCode := ";" vCode
	}
	;==================================================
	;STAGE - LOOP (CONTROL FLOW STATEMENT)
	;Loop
	;Loop [Count]
	;Loop Parse, String [, Delimiters, OmitChars]
	;Loop Files, FilePattern [, Mode]
	;Loop Reg, KeyName [, Mode]
	;Loop Read, InputFile [, OutputFile]
	if (vDoLoopForceExp || vDoLoop || vDoLoopTidy)
	&& (vCode ~= "^Loop ")
	{
		vCode := StrReplace(vCode, " ", ", ", "", 1)
	}
	if (vDoLoopForceExp || vDoLoop)
	&& (vCode ~= "^Loop, ")
	{
		;this script uses '% ' as a coding trick,
		;e.g. if JEE_Cvt() sees a leading '% ',
		;it returns the parameter minus the leading '% '
		vCodeX := JEE_CmdPrepare(vCode, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		vParams := "SSSSS"
		oTemp.2 := Trim(oTemp.2)
		if (oTemp.2 = "Parse")
			vParams := "SISSS"
		if (oTemp.2 ~= "^(Parse|Files|Reg|Read)$")
			oTemp.2 := "% " oTemp.2
		else if !(oTemp.Length() = 2)
			continue
		if vDoLoopForceExp && !vDoLoop
		{
			if (oTemp.Length() = 2) && !(oTemp.2 ~= "^\d+$")
				oTemp.2 := "% % " JEE_Cvt(oTemp.2)
			Loop, 3
			{
				if (oTemp.2 = "% Parse") && (A_Index = 1)
				{
					oTemp.3 := "% " Trim(oTemp.3)
					continue
				}
				vTemp := JEE_Cvt(oTemp[A_Index+2])
				if !(vTemp = "")
					oTemp[A_Index+2] := "% % " vTemp
			}
		}
		vCode := JEE_ArrayToFunc(oTemp, vParams)
		vCode := StrReplace(vCode, "(", ", ", "", 1)
		vCode := SubStr(vCode, 1, -1)
	}
	if vDoLoopTidy && !vDoLoopForceExp && !vDoLoop
	&& (vCode ~= "^Loop, ")
	{
		;this script uses '% ' as a coding trick,
		;e.g. if JEE_Cvt() sees a leading '% ',
		;it returns the parameter minus the leading '% '
		vCodeX := JEE_CmdPrepare(vCode, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		Loop, % oTemp.Length()
		{
			oTemp[A_Index] := Trim(oTemp[A_Index])
			if (oTemp[A_Index] ~= "``,")
				oTemp[A_Index] := "% " JEE_Cvt(oTemp[A_Index])
			if (A_Index = 1)
				continue
			else if (Trim(oTemp.2) = "% Parse") && (A_Index = 3)
				oTemp.3 := "% " Trim(oTemp.3)
			else if !(SubStr(oTemp[A_Index], 1, 2) = "% ")
			&& InStr(oTemp[A_Index], "%")
				oTemp[A_Index] := "% % " JEE_Cvt(oTemp[A_Index])
			else
				oTemp[A_Index] := "% " oTemp[A_Index]
		}
		vCode := JEE_ArrayToFunc(oTemp, vParams)
		vCode := StrReplace(vCode, "(", ", ", "", 1)
		vCode := SubStr(vCode, 1, -1)
	}
	;==================================================
	;STAGE - DIRECTIVES
	;#IfWin(Not)Active/IfWin(Not)Exist
	if vDoIfWinXXXDrv
	&& (vCode ~= "^#IfWin(Not)?(Active|Exist)$")
		vCode := "#If"
	if vDoIfWinXXXDrv
	&& RegExMatch(vCode, "O)^#IfWin(Not)?(Active|Exist),[ ,]", o)
	{
		vCodeX := SubStr(vCode, 2)
		vCodeX := JEE_CmdPrepare(vCodeX, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		vPfx := (o.1 = "") ? "" : "!"
		oTemp.1 := "#If " vPfx "Win" o.2
		vCode := JEE_ArrayToFunc(oTemp, "SSSSSS")
	}
	if vDoIfWinXXXDrv
	&& RegExMatch(vCode, "O)^#IfWin(Not)?(Active|Exist) ", o)
	{
		vCodeX := StrReplace(vCode, " ", ", ", "", 1)
		vCodeX := SubStr(vCodeX, 2)
		vCodeX := JEE_CmdPrepare(vCodeX, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		vPfx := (o.1 = "") ? "" : "!"
		oTemp.1 := "#If " vPfx "Win" o.2
		vCode := JEE_ArrayToFunc(oTemp, "SSSS")
	}
	;==================================================
	;STAGE - GENERAL CONVERT COMMANDS / CONVERT SPECIFIC COMMANDS / CONVERT IFXXX CONTROL FLOW STATEMENTS
	;COMMAND - (no-parameter commands)
	;COMMAND - EnvUpdate
	if (vCode = "EnvUpdate")
	&& vOptVarDoCmdNml
		vCode := "SendMessage(0x1A,,,, ""ahk_id 0xFFFF"") `;env update"
	else if (vCode = "Input")
	&& vOptVarDoCmdNml
		vCode .= "End()"
	else if (vCode = "MsgBox")
	&& vDoJeeMsgBox
		vCode := "JEE_MsgBox()"
	else if (vCode ~= "^(" vListCmd1NP ")$")
	&& vOptVarDoCmdNml
		vCode .= "()"
	else if (oXtra[oTemp.1].1 ~= "R") && !vOptVarDoCmdRtn
		continue
	else if !(oXtra[oTemp.1].1 ~= "R") && !vOptVarDoCmdNml
	&& oXtra.HasKey(oTemp.1)
		continue

	;COMMAND - Control|ControlGet|Drive|DriveGet|Process|WinGet|WinSet
	;note: this section ignores commands with an 'X' label (apart from the 7 commands listed in the line above)
	;ignored commands are handled in 'COMMAND - MULTIPLE - FURTHER' if they have a code block there (otherwise they are ignored)
	;COMMAND - MULTIPLE e.g.:
	;... BlockInput
	;... Click
	;... ClipWait
	;... ControlClick|ControlFocus|ControlGetFocus|ControlGetPos|ControlGetText|ControlMove|ControlSend|ControlSendRaw|ControlSetText
	;... CoordMode
	;... Critical
	;... DetectHiddenText|DetectHiddenWindows
	;... DriveSpaceFree
	;... EnvGet|EnvSet
	;... Exit|ExitApp
	;... FileCopy|FileCopyDir|FileCreateDir|FileCreateShortcut|FileDelete|FileEncoding|FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileMoveDir|FileRecycle|FileRecycleEmpty|FileRemoveDir|FileSelectFile|FileSelectFolder|FileSetTime
	;... FormatTime
	;... GroupActivate|GroupAdd|GroupClose|GroupDeactivate
	;... Hotkey
	;... ImageSearch
	;... IniDelete|IniRead|IniWrite
	;... KeyWait
	;... ListLines
	;... MouseClick|MouseClickDrag|MouseGetPos|MouseMove
	;... OutputDebug
	;... Pause
	;... PostMessage
	;... Run|RunAs|RunWait
	;... Send|SendEvent|SendInput|SendLevel|SendMode|SendPlay|SendRaw
	;... SendMessage
	;... SetCapsLockState|SetControlDelay|SetDefaultMouseSpeed|SetKeyDelay|SetMouseDelay|SetNumLockState|SetRegView|SetScrollLockState|SetStoreCapsLockMode|SetTitleMatchMode|SetWinDelay|SetWorkingDir
	;... SetTimer
	;... Shutdown
	;... Sleep
	;... SoundBeep|SoundGet|SoundPlay|SoundSet
	;... SplitPath
	;... StatusBarGetText|StatusBarWait
	;... StringCaseSense|StringLen
	;... Suspend
	;... Thread
	;... ToolTip
	;... UrlDownloadToFile
	;... WinActivate|WinActivateBottom|WinClose|WinGetClass|WinGetPos|WinGetText|WinGetTitle|WinHide|WinKill|WinMaximize|WinMenuSelectItem|WinMinimize|WinRestore|WinShow|WinWait|WinWaitActive|WinWaitClose|WinWaitNotActive
	else if (vCode ~= "^(" vXtra "),[ ,]")
	&& (oXtra[oTemp.1].1 ~= "[URP]$")
	&& (!InStr(oXtra[oTemp.1].1, "X") || (vCode ~= "^(Control|ControlGet|Drive|DriveGet|Process|WinGet|WinSet),"))
	&& (vOptMode = 12)
	&& !(vCode ~= "^(StringLower|StringUpper),[ ,]")
	&& ((vListBlock = "") || !(vCode ~= "^(" vListBlock "),[ ,]"))
	{
		vParams := oCmd1[oTemp.1]
		vCmdX := ""

		if (oXtra[oTemp.1].1 = "P")
		{
			vParams := oXtra[oTemp.1].3
			vCmdX := oTemp.RemoveAt(1)
			oTemp := JEE_ObjShuffleFrom(oTemp, vParams)
			oTemp.InsertAt(1, vCmdX)
			Loop, % oTemp.Length()
			{
				vTempXX := oTemp.Pop()
				if !(vTempXX = "")
				{
					oTemp.Push(vTempXX)
					break
				}
			}
		}

		if (oXtra[oTemp.1].1 = "XSR")
		{
			if (Trim(oTemp.1) ~= "^(WinGet)$")
				if (Trim(oTemp.3) ~= "^(List)$")
					continue
			if (Trim(oTemp.3) ~= "^(ControlList|ControlListHwnd)$")
				continue

			vParams := oCmd1[oTemp.1]
			vParams := SubStr(vParams, 1, 1) SubStr(vParams, 3)
			vCmdX := oTemp.1 "-" Trim(oTemp.3)
			if !oXtra.HasKey(vCmdX)
				continue
			vSubCmd := oTemp.RemoveAt(3)

			if (oTemp.1 = "ControlGet") && !(Trim(vSubCmd) ~= "^(List|FindString|Line)$")
				oTemp.RemoveAt(3), vParams := SubStr(vParams, 1, -1)
		}
		else if (oXtra[oTemp.1].1 = "XSU")
		{

			if (oTemp.1 = "Control")
				if (Trim(oTemp.2) ~= "^(Enable|Check)$")
					oTemp.3 := 1
				else if (Trim(oTemp.2) ~= "^(Disable|Uncheck)$")
					oTemp.3 := 0
				else if (Trim(oTemp.2) ~= "^(TabLeft|TabRight)$")
				{
					oTabX := oTemp.Clone()
					oTabX.RemoveAt(2, 2)
					oTabX.1 := "ControlGetTab"
					vPfxX := (Trim(oTemp.2) ~= "^(TabLeft)$") ? "-" : ""
					oTemp.3 := "% " vPfxX JEE_Cvt(oTemp.3, "E") "+" JEE_ArrayToFunc(oTabX, "SSSSS")
				}
				else if (Trim(oTemp.2) ~= "^(Hide|HideDropDown|Show|ShowDropDown)$")
					oTemp.RemoveAt(3)
				;other: Add|Choose|ChooseString|Delete|EditPaste|ExStyle|Style

			if (oTemp.1 = "WinSet")
				if (Trim(oTemp.2) ~= "^(Bottom|Redraw|Top)$")
					oTemp.RemoveAt(3)
				else if (Trim(oTemp.2) ~= "^(Enable)$")
					oTemp.3 := 1
				else if (Trim(oTemp.2) ~= "^(Disable)$")
					oTemp.3 := 0

			vParams := oCmd1[oTemp.1]
			vParams := SubStr(vParams, 2)
			vCmdX := oTemp.1 "-" Trim(oTemp.RemoveAt(2))
			if !oXtra.HasKey(vCmdX)
				continue
		}

		if (oXtra[oTemp.1].1 ~= "R$")
			vPfx := Trim(oTemp.RemoveAt(2)) " := ", vParams := SubStr(vParams, 2)
		else
			vPfx := ""

		if !(vCmdX = "")
			oTemp.1 := vCmdX
		if !(oXtra[oTemp.1].2 = "")
			oTemp.1 := oXtra[oTemp.1].2
		if (oTemp.Length() <= StrLen(vParams)+1)
			vCode := vPfx JEE_ArrayToFunc(oTemp, vParams)
		else
			vCode := vCodeOrig
	}

	;COMMAND - MULTIPLE - FURTHER
	;note: this will tidy parameters in any commands that haven't been converted to functions
	else if (vCode ~= "^(" vListCmd1 "),[ ,]")
	&& oCmd1.HasKey(oTemp.1)
	&& !(vCode ~= "^(" vListCmd1X "),[ ,]")
	&& (vOptMode = 12)
	{
		vParams := oCmd1[oTemp.1]
		if (oTemp.Length() <= StrLen(vParams)+1)
			vCode := JEE_ArrayToCmd(oTemp, vParams)
		else
			vCode := vCodeOrig
	}

	;COMMAND - MULTIPLE - SPECIAL
	;... EnvAdd|EnvSub|EnvMult|EnvDiv
	;... FileAppend
	;... FileRead
	;... FileReadLine
	;... FileSetAttrib
	;... GetKeyState
	;... IfEqual|IfGreater|IfGreaterOrEqual|IfLess|IfLessOrEqual|IfNotEqual
	;... IfExist|IfNotExist
	;... IfInString|IfNotInString
	;... IfWinActive|IfWinNotActive
	;... IfWinExist|IfWinNotExist
	;... Input
	;... InputBox
	;... Menu
	;... MsgBox
	;... PixelGetColor
	;... PixelSearch
	;... Progress
	;... Random
	;... RegDelete
	;... RegRead
	;... RegWrite
	;... SetEnv
	;... Sort
	;... SoundGetWaveVolume|SoundSetWaveVolume
	;... SplashImage
	;... SplashTextOff
	;... SplashTextOn
	;... StringGetPos
	;... StringLeft|StringRight
	;... StringLower|StringUpper
	;... StringMid
	;... StringReplace
	;... StringSplit
	;... StringTrimLeft|StringTrimRight
	;... SysGet
	;... Transform
	;... TrayTip
	;... WinGetActiveStats
	;... WinGetActiveTitle
	;... WinMove
	;... WinSetTitle
	else if (vCode ~= "^(" vListCmd1X "),[ ,]")
	&& oCmd1.HasKey(oTemp.1)
	&& (vOptMode = 12)
	&& ((vListBlock = "") || !(vCode ~= "^(" vListBlock "),[ ,]"))
	{
		vCodeX := JEE_CmdPrepare(vCode, 1, vUnused, vIsErrorNoEnd)
		oTemp := StrSplit(vCodeX, vUnused)
		if oXtra[oTemp.1].HasKey(2)
			vCmdX := oXtra[oTemp.1].2
		else
			vCmdX := oTemp.1
		for vKey, vValue in oTemp
			oTemp[vKey] := Trim(vValue)

		;COMMAND - EnvAdd|EnvSub|EnvMult|EnvDiv
		if (oTemp.1 ~= "^(EnvAdd|EnvSub|EnvMult|EnvDiv)$")
		&& (oTemp.Length() = 3)
		{
			vOp := {Add:"+",Sub:"-",Mult:"*",Div:"/"}[SubStr(oTemp.1, 4)]
			vCode := oTemp.2 " " vOp "= " JEE_Cvt(oTemp.3, "E")
		}
		if (oTemp.1 ~= "^(EnvAdd|EnvSub)$")
		&& (oTemp.Length() = 4)
			vCode := oTemp.2 " := " vCmdX "(" oTemp.2 ", " JEE_Cvt(oTemp.3, "E") ", " JEE_Cvt(oTemp.4) ")"

		;COMMAND - FileAppend
		;Text, EOL/Filename, Encoding
		;Text, Filename, Options
		if (oTemp.1 = "FileAppend")
		{
			if InStr(vCode, "ClipboardAll")
				continue
			if (oTemp.2 = "")
				oTemp.2 := "% """""
			oTemp.3 := JEE_Cvt(oTemp.3)
			if (SubStr(oTemp.3, 1, 4) = """*"" ")
				vOpt := "", oTemp.3 := "% " SubStr(oTemp.3, 5)
			else
				oTemp.3 := "% " oTemp.3, oTemp.4 := "% " JEE_CvtJoin("SS", "``n ", oTemp.4)
			;handle: FileAppend,, % vPath -> FileAppend("", vPath)
			if (oTemp.2 = "% """"") && (oTemp.4 = "% ""``n""")
				oTemp.4 := ""
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSS")
		}

		;COMMAND - FileRead
		;v1: OutputVar Options/Filename
		;v2: Filename Options
		;from: FileRead, vData, *c %vPath%
		;to: JEE_FileReadBin(vData, vPath)
		if (oTemp.1 = "FileRead") && vDoJeeFileReadBin
		&& RegExMatch(vCode, "O)FileRead, (" vVar "), \*c %(" vVar ")%", o)
		{
			vCode := "JEE_FileReadBin(" o.1 ", " o.2 ")"
		}
		else if (oTemp.1 = "FileRead")
		{
			vCode := Trim(oTemp.RemoveAt(2)) " := "
			vOpt := ""
			if RegExMatch(oTemp.2, "O)\*(m\d+)", o)
				vOpt := o.1, oTemp.2 := RegExReplace(oTemp.2, "\*(m\d+)")
			if InStr(oTemp.2, "*t")
				vOpt .= " ``n", oTemp.2 := StrReplace(oTemp.2, "*t")
			if RegExMatch(oTemp.2, "O)\*(P\d+)", o)
				vOpt .= " C" o.1, oTemp.2 := RegExReplace(oTemp.2, "\*(P\d+)")
			if InStr(oTemp.2, "*")
				continue
			if (vOpt = "")
				oTemp.3 := vOpt

			JEE_ObjPopBlank(oTemp)
			vCode .= JEE_ArrayToFunc(oTemp, "SS")
		}

		;COMMAND - FileReadLine
		if (oTemp.1 = "FileReadLine") && vDoFileReadLine
		{
			vCode := oTemp.2 " := StrSplit(FileRead(" JEE_Cvt(oTemp.3) "), ""``n"", ""``r"")[" JEE_Cvt(oTemp.4, "E") "] `;file read line"
		}

		;COMMAND - FileSetAttrib
		if (oTemp.1 = "FileSetAttrib")
		{
			if !(oTemp.4 ~= "^(|0|1|2)$")
			|| !(oTemp.5 ~= "^(|0|1)$")
				continue
			vNew := ""
			(oTemp.4 = 1) && vNew .= "FD"
			(oTemp.4 = 2) && vNew .= "D"
			(oTemp.5 = 1) && vNew .= "R"
			oTemp.4 := vNew, oTemp.5 := ""
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSS")
		}

		;COMMAND - GetKeyState
		if (oTemp.1 = "GetKeyState") && vDoGetKeyState
		{
			vPfx := (oTemp.RemoveAt(2)) " := "
			vCode := vPfx "{0:""U"",1:""D""}[" JEE_ArrayToFunc(oTemp) "]"
		}

		;COMMAND - IfEqual|IfGreater|IfGreaterOrEqual|IfLess|IfLessOrEqual|IfNotEqual
		if (oTemp.1 ~= "^(IfEqual|IfGreater|IfGreaterOrEqual|IfLess|IfLessOrEqual|IfNotEqual)$")
		&& (oTemp.Length() = 3)
		{
			vOp := {IfEqual:"=",IfGreater:">",IfGreaterOrEqual:">=",IfLess:"<",IfLessOrEqual:"<=",IfNotEqual:"<>"}[oTemp.1]
			vCode := "if (" oTemp.2 " " vOp " " JEE_Cvt(oTemp.3) ")"
		}

		;COMMAND - IfExist|IfNotExist
		;COMMAND - IfWinActive|IfWinNotActive
		;COMMAND - IfWinExist|IfWinNotExist
		if (oTemp.1 ~= "^If.*(Active|Exist)$") ;If(Not)Exist/IfWin(Not)Active/IfWin(Not)Exist
		{
			oTemp.1 := oXtra[oTemp.1].2
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSSS")
		}

		;COMMAND - IfInString|IfNotInString
		if (oTemp.1 = "IfInString")
		&& (oTemp.Length() = 3)
		{
			vCode := "if InStr(" oTemp.2 ", " JEE_Cvt(oTemp.3) ")"
		}
		if (oTemp.1 = "IfNotInString")
		&& (oTemp.Length() = 3)
		{
			vCode := "if !InStr(" oTemp.2 ", " JEE_Cvt(oTemp.3) ")"
		}

		;COMMAND - Input
		if (oTemp.1 = "Input")
		{
			vCode := Trim(oTemp.RemoveAt(2)) " := "
			vCode .= JEE_ArrayToFunc(oTemp, "SSS")
		}

		;COMMAND - InputBox
		;InputBox, OutputVar [, Title, Prompt, HIDE, Width, Height, X, Y, Font, Timeout, Default]
		;InputBox([Text, Title, Options, Default])
		;1:OutputVar 2:Title 3:Prompt[Text] 4:HIDE
		;5678: Width Height X Y
		;9,10,11: Font Timeout Default
		;2:Text[Prompt] 3:Title 4:Options 5:Default
		if (oTemp.1 = "InputBox")
		{
			Loop, % oTemp.Length()
				oTemp[A_Index] := Trim(oTemp[A_Index])
			oTemp.RemoveAt(1)
			vVar := Trim(oTemp.1)
			oNew := ["InputBox"]
			if !(oTemp.2 = "")
				oNew.3 := "% " JEE_Cvt(oTemp.2)
			if !(oTemp.3 = "")
				oNew.2 := "% " JEE_Cvt(oTemp.3)
			if !(oTemp.7 = "")
				oNew.4 .= " "" X"" " JEE_Cvt(oTemp.7)
			if !(oTemp.8 = "")
				oNew.4 .= " "" Y"" " JEE_Cvt(oTemp.8)
			if !(oTemp.5 = "")
				oNew.4 .= " "" W"" " JEE_Cvt(oTemp.5)
			if !(oTemp.6 = "")
				oNew.4 .= " "" H"" " JEE_Cvt(oTemp.6)
			if !(oTemp.10 = "")
				oNew.4 .= " "" T"" " JEE_Cvt(oTemp.10)
			if (oTemp.4 = "HIDE")
				oNew.4 .= " "" Password*"""
			else if !(oTemp.4 = "")
				oNew.4 .= " (" JEE_Cvt(oTemp.4) " = ""HIDE"" ? "" Password*"" : """")"
			if !(oTemp.11 = "")
				oNew.5 := "% " JEE_Cvt(oTemp.11)
			if !(oNew.4 = "")
				oNew.4 := Trim(oNew.4), oNew.4 := JEE_ExpTrim(oNew.4)
			if (SubStr(oNew.4, 1, 2) = Chr(34) " ")
				oNew.4 := Chr(34) SubStr(oNew.4, 3)
			if !(oNew.4 = "")
				oNew.4 := "% " oNew.4

			if vDoJeeInputBox
			{
				vDefault := oNew.RemoveAt(5)
				oNew.InsertAt(3, vDefault)
				vPfx := "JEE_"
			}
			else
				vPfx := ""
			JEE_ObjPopBlank(oNew)
			vCode := vVar " := " vPfx JEE_ArrayToFunc(oNew, "SSSS")
		}

		;COMMAND - Menu
		if (oTemp.1 = "Menu")
		{
			;Menu, Tray, Click, 1 -> A_TrayMenu.ClickCount := 1
			if (oTemp.1 = "Menu")
			&& (oTemp.2 = "Tray")
			&& (oTemp.3 = "Click")
				vCode := "A_TrayMenu.ClickCount := " JEE_Cvt(oTemp.4)
		}

		;COMMAND - MsgBox
		;Type, Title, Text, Timeout
		;Text, Title, Options
		if (oTemp.1 = "MsgBox")
		{
			vPfx := vDoJeeMsgBox ? "JEE_" : ""
			if (oTemp.Length() = 2)
				vCode := vPfx JEE_ArrayToFunc(oTemp, "S")
			else
			{
				if (oTemp.5 = "")
					vOpt := JEE_Cvt(oTemp.2)
				else
					vOpt := JEE_ExpTrim(JEE_CvtJoin("SSS", oTemp.2, "% "" T""", oTemp.5))
				if !(vOpt = "")
					vOpt := "% " vOpt
				oNew := ["MsgBox", oTemp.4, oTemp.3, vOpt]
				JEE_ObjPopBlank(oNew)
				vCode := "JEE_" JEE_ArrayToFunc(oNew, "SSS")
			}
		}

		;COMMAND - PixelGetColor
		if (oTemp.1 = "PixelGetColor")
		{
			if !(JEE_Cvt(oTemp.5) = Chr(34) "RGB" Chr(34))
				continue
			oTemp.5 := ""
			vCode := Trim(oTemp.RemoveAt(2)) " := "
			JEE_ObjPopBlank(oTemp)
			vCode .= JEE_ArrayToFunc(oTemp, "EES")
		}

		;COMMAND - PixelSearch
		if (oTemp.1 = "PixelSearch")
		{
			if !(JEE_Cvt(oTemp.10) = Chr(34) "RGB" Chr(34) )
			&& !(JEE_ColRGBIsBGR(Trim(oTemp.8)) && (oTemp.10 = ""))
				continue
			oTemp.10 := ""
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "OOEEEEEES")
		}

		;COMMAND - Progress
		if (oTemp.1 = "Progress")
		&& vDoJeeProgress
		{
			oTemp.1 := "JEE_Progress" ;[NOT IMPLEMENTED]
			vCode := JEE_ArrayToFunc(oTemp, "SSSSS")
		}

		;COMMAND - Random
		if (oTemp.1 = "Random")
		{
			if (oTemp.2 = "")
				vCode := "RandomSeed(" JEE_Cvt(oTemp.3, "E") ")"
			else
				vTemp := oTemp.RemoveAt(2), vCode := vTemp " := " JEE_ArrayToFunc(oTemp, "EE")
		}

		;COMMAND - RegDelete
		;v1o: RegDelete RK SK   ValueName
		;v1n: RegDelete KeyName ValueName
		;v2:  RegDelete KeyName ValueName
		;v2:  RegDeleteKey KeyName
		if (oTemp.1 = "RegDelete")
		{
			vIsReady := 0
			if !vIsReady && (oTemp.Length() = 4)
				if (oTemp.3 = "")
					oTemp.RemoveAt(3), vIsReady := 1
				else
					oTemp.2 := "% " JEE_CvtJoin("SSS", oTemp.2, "\", oTemp.RemoveAt(3)),, vIsReady := 1
			if !vIsReady && (oTemp.Length() = 3)
			&& InStr(oTemp.2, "\")
				vIsReady := 1
			if !vIsReady
				continue
			if (JEE_Cvt(oTemp.4) = Chr(34) "AHK_DEFAULT" Chr(34))
				oTemp.4 := ""
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSS")
		}

		;COMMAND - RegRead
		;v1o: OutputVar KeyName ValueName
		;v1n: OutputVar RK SK   ValueName
		;v2:            KeyName ValueName
		if (oTemp.1 = "RegRead")
		{
			vCode := Trim(oTemp.RemoveAt(2)) " := "
			vIsReady := 0
			if (oTemp.Length() = 1) || (oTemp.Length() = 2)
				vIsReady := 1
			if !vIsReady && (oTemp.Length() = 4)
				if (oTemp.3 = "")
					oTemp.RemoveAt(3), vIsReady := 1
				else
					oTemp.2 := "% " JEE_CvtJoin("SSS", oTemp.2, "\", oTemp.RemoveAt(3)), vIsReady := 1
			if !vIsReady && (oTemp.Length() = 3)
			&& InStr(oTemp.2, "\")
				vIsReady := 1
			if !vIsReady && (oTemp.Length() = 3)
			&& !RegExMatch(oTemp.2, "[\%]")
				oTemp.2 := "% " JEE_CvtJoin("SSS", oTemp.2, "\", oTemp.RemoveAt(3)), vIsReady := 1
			if !vIsReady
				continue
			JEE_ObjPopBlank(oTemp)
			vCode .= JEE_ArrayToFunc(oTemp, "SS")
		}

		;COMMAND - RegWrite
		;[4] v1o       ValueType KeyName ValueName Value
		;[5] v1n       ValueType RK SK   ValueName Value
		;[1] v1L Value
		;[4] v2  Value ValueType KeyName ValueName
		if (oTemp.1 = "RegWrite")
		{
			vIsReady := 0
			if (oTemp.Length() = 2)
				vIsReady := 1
			if !vIsReady && (oTemp.Length() = 6)
				if (oTemp.4 = "")
					oTemp.RemoveAt(4), vIsReady := 1
				else
					oTemp.3 := "% " JEE_CvtJoin("SSS", oTemp.3, "\", oTemp.RemoveAt(4)), vIsReady := 1
			if !vIsReady && (oTemp.Length() = 5)
			&& InStr(oTemp.3, "\")
				vIsReady := 1
			if !vIsReady && (oTemp.Length() = 5)
			&& !RegExMatch(oTemp.3, "[\%]")
				oTemp.3 := "% " JEE_CvtJoin("SSS", oTemp.3, "\", oTemp.RemoveAt(4)), vIsReady := 1
			if !vIsReady
				continue
			if (!oTemp.HasKey(5) || (oTemp.5 = "")) && (JEE_Cvt(oTemp.2) ~= "\x22(REG_SZ|REG_EXPAND_SZ|REG_MULTI_SZ|REG_BINARY)\x22")
				oTemp.5 := "% """""
			else if (!oTemp.HasKey(5) || (oTemp.5 = "")) && (JEE_Cvt(oTemp.2) = Chr(34) "REG_DWORD" Chr(34))
				oTemp.5 := 0
			else if (!oTemp.HasKey(5) || (oTemp.5 = ""))
				continue
			vValue := oTemp.RemoveAt(5), oTemp.InsertAt(2, vValue)
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSSS")
		}

		;COMMAND - SetEnv
		if (oTemp.1 = "SetEnv")
		{
			vCode := oTemp.2 " := " JEE_Cvt(oTemp.3)
		}

		;COMMAND - Sort
		if (oTemp.1 = "Sort")
		&& (oTemp.Length() = 3)
			vCode := oTemp.2 " := " vCmdX "(" oTemp.2 ", " JEE_Cvt(oTemp.3) ")"
		if (oTemp.1 = "Sort")
		&& (oTemp.Length() = 2)
			vCode := oTemp.2 " := " vCmdX "(" oTemp.2 ")"

		;COMMAND - SoundGetWaveVolume|SoundSetWaveVolume
		;SoundGetWaveVolume, OutputVar [, DeviceNumber]
		;SoundGet, OutputVar, Wave, Volume [, DeviceNumber]
		;SoundSetWaveVolume, Percent [, DeviceNumber]
		;SoundSet, NewSetting, Wave, Volume [, DeviceNumber]
		if (oTemp.1 = "SoundGetWaveVolume")
		{
			vTemp := oTemp.RemoveAt(2)
			oTemp.1 := "SoundGet"
			oTemp.InsertAt(2, "Wave", "Volume")
			JEE_ObjPopBlank(oTemp)
			vCode := Trim(vTemp) " := " JEE_ArrayToFunc(oTemp, "SSE")
		}
		if (oTemp.1 = "SoundSetWaveVolume")
		{
			oTemp.1 := "SoundSet"
			oTemp.InsertAt(3, "Wave", "Volume")
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "ESSE")
		}

		;COMMAND - SplashImage
		if (oTemp.1 = "SplashImage")
		&& vDoJeeSplashImage
		{
			oTemp.1 := "JEE_SplashImage" ;[NOT IMPLEMENTED]
			vCode := JEE_ArrayToFunc(oTemp, "SSSSSS")
		}

		;COMMAND - SplashImage
		if (oTemp.1 = "SplashImage")
		&& vDoJeeSplashImage
		{
			oTemp.1 := "JEE_SplashImage" ;[NOT IMPLEMENTED]
			vCode := JEE_ArrayToFunc(oTemp, "SSSSSS")
		}

		;COMMAND - SplashTextOff
		if (oTemp.1 = "SplashTextOff")
		&& vDoJeeSplashText
		{
		}

		;COMMAND - SplashTextOn
		if (oTemp.1 = "SplashTextOn")
		&& vDoJeeSplashText
		{
		}

		;COMMAND - StringGetPos
		;StringGetPos, OutputVar, InputVar, SearchText [, L#|R#, Offset]
		if (oTemp.1 = "StringGetPos")
		{
			(oTemp.5 = "1") && (oTemp.5 := "R1")
			if !(oTemp.5 = "") & !(oTemp.5 ~= "^[LR]\d+")
				continue
			vIsR := InStr(oTemp.5, "R")
			vOcc := SubStr(oTemp.5, 2)
			oTemp.5 := RegExReplace(oTemp.5, "[LR]")
			if vIsR && (oTemp.6 = "")
				oTemp.6 := 0
			if vIsR && !(oTemp.6 = "")
				oTemp.6 := "% -1-" JEE_Cvt(oTemp.6, "E")
			else if oTemp.6
				oTemp.6 := "% 1+" JEE_Cvt(oTemp.6, "E")
			oTemp2 := ["InStr", oTemp.3, oTemp.4, "", oTemp.6, oTemp.5]
			JEE_ObjPopBlank(oTemp2)
			vCode := oTemp.2 " := " JEE_ArrayToFunc(oTemp2, "OSSS")
		}

		;COMMAND - StringLeft|StringRight
		if (oTemp.1 = "StringLeft")
		{
			vCode := oTemp.2 " := SubStr(" oTemp.3 ", 1, " JEE_Cvt(oTemp.4, "E") ")"
		}
		if (oTemp.1 = "StringRight") && vDoJeeStringRight
		{
			vCode := oTemp.2 " := JEE_SubStr(" oTemp.3 ", -" JEE_Cvt(oTemp.4, "E") ")"
		}

		;COMMAND - StringLower|StringUpper
		if ((oTemp.1 = "StringLower") && vDoStringLower)
		|| ((oTemp.1 = "StringUpper") && vDoStringUpper)
		{
			if (Trim(oTemp.4) = "T")
				vLetter := "T"
			else if (oTemp.4 = "")
				vLetter := SubStr(oTemp.1, 7, 1)
			else
				continue
			vCode := oTemp.2 " := Format(""{:" vLetter "}"", " JEE_Cvt(oTemp.3, "I") ")"
		}
		if ((oTemp.1 = "StringLower") && !vDoStringLower)
		|| ((oTemp.1 = "StringUpper") && !vDoStringUpper)
		{
			oTemp.1 := oXtra[oTemp.1].2
			if !(oTemp.4 = "")
				vCode := oTemp.2 " := " oTemp.1 "(" JEE_Cvt(oTemp.3, "I") ", " JEE_Cvt(oTemp.4) ")"
			else
				vCode := oTemp.2 " := " oTemp.1 "(" JEE_Cvt(oTemp.3, "I") ")"
		}

		;COMMAND - StringMid
		;StringMid, OutputVar, InputVar, StartChar [, Count, L]
		if (oTemp.1 = "StringMid")
		{
			if (oTemp.Length() >= 6)
				continue
			oTemp2 := ["SubStr", oTemp.3, "% 1+" JEE_Cvt(oTemp.4, "E"), oTemp.5]
			JEE_ObjPopBlank(oTemp2)
			vCode := oTemp.2 " := " JEE_ArrayToFunc(oTemp2, "OSEE")
		}

		;COMMAND - StringReplace
		if (oTemp.1 = "StringReplace")
		{
			if RegExMatch(vCode, vRx := "^StringReplace, (" vVar "), (" vVar "), ([A-Za-z_]+), ([A-Za-z_]+),, All$")
				vCode := RegExReplace(vCode, vRx, "$1 := StrReplace($2, " Chr(34) "$3" Chr(34) ", " Chr(34) "$4" Chr(34) ")")
			else if RegExMatch(vCode, vRx := "^StringReplace, (" vVar "), (" vVar "), ([^%``""]+),, All$")
				vCode := RegExReplace(vCode, vRx, "$1 := StrReplace($2, " Chr(34) "$3" Chr(34) ")")
			else if RegExMatch(vCode, vRx := "^StringReplace, (" vVar "), (" vVar "), ([^%``""]+), ([^%``""]+), All$")
				vCode := RegExReplace(vCode, vRx, "$1 := StrReplace($2, " Chr(34) "$3" Chr(34) ", " Chr(34) "$4" Chr(34) ")")
			else if RegExMatch(vCode, vRx := "^StringReplace, (" vVar "), (" vVar "), ([^%""]+), ([^%""]+), All$")
			&& !InStr(vCode, "```,") && !InStr(vCode, "```;")
				vCode := RegExReplace(vCode, vRx, "$1 := StrReplace($2, " Chr(34) "$3" Chr(34) ", " Chr(34) "$4" Chr(34) ")")
			else if RegExMatch(vCode, vRx := "^StringReplace, (" vVar "), (" vVar "), ([^%""]+),, All$")
			&& !InStr(vCode, "```,") && !InStr(vCode, "```;")
				vCode := RegExReplace(vCode, vRx, "$1 := StrReplace($2, " Chr(34) "$3" Chr(34) ")")

			else if RegExMatch(vCode, "^StringReplace, ")
			&& !InStr(vCode, "UseErrorLevel")
			{
				if (oTemp.Length() = 4)
					oTemp.Push("")
				if (oTemp.Length() = 5)
					oTemp.Push(0)
				if !(oTemp.Length() = 6)
					continue
				oTemp.6 := Trim(oTemp.6)
				if !(oTemp.6 ~= "^(All|A|1|0)$")
					continue
				oTemp.6 := (oTemp.6 = 0) ? 1 : ""

				oTemp.InsertAt(6, "")
				oTemp.1 := "StrReplace"
				vTemp := Trim(oTemp.RemoveAt(2))
				JEE_ObjPopBlank(oTemp)
				vCode := vTemp " := " JEE_ArrayToFunc(oTemp, "ISSOS")
			}
		}

		;COMMAND - StringSplit
		;StringSplit, OutputArray, InputVar, Delimiters, OmitChars
		if (oTemp.1 = "StringSplit") && vDoStringSplit
		{
			if (oTemp.Length() = 3)
				vCode := "Loop, Parse, " oTemp.3
			else if (oTemp.Length() = 4)
				vCode := "Loop, Parse, " oTemp.3 ", % " JEE_Cvt(oTemp.4)
			else if (oTemp.Length() = 5)
				vCode := "Loop, Parse, " oTemp.3 ", % " JEE_Cvt(oTemp.4) ", % " JEE_Cvt(oTemp.5)
			else
				continue
			if (SubStr(oTemp.2, 1, 2) = "% ")
				oTemp.2 := "%" SubStr(oTemp.2, 3) "%"
			vCode .= " `;string split`r`n" vWhitespace vOptIndent oTemp.2 "%A_Index% := A_LoopField, " oTemp.2 "0 := A_Index"
		}

		;COMMAND - StringTrimLeft|StringTrimRight
		if (oTemp.1 = "StringTrimLeft")
		{
			oTemp.4 := JEE_Cvt(oTemp.4, "E")
			if (oTemp.4 ~= "^\d+")
				oTemp.4++
			else
				oTemp.4 .= "+1"
			vCode := oTemp.2 " := SubStr(" oTemp.3 ", " oTemp.4 ")"
		}
		if (oTemp.1 = "StringTrimRight")
		{
			vCode := oTemp.2 " := SubStr(" oTemp.3 ", 1, " ("-"JEE_Cvt(oTemp.4, "E")) ")"
		}

		;COMMAND - SysGet
		if (oTemp.1 = "SysGet")
		{
			vVar := oTemp.2
			vFunc := oTemp.3
			vNum := oTemp.4
			vPfx := ""
			if (vFunc = "Monitor")
				oTemp := ["MonitorGet", vNum, vVar "Left", vVar "Top", vVar "Right", vVar "Bottom"]
			else if (vFunc = "MonitorWorkArea")
				oTemp := ["MonitorGetWorkArea", vNum, vVar "Left", vVar "Top", vVar "Right", vVar "Bottom"]
			else if (vFunc = "MonitorCount")
				vPfx := vVar " := ", oTemp := ["MonitorGetCount"]
			else if (vFunc = "MonitorPrimary")
				vPfx := vVar " := ", oTemp := ["MonitorGetPrimary"]
			else if (vFunc = "MonitorName")
				vPfx := vVar " := ", oTemp := ["MonitorGetName", vNum]
			else
				vPfx := vVar " := ", oTemp := ["SysGet", vFunc]
			JEE_ObjPopBlank(oTemp)
			vCode := vPfx JEE_ArrayToFunc(oTemp, "SOOOO")
		}

		;COMMAND - Transform
		;Transform, OutputVar, Cmd, Value1 [, Value2]
		if (oTemp.1 = "Transform")
		{
			;not available (5):
			;Unicode|HTML|Deref|FromCodePage|ToCodePage

			;available (24):
			;Asc|Chr
			;Mod|Pow|Exp|Sqrt|Log|Ln
			;Round|Ceil|Floor|Abs
			;Sin|Cos|Tan|ASin|ACos|ATan
			;BitNot|BitAnd|BitOr|BitXOr|BitShiftLeft|BitShiftRight

			;note:
			;Asc (Ord)
			;Pow (**)
			;BitNot (~)/BitAnd (&)
			;BitOr (|)/BitXOr (^)
			;BitShiftLeft (<<)/BitShiftRight (>>)

			if (oTemp.3 ~= "^(Unicode|HTML|Deref|FromCodePage|ToCodePage)$")
				continue
			(oTemp.3 = "Asc") && (oTemp.3 := "Ord")
			if !(oTemp.3 = "Pow") && !(oTemp.3 ~= "^Bit")
			{
				oTemp2 := [oTemp.3, oTemp.4, oTemp.5]
				JEE_ObjPopBlank(oTemp2)
				vCode := oTemp.2 " := " JEE_ArrayToFunc(oTemp2, "EE")
			}
			else
			{
				vOp := {Pow:"**",BitNot:"~",BitAnd:"&",BitOr:"|",BitXOr:"^",BitShiftLeft:"<<",BitShiftRight:">>"}[oTemp.3]
				vCode := oTemp.2 " := " JEE_Cvt(oTemp.4, "E") " " vOp " " JEE_Cvt(oTemp.5, "E")
			}
		}

		;COMMAND - TrayTip
		if (oTemp.1 = "TrayTip")
		{
			if !(oTemp.4 = "")
				oTemp.4 := JEE_CvtJoin("SEE", "S", oTemp.4, oTemp.5)
			else
				oTemp.4 := JEE_Cvt(oTemp.5, "E")
			oTemp.5 := ""
			oTemp := JEE_ObjShuffleFrom(oTemp, 1324)
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSS")
		}

		;COMMAND - WinGetActiveStats
		if (oTemp.1 = "WinGetActiveStats")
		{
			vCode := oTemp.2 " := WinGetTitle(""A"")"
			oTemp := ["WinGetPos", oTemp.5, oTemp.6, oTemp.3, oTemp.4, "A"]
			vCode .= ", " JEE_ArrayToFunc(oTemp, "OOOO")
		}

		;COMMAND - WinGetActiveTitle
		if (oTemp.1 = "WinGetActiveTitle")
		{
			vCode := oTemp.2 " := WinGetTitle(""A"")"
		}

		;COMMAND - WinMove
		if (oTemp.1 = "WinMove")
		{
			if (oTemp.Length() > 3)
				oTemp := JEE_ObjShuffleFrom(oTemp, 145672389)
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "EEEESSSS")
		}

		;COMMAND - WinSetTitle
		if (oTemp.1 = "WinSetTitle")
		{
			if (oTemp.Length() > 2)
				oTemp := JEE_ObjShuffleFrom(oTemp, 142356)
			JEE_ObjPopBlank(oTemp)
			vCode := JEE_ArrayToFunc(oTemp, "SSSSS")
		}

		;OTHER COMMANDS, NOT NOT FULLY HANDLED:
		;COMMAND - AutoTrim (typically handled by vListCommentOut)
		;COMMAND - Gui|GuiControl|GuiControlGet
		;COMMAND - IfMsgBox
		;COMMAND - OnExit
		;COMMAND - SetBatchLines (typically handled by vListCommentOut)
		;COMMAND - SetFormat
	}
	;==============================
	;STAGE - COMMANDS - REMOVE COMMAS (FROM COMMAND/CONTROL FLOW STATEMENT/DIRECTIVE NAMES)
	;remove initial commas from commands/control flow statements/directives
	;removed based on settings
	;remove initial commas from commands/control flow statements/directives
	if vOptRemCommaCmd
	&& RegExMatch(vCode, vRx := "O)^(" vListCmd "),", o)
	{
		vCode := oName[o.1] " " LTrim(SubStr(vCode, StrLen(o.1)+2))
	}
	else if vOptRemCommaLoop
	&& RegExMatch(vCode, vRx := "^Loop,")
	{
		vCode := "Loop " LTrim(SubStr(vCode, StrLen(o.1)+2))
	}
	else if	vOptRemCommaCFS
	&& !(vCode ~= "^Loop")
	&& RegExMatch(vCode, vRx := "iO)^(" vListCFS "),", o)
	{
		vCode := oName[o.1] " " LTrim(SubStr(vCode, StrLen(o.1)+2))
	}
	if vOptRemCommaDrv
	&& RegExMatch(vCode, vRx := "iO)^(" vListDrv "),", o)
	{
		vCode := oName[o.1] " " LTrim(SubStr(vCode, StrLen(o.1)+2))
	}
	;==============================
	;STAGE - FINAL
	if !(vCode == vCodeOrig)
		oArrayNew[A_Index] := vWhitespace vCode vComments
	if (!vOptLogOmitCaseChange && !(vCode == vCodeOrig))
	|| (vOptLogOmitCaseChange && !(vCode = vCodeOrig))
	{
		if !oChange.HasKey("z" vCodeOrig)
		{
			oChange["z" vCodeOrig] := ""
			vCodeNext := oArrayNew[A_Index+1]
			vCodeNext := RegExReplace(vCodeNext, "^[ `t]*|[ `t]+;.*")
			;if the line has changed,
			;(and it wasn't a line that marked the assignment of a continuation section,)
			;add it to the log of changed lines
			if !(SubStr(vCodeNext, 1, 1) = "(") && !InStr(vCodeNext, ")")
				vListChange .= "[1]" vCodeOrig "`r`n[2]" vCode "`r`n"
		}
	}
}

Loop, % oArrayNew.Length()
	vOutput .= (A_Index=1?"":"`r`n") oArrayNew[A_Index]
if (vTextOrig == vOutput)
	MsgBox, % "same"
else if !vIsCdLn
{
	Clipboard := vOutput
	SendInput, ^v
}
;JEE_WinMergeCompareStrings("BEFORE`r`n" vTextOrig, "AFTER`r`n" vOutput)
if (vOptLog && !(vTextOrig == vOutput))
|| (vIsCdLn && !(vPathOutLog = ""))
{
	if vIsCdLn
	&& !(vPathOutLog = "")
	{
		vPathLog := vPathOutLog
		SplitPath, vPathLog,, vDirLog
	}
	else
		vPathLog := vDirLog "\z ahk convert " A_Now ".txt"
	if !FileExist(vDirLog)
		FileCreateDir, % vDirLog
	vPfx := (vPathLog ~= "^\*{1,2}$") ? "" : "*"
	if FileExist(vPathLog)
		FileAppend, % "`r`n" vListChange, % vPfx vPathLog, UTF-8
	else
		FileAppend, % vListChange, % vPfx vPathLog, UTF-8
	if !vIsCdLn
	&& (JEE_StrCount(vListChange, "`n") >= vOptRunLogLimit)
	&& vOptRunLogAllow
		Run, % vPathLog
}
vPfx := (vPathOut ~= "^\*{1,2}$") ? "" : "*"
if vIsCdLn && !(vPathOut = "")
	FileAppend, % vOutput, % vPfx vPathOut, UTF-8
if vIsCdLn
	ExitApp
return

;==================================================

;MAIN SECTION 5 - HOTKEY 2

;==================================================

SubCheckLines: ;check lines are standard (list nonstandard lines)
vMode := 1 ;only allow AHK v1-style lines
vMode := 2 ;only allow AHK v2-style lines
;vMode := 12 ;allow AHK v1/v2-style lines

if (vMode ~= 1)
	vV1VarValue := "| = "
else
	vV1VarValue := ""

WinGetTitle, vWinTitle, A
vText := JEE_GetSelectedText()
;ControlGet, vText, Selected,, Edit1, A
;vPath := A_ScriptDir "\AutoHotkey.ahk"
;FileRead, vText, % vPath

vTextOrig := vText
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2)
vText := StrReplace(vText, "`r`n", "`n")
oArrayOrig := StrSplit(vText, "`n")
oArrayNew := StrSplit(vText, "`n")
vIsConSec := 0
vLastCodeLine := 0
vConSecPfx := ""
vConSecSfx := ""
vUnused := JEE_StrUnused(1, vText)

Loop, % oArrayNew.Length()
{
	vTemp := oArrayNew[A_Index]
	if (SubStr(vTemp, 1, 1) = ";")
		vTempCode := "", vComments := vTemp
	else if (vPos := RegExMatch(vTemp, "[ `t]+;"))
		vTempCode := SubStr(vTemp, 1, vPos-1), vComments := SubStr(vTemp, vPos)
	else
		vTempCode := vTemp, vComments := ""
	vCode := LTrim(vTempCode)
	vWhitespace := SubStr(vTempCode, 1, StrLen(vTempCode)-StrLen(vCode))
	;==============================
	;HANDLE CONTINUATION SECTIONS
	if (SubStr(vCode, 1, 1) = "(") && !InStr(vCode, ")")
	{
		vIsConSec := 1
		if !vLastCodeLine || !(vLastCodeLine < A_Index)
		{
			MsgBox, % "error: " vLastCodeLine " " A_Index
			return
		}
		Loop, % A_Index - vLastCodeLine
			oArrayNew[vLastCodeLine-1+A_Index] := vConSecPfx oArrayOrig[vLastCodeLine-1+A_Index] vConSecSfx
	}
	if vIsConSec && !(vConSecPfx vConSecSfx = "")
		oArrayNew[A_Index] := vConSecPfx vTemp vConSecSfx
	if (SubStr(vCode, 1, 1) = ")")
		vIsConSec := 0
	if vIsConSec
		continue
	if !(vCode = "")
		vLastCodeLine := A_Index
	;==============================
	vCodeOrig := vCode
	;==============================
	if (vCode ~= "^(" vCmd "),[ ,]")
	{
		vCmdTemp := RegExReplace(vCode, ",.*")
		if (JEE_StrCount(vCode, ",") > StrLen(oParams[vCmdTemp]))
			vOutput .= "[PARAM COUNT]" vCode "`r`n"
	}

	if (vCode = "")
	|| (vCode ~= "^;")
	|| (vCode ~= "^\)")
	|| (vCode = "{")
	|| (vCode = "}")
	|| (vCode ~= "^(&&|\Q||\E)")
	|| (vCode ~= "^JEE_")
	|| (vCode ~= "^Gdip_")
	|| (vCode ~= "^Acc_")
	|| (vCode ~= "^(Gosub|Goto|Loop), ")
	|| (vCode ~= "^(" vListCFS ") ")
	|| ((vCode ~= "^(" vListCmd "),[ ,]") && (vMode ~= 1))
	|| ((vCode ~= "^(" vListDrv "),") && (vMode ~= 1))
	|| (vCode ~= "^(" vListDrv ") ")
	|| (vCode ~= "^(" vListFnc ")\(")
	|| (vCode ~= "^(else if ), ")
	|| (vCode ~= "^(global|static|local) ")
	|| (vCode ~= "^(global)$")
	|| (vCode ~= "^(#NoTrayIcon|#UseHook)$")
	|| (vCode ~= "^(break|continue|else|Loop|return)$")
	|| ((vMode ~= 1) && (vCode ~= "^(#IfWinActive|#NoEnv|" vListCmd1NP ")$"))
	|| (vCode ~= "::(Suspend)?$")
	|| (vCode ~= ":$")
	|| (vCode ~= "^(hCtl|hWnd|hFont|hIcon|hMenu|hSubMenu|hProc|hBuf|hDrop|hBitmap|hBrush|hDC|hFile|hData|hItem|hItemNext|_?[ov][A-Z]|Clipboard|pToken|pBitmap|pBuf|pDrop|pBrush|pData|pWndProc|pGraphics|ErrorLevel|ClipSaved|lParam|wParam|uMsg|IID_)[\w%[\]]*( := | .= " vV1VarValue "|\Q++\E|--)")
	|| (vCode ~= "^(oWB|oElt|oAcc|oYTPlayer|oWin|oDoc|oArray|oImg|oFile|oHTTP|oHTML|oTemp|oADODB|oChild|oXl|oWd|oOutput|oTrayInfo|oFontInfo)\w*[\.[]")
	|| (vCode ~= "^%v\w+%(|%v\w+%|%A_\w+%|0) (:=|.=) ")
	|| (vCode ~= "^. (\x22|v)")
		continue
	if (vMode ~= 2) && (vCode ~= "^(" vListFnc2 ")\(")
		continue
	;ignore more variables/functions
	if (vCode ~= "^\w+ := ")
	|| (vCode ~= "^\w+\(")
	|| (vCode ~= "^\w+\.\w+ := ")
		continue
	vOutput .= vCode "`r`n"
	;==============================
}

if (vOutput = "")
	MsgBox, % "same"
else
{
	Clipboard := vWinTitle "`r`n" vOutput
	MsgBox, % vOutput
}
;JEE_WinMergeCompareStrings("BEFORE`r`n" vTextOrig, "AFTER`r`n" vOutput)
return

;==================================================

Code: Select all

;==================================================

;an AHK v1 script
;AHK v1 to v2 converter by jeeswg
;additional library
;[first released: 2018-02-21]

;from:
;AHK v1 to AHK v2 converter (initial work) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?t=36754
;ahk v1 converter.ahk [main script]
;ahk v1 converter lib.ahk [this script]

;==================================================

;JEE_InputBox ;simple version
;JEE_MsgBox ;simple version
;JEE_ArrayToCmd
;JEE_ArrayToFunc
;JEE_CmdParamClean
;JEE_CmdPrepare
;JEE_ColRGBIsBGR
;JEE_Cvt
;JEE_CvtCmdGetList
;JEE_CvtJoin
;JEE_ExpTrim
;JEE_GetSelectedText
;JEE_ObjList
;JEE_ObjPopBlank
;JEE_ObjShuffleFrom
;JEE_StrCount
;JEE_StrJoin
;JEE_StrRept
;JEE_StrUnused
;JEE_WinMergeCompareStrings

;==================================================

;simple version of function
;the full version allows a custom font
JEE_InputBox(vText:="", vDefault:="", vWinTitle:="", vOpt:="")
{
	hWnd := WinGetID("A")
	if (vWinTitle = "")
		SplitPath(A_ScriptName,,,, vWinTitle)
	vRet := InputBox(vText, vWinTitle, vOpt, vDefault) ;[DO NOT CONVERT]
	WinActivate("ahk_id " hWnd)
	return vRet
}

;==================================================

;simple version of function
;the full version allows a custom font
JEE_MsgBox(vText:="Press OK to continue.", vWinTitle:="", vOpt:="")
{
	hWnd := WinGetID("A")
	;note: Ctrl+C on a MsgBox, converts `n to `r`n
	vText := StrReplace(vText, "`r`n", "`n")
	if (vWinTitle = "")
		SplitPath(A_ScriptName,,,, vWinTitle)
	vRet := MsgBox(vText, vWinTitle, vOpt) ;[DO NOT CONVERT]
	WinActivate("ahk_id " hWnd)
	return vRet
}

;==================================================

JEE_ArrayToCmd(oArray, vParams:="")
{
	vOutput := Trim(oArray.1) ","
	Loop, % oArray.Length()-1
	{
		vType := SubStr(vParams, A_Index, 1)
		vTemp := JEE_CmdParamClean(oArray[A_Index+1], vType)
		if (vTemp = "")
			vOutput .= vTemp ","
		else
			vOutput .= " " vTemp ","
	}
	return SubStr(vOutput, 1, -1)
}

;==================================================

JEE_ArrayToFunc(oArray, vParams:="")
{
	if (oArray.Length() = 1)
		return Trim(oArray.1) "()"
	vOutput := Trim(oArray.1) "("
	Loop, % oArray.Length()-1
	{
		vType := SubStr(vParams, A_Index, 1)
		vTemp := JEE_Cvt(oArray[A_Index+1], vType)
		if (A_Index = 1) || (vTemp = "")
			vOutput .= vTemp ","
		else
			vOutput .= " " vTemp ","
	}
	return SubStr(vOutput, 1, -1) ")"
}

;==================================================

JEE_CmdParamClean(vText, vType)
{
	vText := Trim(vText)
	if (SubStr(vText, 1, 2) = "% ")
	|| (vType = "I") || (vType = "O")
		return vText
	;if RegExMatch(vText, "[`` `t;,%]")
	if RegExMatch(vText, "%")
	&& !InStr(vText, "``")
		return "% " JEE_Cvt(vText)
	else
		return vText
}

;==================================================

;find the point where the command ends (assume first CR/LF) [CHECK: improve find end]
;replace commas with delimiter character
;start looking for command vPos within vText
;e.g.
;vText2 := JEE_CmdPrepare(vText, 1, Chr(1), vIsErrorNoEnd)

JEE_CmdPrepare(ByRef vText, vPos, vDelim, ByRef vIsErrorNoEnd)
{
	local oCommaCount,oInQuote,oQuoteCount,vChar,vCharLast,vCharLastX,vDepth,vDoReset,vIsXpn,vLen,vPos2,vPosEnd,vText2
	vIsErrorNoEnd := 0
	vLen := StrLen(vText)
	if !vPosEnd := RegExMatch(vText, "`r|`n",, vPos)
		vPosEnd := vLen+1
	vPosEnd--
	vText2 := SubStr(vText, vPos, vPosEnd-vPos+1)
	if RegExMatch(vText2, "^\w+[ `t]*,")
		vText2 := StrReplace(vText2, ",", vDelim,, 1)
	else if RegExMatch(vText2, "^\w+ ")
		vText2 := StrReplace(vText2, " ", vDelim,, 1)
	else if RegExMatch(vText2, "^\w+`t")
		vText2 := StrReplace(vText2, "`t", vDelim,, 1)
	else
		return vText2
	vPos2 := InStr(vText2, vDelim)
	vText2 := SubStr(vText2, 1, vPos2)
	vPos += vPos2
	vCharLastX := ""
	vIsXpn := 0
	Loop
	{
		vChar := SubStr(vText, vPos, 1)
		if (vPos > vPosEnd)
		{
			vIsErrorNoEnd := 0
			break
		}
		vPos++
		if vIsXpn
		{
			if (vChar = Chr(34))
				oInQuote[vDepth] := !oInQuote[vDepth], oQuoteCount.HasKey(vDepth) ? oQuoteCount[vDepth] += 1 : oQuoteCount[vDepth] := 1
			if (vCharLast = Chr(34))
			&& !(oQuoteCount[vDepth] & 1) && !(vChar = Chr(34))
				oInQuote[vDepth] := 0
			if (vChar ~= "\(|\[") && !oInQuote[vDepth]
				vDepth++
			if (vChar ~= "\)|]") && !oInQuote[vDepth]
				vDepth--
			if (vChar = ",") && !(vCharLast = "``") && !oInQuote[vDepth]
				oCommaCount[vDepth] := oCommaCount[vDepth] ? oCommaCount[vDepth]+1 : 1

			if !(vChar = " ") && !(vChar = "`t")
				vCharLastX .= vChar

			if (vChar = ",") && !(vCharLast = "``") && (vDepth = 1) && !oInQuote[1]
				vText2 .= vDelim, vCharLastX := "", vDoReset := 1, vIsXpn := 0
			else
				vText2 .= vChar
			;if !vDepth
			;	break
		}
		else
		{
			if !(vChar = " ") && !(vChar = "`t")
				vCharLastX .= vChar

			if (vChar = ",") && !(vCharLast = "``")
				vText2 .= vDelim, vCharLastX := ""
			else
				vText2 .= vChar
			if (vCharLastX = "%")
			&& ((vChar = " ") || (vChar = "`t"))
				vIsXpn := 1, vDoReset := 1
		}
		vCharLast := vChar
		if vDoReset
		{
			vDepth := 1
			vDoReset := 0
			oCommaCount := {}, oQuoteCount := {}, oInQuote := {}
		}
	}
	oCommaCount := oQuoteCount := oInQuote := ""
	return vText2
}

;==================================================

;do red/blue colour values match
JEE_ColRGBIsBGR(vNum)
{
	if RegExMatch(vNum, "^0x\d{6}$")
		return (SubStr(vNum, 3, 2) = SubStr(vNum, 7, 2))
	else if RegExMatch(vNum, "^\d+$")
		return ((vNum && 0xFF0000 >> 16) = (vNum && 0xFF))
	else
		return 0
}

;==================================================

;23:38 22/11/2017

;possible types: blank/S, I, O, E, Z, T
;parameters names (SIOE) based on: https://github.com/Lexikos/Scintillua-ahk/blob/master/ahk1.lua
;S: output as "text" or num
;S2: output as "text" or num (same as S, but as num less often)
;I: input variable
;O: output variable
;E: can be an expression
;Z: unchanged (except for cropping leading '% ')
;T: output as "text" or "num"
JEE_Cvt(vText, vType:="")
{
	vText := Trim(vText)
	if (vType = "Z")
		if (SubStr(vText, 1, 2) = "% ")
			return SubStr(vText, 3)
		else
			return vText

	if (vType = "I") && (SubStr(vText, 1, 2) = "% ")
		vText := SubStr(vText, 3)
	if (vType = "O") && (vText = Chr(34) Chr(34))
		return
	if (vType = "I") || (vType = "O")
		return vText

;MsgBox, % vType " [" vText "]"
	;check: handle 'can be an expression'
	if (vType = "E") && (SubStr(vText, 1, 2) = "% ")
		vText := SubStr(vText, 3)
;MsgBox, % vType " [" vText "]"
	if (vType = "E") && RegExMatch(vText, "^%\w+%$")
		return SubStr(vText, 2, -1)
;MsgBox, % vType " [" vText "]"
	if (vType = "E")
		return vText

	if (vType = "S2")
		if (vText = "0")
		|| RegExMatch(vText, "^-?[1-9]\d*$") ;e.g. -123
			return vText

	if !(vType = "T")
	&& !(vType = "S2")
		if RegExMatch(vText, "^-?\d+((\.)?\d+)?$") ;e.g. -123.123
		|| RegExMatch(vText, "^-?0x[\dA-Fa-f]+$") ;e.g. -0x123ABC
			return vText
	if (vText = "") ;blank
		return
	;if (vText = "") ;blank text
	;	return Chr(34) Chr(34)
	if (vText = Chr(34))
		return "Chr(34)"
	if (vText = "%A_Space%")
		return Chr(34) " " Chr(34)

	vText := StrReplace(vText, "%A_Tab%", "``t")
	vUnused := JEE_StrUnused(1, vText)
	vText := StrReplace(vText, "```,", ",")
	vText := StrReplace(vText, "```;", ";")
	vText := StrReplace(vText, " `;", " ```;")
	if (SubStr(vText, 1, 2) = "% ")
		return SubStr(vText, 3)
	vText := StrReplace(vText, "```%", vUnused)
	vText := StrReplace(vText, " ", "%A_Space%")
	vText := RegExReplace(vText, "(?<!%A_Space%)%A_Space%(?!%A_Space%)", " ")

	vOutput := ""
	Loop, Parse, vText, % "%"
	{
		vTemp := A_LoopField
		if (vTemp = "")
			continue
		if (A_Index & 1)
		{
			vTemp := StrReplace(vTemp, Chr(34), Chr(34) " Chr(34) " Chr(34))
			while RegExMatch(vTemp, " \x22\x22 ")
				vTemp := RegExReplace(vTemp, " \x22\x22 ", " ")
			if !(vTemp = "")
				vTemp := Chr(34) vTemp Chr(34)
			vTemp := RegExReplace(vTemp, "^\x22\x22 | \x22\x22$")
			vOutput .= vTemp " "
		}
		else
			vOutput .= Trim(A_LoopField) " "
	}
	vOutput := StrReplace(vOutput, vUnused, "%")
	return Trim(vOutput)
}

;==================================================

JEE_CvtCmdGetList()
{
	;command types:
	;P,,#,# - parameters shuffled
	;XM - need custom replace, parameters modified
	;XP - need custom replace, parameters shuffled
	;R - unchanged, but drop the first parameter (it becomes the return value)
	;U - unchanged
	;X - removed, have an equivalent function
	;XI - removed, if control flow statements
	;XX - removed, have no direct equivalent
	;XS - removed, split (subcommands to separate functions)

	;command info:
	;Cmd=CmdType,NewName,NewOrder,ParamTypes

	;[Thread subcommands not added]
	;Thread-Interrupt
	;Thread-NoTimers
	;Thread-Priority

	;[Transform subcommands: not available]
	;Transform-Deref
	;Transform-FromCodePage
	;Transform-HTML
	;Transform-ToCodePage
	;Transform-Unicode

	;[Transform subcommands: available]
	;Transform-Abs
	;Transform-ACos
	;Transform-Asc
	;Transform-ASin
	;Transform-ATan
	;Transform-BitAnd
	;Transform-BitNot
	;Transform-BitOr
	;Transform-BitShiftLeft
	;Transform-BitShiftRight
	;Transform-BitXOr
	;Transform-Ceil
	;Transform-Chr
	;Transform-Cos
	;Transform-Exp
	;Transform-Floor
	;Transform-Ln
	;Transform-Log
	;Transform-Mod
	;Transform-Pow
	;Transform-Round
	;Transform-Sin
	;Transform-Sqrt
	;Transform-Tan

	vText1 := " ;continuation section
	(LTrim
	[ahk1toahk2]
	ControlMove=P,,234516789,EEEESSSSS
	ControlSend=P,,213456,SSSSSS
	ControlSendRaw=P,,213456,SSSSSS
	ControlSetText=P,,213456,SSSSSS
	GroupAdd=P,,12356,SSSSS
	Sort=XPR
	WinMove=XP
	WinSetTitle=XP

	FileAppend=XM
	FileRead=XMR
	FileSetAttrib=XM
	Input=XMR
	InputBox=XMR
	MsgBox=XM
	PixelGetColor=XMR
	PixelSearch=XM
	Random=XMR
	RegDelete=XM
	RegRead=XMR
	RegWrite=XM
	SysGet=XMR
	TrayTip=XM

	Control=XSU
	ControlGet=XSR
	Drive=XSU
	DriveGet=XSR
	Process=XSU
	WinGet=XSR
	WinSet=XSU

	IfEqual=XI,if
	IfExist=XI,if FileExist
	IfGreater=XI,if
	IfGreaterOrEqual=XI,if
	IfInString=XI,if InStr
	IfLess=XI,if
	IfLessOrEqual=XI,if
	IfMsgBox=XI
	IfNotEqual=XI,if
	IfNotExist=XI,if !FileExist
	IfNotInString=XI,if !InStr
	IfWinActive=XI,if WinActive
	IfWinExist=XI,if WinExist
	IfWinNotActive=XI,if !WinActive
	IfWinNotExist=XI,if !WinExist

	AutoTrim=XX
	EnvUpdate=XX
	FileReadLine=XXR
	Gui=XX
	GuiControl=XX
	GuiControlGet=XXR
	Menu=XX
	Progress=XX
	SetBatchLines=XX
	SetFormat=XX
	SplashImage=XX
	SplashTextOff=XX
	SplashTextOn=XX
	StringSplit=XXR
	Transform=XX

	EnvAdd=XR,DateAdd
	EnvDiv=XR
	EnvMult=XR
	EnvSub=XR,DateDiff
	GetKeyState=XR
	OnExit=X
	SetEnv=X
	SoundGetWaveVolume=XR
	SoundSetWaveVolume=X
	StringGetPos=XR,InStr
	StringLeft=XR,SubStr
	StringMid=XR,SubStr
	StringReplace=XR,StrReplace
	StringRight=XR,SubStr
	StringTrimLeft=XR,SubStr
	StringTrimRight=XR,SubStr
	WinGetActiveStats=XR
	WinGetActiveTitle=XR

	ControlGetFocus=R
	ControlGetText=R
	DriveSpaceFree=R,DriveGetSpaceFree
	EnvGet=R
	FileGetAttrib=R
	FileGetSize=R
	FileGetTime=R
	FileGetVersion=R
	FileSelectFile=R,FileSelect
	FileSelectFolder=R,DirSelect
	FormatTime=R
	IniRead=R
	SoundGet=R
	StatusBarGetText=R
	StringLen=R,StrLen
	StringLower=R,StrLower
	StringUpper=R,StrUpper
	WinGetClass=R
	WinGetText=R
	WinGetTitle=R

	BlockInput=U
	Click=U
	ClipWait=U
	ControlClick=U
	ControlFocus=U
	ControlGetPos=U
	CoordMode=U
	Critical=U
	DetectHiddenText=U
	DetectHiddenWindows=U
	Edit=U
	EnvSet=U
	Exit=U
	ExitApp=U
	FileCopy=U
	FileCopyDir=U,DirCopy
	FileCreateDir=U,DirCreate
	FileCreateShortcut=U
	FileDelete=U
	FileEncoding=U
	FileGetShortcut=U
	FileInstall=U
	FileMove=U
	FileMoveDir=U,DirMove
	FileRecycle=U
	FileRecycleEmpty=U
	FileRemoveDir=U,DirDelete
	FileSetTime=U
	GroupActivate=U
	GroupClose=U
	GroupDeactivate=U
	Hotkey=U
	ImageSearch=U
	IniDelete=U
	IniWrite=U
	KeyHistory=U
	KeyWait=U
	ListHotkeys=U
	ListLines=U
	ListVars=U
	MouseClick=U
	MouseClickDrag=U
	MouseGetPos=U
	MouseMove=U
	OutputDebug=U
	Pause=U
	PostMessage=U
	Reload=U
	Run=U
	RunAs=U
	RunWait=U
	Send=U
	SendEvent=U
	SendInput=U
	SendLevel=U
	SendMessage=U
	SendMode=U
	SendPlay=U
	SendRaw=U
	SetCapsLockState=U
	SetControlDelay=U
	SetDefaultMouseSpeed=U
	SetKeyDelay=U
	SetMouseDelay=U
	SetNumLockState=U
	SetRegView=U
	SetScrollLockState=U
	SetStoreCapsLockMode=U
	SetTimer=U
	SetTitleMatchMode=U
	SetWinDelay=U
	SetWorkingDir=U
	Shutdown=U
	Sleep=U
	SoundBeep=U
	SoundPlay=U
	SoundSet=U
	SplitPath=U
	StatusBarWait=U
	StringCaseSense=U
	Suspend=U
	Thread=U
	ToolTip=U
	UrlDownloadToFile=U,Download
	WinActivate=U
	WinActivateBottom=U
	WinClose=U
	WinGetPos=U
	WinHide=U
	WinKill=U
	WinMaximize=U
	WinMenuSelectItem=U,MenuSelect
	WinMinimize=U
	WinMinimizeAll=U
	WinMinimizeAllUndo=U
	WinRestore=U
	WinShow=U
	WinWait=U
	WinWaitActive=U
	WinWaitClose=U
	WinWaitNotActive=U
	)"

	vText2 := " ;continuation section
	(LTrim
	;[ahk1 subcommands]
	Control-Add=XSU,ControlAddItem
	Control-Check=XSU,ControlSetChecked
	Control-Choose=XSU,ControlChoose
	Control-ChooseString=XSU,ControlChooseString
	Control-Delete=XSU,ControlDeleteItem
	Control-Disable=XSU,ControlSetEnabled
	Control-EditPaste=XSU,ControlEditPaste
	Control-Enable=XSU,ControlSetEnabled
	Control-ExStyle=XSU,ControlSetExStyle
	Control-Hide=XSU,ControlHide
	Control-HideDropDown=XSU,ControlHideDropDown
	Control-Show=XSU,ControlShow
	Control-ShowDropDown=XSU,ControlShowDropDown
	Control-Style=XSU,ControlSetStyle
	Control-TabLeft=XSU,ControlSetTab
	Control-TabRight=XSU,ControlSetTab
	Control-Uncheck=XSU,ControlSetChecked

	ControlGet-Checked=XSR,ControlGetChecked
	ControlGet-Choice=XSR,ControlGetChoice
	ControlGet-CurrentCol=XSR,ControlGetCurrentCol
	ControlGet-CurrentLine=XSR,ControlGetCurrentLine
	ControlGet-Enabled=XSR,ControlGetEnabled
	ControlGet-ExStyle=XSR,ControlGetExStyle
	ControlGet-FindString=XSR,ControlFindItem
	ControlGet-Hwnd=XSR,ControlGetHwnd
	ControlGet-Line=XSR,ControlGetLine
	ControlGet-LineCount=XSR,ControlGetLineCount
	ControlGet-List=XSR,ControlGetList
	ControlGet-Selected=XSR,ControlGetSelected
	ControlGet-Style=XSR,ControlGetStyle
	ControlGet-Tab=XSR,ControlGetTab
	ControlGet-Visible=XSR,ControlGetVisible

	;see also: DriveSpaceFree=XSR,DriveGetSpaceFree
	DriveGet-Capacity=XSR,DriveGetCapacity
	DriveGet-Filesystem=XSR,DriveGetFilesystem
	DriveGet-Label=XSR,DriveGetLabel
	DriveGet-List=XSR,DriveGetList
	DriveGet-Serial=XSR,DriveGetSerial
	DriveGet-Status=XSR,DriveGetStatus
	DriveGet-StatusCD=XSR,DriveGetStatusCD
	DriveGet-Type=XSR,DriveGetType

	Drive-Eject=XSU,DriveEject
	Drive-Label=XSU,DriveSetLabel
	Drive-Lock=XSU,DriveLock
	Drive-Unlock=XSU,DriveUnlock

	Process-Close=XSU,ProcessClose
	Process-Exist=XSU,ProcessExist
	Process-Priority=XSU,ProcessSetPriority
	Process-Wait=XSU,ProcessWait
	Process-WaitClose=XSU,ProcessWaitClose

	WinGet-ControlList=XSR,WinGetControls
	WinGet-ControlListHwnd=XSR,WinGetControlsHwnd
	WinGet-Count=XSR,WinGetCount
	WinGet-ExStyle=XSR,WinGetExStyle
	WinGet-ID=XSR,WinGetID
	WinGet-IDLast=XSR,WinGetIDLast
	WinGet-List=XSR,WinGetList
	WinGet-MinMax=XSR,WinGetMinMax
	WinGet-PID=XSR,WinGetPID
	WinGet-ProcessName=XSR,WinGetProcessName
	WinGet-ProcessPath=XSR,WinGetProcessPath
	WinGet-Style=XSR,WinGetStyle
	WinGet-TransColor=XSR,WinGetTransColor
	WinGet-Transparent=XSR,WinGetTransparent

	WinSet-AlwaysOnTop=XSU,WinSetAlwaysOnTop
	WinSet-Bottom=XSU,WinMoveBottom
	WinSet-Disable=XSU,WinSetEnabled
	WinSet-Enable=XSU,WinSetEnabled
	WinSet-ExStyle=XSU,WinSetExStyle
	WinSet-Redraw=XSU,WinRedraw
	WinSet-Region=XSU,WinSetRegion
	WinSet-Style=XSU,WinSetStyle
	WinSet-Top=XSU,WinMoveTop
	WinSet-TransColor=XSU,WinSetTransColor
	WinSet-Transparent=XSU,WinSetTransparent

	[ahk1 commands]
	AutoTrim=S
	BlockInput=S
	Click=S
	ClipWait=EE
	Control=SSSSSSS
	ControlClick=SSSSESSS
	ControlFocus=SSSSS
	ControlGet=OSSSSSSS
	ControlGetFocus=OSSSS
	ControlGetPos=OOOOSSSSS
	ControlGetText=OSSSSS
	ControlMove=SEEEESSSS
	ControlSend=SSSSSS
	ControlSendRaw=SSSSSS
	ControlSetText=SSSSSS
	CoordMode=SS
	Critical=S
	DetectHiddenText=S
	DetectHiddenWindows=S
	Drive=SSS
	DriveGet=OSS
	DriveSpaceFree=OS
	Edit=
	EnvAdd=OES
	EnvDiv=OE
	EnvGet=OS
	EnvMult=OE
	EnvSet=SS
	EnvSub=OES
	EnvUpdate=
	Exit=E
	ExitApp=E
	FileAppend=SSS
	FileCopy=SSE
	FileCopyDir=SSE
	FileCreateDir=S
	FileCreateShortcut=SSSSSSSEE
	FileDelete=S
	FileEncoding=S
	FileGetAttrib=OS
	FileGetShortcut=SOOOOOOO
	FileGetSize=OSS
	FileGetTime=OSS
	FileGetVersion=OS
	FileInstall=SSE
	FileMove=SSE
	FileMoveDir=SSS
	FileRead=OS
	FileReadLine=OSE
	FileRecycle=S
	FileRecycleEmpty=S
	FileRemoveDir=SE
	FileSelectFile=OSSSS
	FileSelectFolder=OSES
	FileSetAttrib=SSEE
	FileSetTime=ESSEE
	FormatTime=OSS
	GetKeyState=OSS
	GroupActivate=SS
	GroupAdd=SSSSSS
	GroupClose=SS
	GroupDeactivate=SS
	Gui=SSSS
	GuiControl=SSS
	GuiControlGet=OSSS
	Hotkey=SSS
	IfEqual=IS
	IfExist=S
	IfGreater=IS
	IfGreaterOrEqual=IS
	IfInString=IS
	IfLess=IS
	IfLessOrEqual=IS
	IfMsgBox=S
	IfNotEqual=IS
	IfNotExist=S
	IfNotInString=IS
	IfWinActive=SSSS
	IfWinExist=SSSS
	IfWinNotActive=SSSS
	IfWinNotExist=SSSS
	ImageSearch=OOEEEES
	IniDelete=SSS
	IniRead=OSSSS
	IniWrite=SSSS
	Input=OSSS
	InputBox=OSSSEEEESES
	KeyHistory=
	KeyWait=SS
	ListHotkeys=
	ListLines=S
	ListVars=
	Menu=SSSSSS
	MouseClick=SEEEESS
	MouseClickDrag=SEEEEES
	MouseGetPos=OOOOE
	MouseMove=EEES
	MsgBox=SSSS
	OnExit=SS
	OutputDebug=S
	Pause=SS
	PixelGetColor=OEES
	PixelSearch=OOEEEEEES
	PostMessage=EEESSSSS
	Process=SSS
	Progress=SSSSSS
	Random=OEE
	RegDelete=SSS
	RegRead=OSSS
	RegWrite=SSSSS
	Reload=
	Run=SSSO
	RunAs=SSS
	RunWait=SSSO
	Send=S
	SendEvent=S
	SendInput=S
	SendLevel=E
	SendMessage=EEESSSSSE
	SendMode=S
	SendPlay=S
	SendRaw=S
	SetBatchLines=S
	SetCapsLockState=S
	SetControlDelay=E
	SetDefaultMouseSpeed=E
	SetEnv=OS
	SetFormat=SS
	SetKeyDelay=EES
	SetMouseDelay=ES
	SetNumLockState=S
	SetRegView=S
	SetScrollLockState=S
	SetStoreCapsLockMode=S
	SetTimer=SSE
	SetTitleMatchMode=S
	SetWinDelay=E
	SetWorkingDir=S
	Shutdown=E
	Sleep=E
	Sort=IS
	SoundBeep=EE
	SoundGet=OSSE
	SoundGetWaveVolume=OE
	SoundPlay=SS
	SoundSet=ESSE
	SoundSetWaveVolume=EE
	SplashImage=SSSSSSS
	SplashTextOff=
	SplashTextOn=EESS
	SplitPath=IOOOOO
	StatusBarGetText=OESSSS
	StatusBarWait=SEESSESS
	StringCaseSense=S
	StringGetPos=OISSE
	StringLeft=OIE
	StringLen=OI
	StringLower=OIS
	StringMid=OIEES
	StringReplace=OISSS
	StringRight=OIE
	StringSplit=SISSS
	StringTrimLeft=OIE
	StringTrimRight=OIE
	StringUpper=OIS
	Suspend=S
	SysGet=OSSS
	Thread=SEE
	ToolTip=SEEE
	Transform=OSSS
	TrayTip=SSEE
	UrlDownloadToFile=SS
	WinActivate=SSSS
	WinActivateBottom=SSSS
	WinClose=SSESS
	WinGet=OSSSSS
	WinGetActiveStats=OOOOO
	WinGetActiveTitle=O
	WinGetClass=OSSSS
	WinGetPos=OOOOSSSS
	WinGetText=OSSSS
	WinGetTitle=OSSSS
	WinHide=SSSS
	WinKill=SSESS
	WinMaximize=SSSS
	WinMenuSelectItem=SSSSSSSSSSS
	WinMinimize=SSSS
	WinMinimizeAll=
	WinMinimizeAllUndo=
	WinMove=SSSSSSSS
	WinRestore=SSSS
	WinSet=SSSSSS
	WinSetTitle=SSSSS
	WinShow=SSSS
	WinWait=SSESS
	WinWaitActive=SSESS
	WinWaitClose=SSESS
	WinWaitNotActive=SSESS

	[ahk1 functions]
	Abs=S
	ACos=S
	Array=SSSSSSSSSSSSSSSSSSSS
	Asc=S
	ASin=S
	ATan=S
	Ceil=S
	Chr=S
	ComObjActive=S
	ComObjArray=SSSSSSSSS
	ComObjConnect=SS
	ComObjCreate=SS
	ComObject=SSS
	ComObjEnwrap=S
	ComObjError=S
	ComObjFlags=SSS
	ComObjGet=S
	ComObjMissing=
	ComObjParameter=SS
	ComObjQuery=SSS
	ComObjType=SS
	ComObjUnwrap=S
	ComObjValue=S
	Cos=S
	DllCall=SSSSSSSSSSSSSSSSSSSS
	Exception=SSS
	Exp=S
	FileExist=S
	FileOpen=SSS
	Floor=S
	Format=SSSSSSSSSSSSSSSSSSSS
	Func=S
	GetKeyName=S
	GetKeySC=S
	GetKeyState=SS
	GetKeyVK=S
	IL_Add=SSSS
	IL_Create=SSS
	IL_Destroy=S
	InStr=SSSSS
	IsByRef=S
	IsFunc=S
	IsLabel=S
	IsObject=S
	Ln=S
	LoadPicture=SSS
	Log=S
	LTrim=SS
	LV_Add=SSSSSSSSSSSSSSSSSSSS
	LV_Delete=S
	LV_DeleteCol=S
	LV_GetCount=S
	LV_GetNext=SS
	LV_GetText=SSS
	LV_Insert=SSSSSSSSSSSSSSSSSSSS
	LV_InsertCol=SSS
	LV_Modify=SSSSSSSSSSSSSSSSSSSS
	LV_ModifyCol=SSS
	LV_SetImageList=SS
	MenuGetHandle=S
	MenuGetName=S
	Mod=SS
	NumGet=SSS
	NumPut=SSSS
	ObjAddRef=S
	ObjBindMethod=SSSSSSSSSSSSSSSSSSSS
	ObjClone=S
	ObjDelete=SSS
	Object=SSSSSSSSSSSSSSSSSSSS
	ObjGetAddress=SS
	ObjGetCapacity=SS
	ObjHasKey=SS
	ObjInsert=SSSSSSSSSSSSSSSSSSSS
	ObjInsertAt=SSSSSSSSSSSSSSSSSSSS
	ObjLength=S
	ObjMaxIndex=S
	ObjMinIndex=S
	ObjNewEnum=S
	ObjPop=S
	ObjPush=SSSSSSSSSSSSSSSSSSSS
	ObjRawSet=SSS
	ObjRelease=S
	ObjRemove=SSS
	ObjRemoveAt=SSS
	ObjSetCapacity=SSS
	OnClipboardChange=SS
	OnExit=SS
	OnMessage=SSS
	Ord=S
	RegExMatch=SSSS
	RegExReplace=SSSSSS
	RegisterCallback=SSSS
	Round=SS
	RTrim=SS
	SB_SetIcon=SSS
	SB_SetParts=SSSSSSSSSSSSSSSSSSSS
	SB_SetText=SSS
	Sin=S
	Sqrt=S
	StrGet=SSS
	StrLen=S
	StrPut=SSSS
	StrReplace=SSSOS
	StrSplit=SSS
	SubStr=SSS
	Tan=S
	Trim=SS
	TV_Add=SSS
	TV_Delete=S
	TV_Get=SS
	TV_GetChild=S
	TV_GetCount=
	TV_GetNext=SS
	TV_GetParent=S
	TV_GetPrev=S
	TV_GetSelection=
	TV_GetText=SS
	TV_Modify=SSS
	TV_SetImageList=SS
	VarSetCapacity=SSS
	WinActive=SSSS
	WinExist=SSSS

	[ahk2 functions]
	Abs=S
	ACos=S
	Array=SSSSSSSSSSSSSSSSSSSS
	ASin=S
	ATan=S
	BlockInput=S
	Ceil=S
	Chr=S
	Click=SSSSSS
	ClipboardAll=SS
	ClipWait=SS
	ComObjActive=S
	ComObjArray=SSSSSSSSS
	ComObjConnect=SS
	ComObjCreate=SS
	ComObject=SSS
	ComObjError=S
	ComObjFlags=SSS
	ComObjGet=S
	ComObjQuery=SSS
	ComObjType=SS
	ComObjValue=S
	ControlAddItem=SSSSSS
	ControlChoose=SSSSS
	ControlChooseString=SSSSSS
	ControlClick=SSSSSSSS
	ControlDeleteItem=SSSSSS
	ControlEditPaste=SSSSSS
	ControlFindItem=SSSSSS
	ControlFocus=SSSSS
	ControlGetChecked=SSSSS
	ControlGetChoice=SSSSS
	ControlGetCurrentCol=SSSSS
	ControlGetCurrentLine=SSSSS
	ControlGetEnabled=SSSSS
	ControlGetExStyle=SSSSS
	ControlGetFocus=SSSS
	ControlGetHwnd=SSSSS
	ControlGetLine=SSSSSS
	ControlGetLineCount=SSSSS
	ControlGetList=SSSSSS
	ControlGetPos=OOOOSSSSS
	ControlGetSelected=SSSSS
	ControlGetStyle=SSSSS
	ControlGetTab=SSSSS
	ControlGetText=SSSSS
	ControlGetVisible=SSSSS
	ControlHide=SSSSS
	ControlHideDropDown=SSSSS
	ControlMove=SSSSSSSSS
	ControlSend=SSSSSS
	ControlSendRaw=SSSSSS
	ControlSetChecked=SSSSSS
	ControlSetEnabled=SSSSSS
	ControlSetExStyle=SSSSSS
	ControlSetStyle=SSSSSS
	ControlSetTab=SSSSSS
	ControlSetText=SSSSSS
	ControlShow=SSSSS
	ControlShowDropDown=SSSSS
	CoordMode=SS
	Cos=S
	Critical=S
	DateAdd=SSS
	DateDiff=SSS
	DetectHiddenText=S
	DetectHiddenWindows=S
	DirCopy=SSS
	DirCreate=S
	DirDelete=SS
	DirExist=S
	DirMove=SSS
	DirSelect=SSS
	DllCall=SSSSSSSSSSSSSSSSSSSS
	Download=SS
	DriveEject=SS
	DriveGetCapacity=S
	DriveGetFilesystem=S
	DriveGetLabel=S
	DriveGetList=S
	DriveGetSerial=S
	DriveGetSpaceFree=S
	DriveGetStatus=S
	DriveGetStatusCD=S
	DriveGetType=S
	DriveLock=S
	DriveSetLabel=SS
	DriveUnlock=S
	Edit=
	EnvGet=S
	EnvSet=SS
	Exception=SSS
	Exit=S
	ExitApp=S
	Exp=S
	FileAppend=SSS
	FileCopy=SSS
	FileCreateShortcut=SSSSSSSSS
	FileDelete=S
	FileEncoding=S
	FileExist=S
	FileGetAttrib=S
	FileGetShortcut=SOOOOOOO
	FileGetSize=SS
	FileGetTime=SS
	FileGetVersion=S
	FileInstall=SSS
	FileMove=SSS
	FileOpen=SSS
	FileRead=S
	FileRecycle=S
	FileRecycleEmpty=S
	FileSelect=SSSS
	FileSetAttrib=SSS
	FileSetTime=SSSS
	Floor=S
	Format=SSSSSSSSSSSSSSSSSSSS
	FormatTime=SS
	Func=S
	GetKeyName=S
	GetKeySC=S
	GetKeyState=SS
	GetKeyVK=S
	GroupActivate=SS
	GroupAdd=SSSSS
	GroupClose=SS
	GroupDeactivate=SS
	GuiCreate=SS
	GuiCtrlFromHwnd=SSS
	GuiFromHwnd=S
	Hotkey=SSS
	ImageSearch=OOSSSSS
	IniDelete=SSS
	IniRead=SSSS
	IniWrite=SSSS
	Input=SSS
	InputBox=SSSS
	InputEnd=
	InStr=SSSSS
	IsByRef=S
	IsFunc=S
	IsLabel=S
	IsObject=S
	KeyHistory=
	KeyWait=SS
	ListHotkeys=
	ListLines=S
	ListVars=
	Ln=S
	LoadPicture=SSS
	Log=S
	LTrim=SS
	MenuGetHandle=S
	MenuGetName=S
	MenuSelect=SSSSSSSSSSS
	Mod=SS
	MonitorGet=SSSSS
	MonitorGetCount=
	MonitorGetName=S
	MonitorGetPrimary=
	MonitorGetWorkArea=SSSSS
	MouseClick=SSSSSSS
	MouseClickDrag=SSSSSSS
	MouseGetPos=OOOOS
	MouseMove=SSSS
	MsgBox=SSS
	NumGet=SSS
	NumPut=SSSS
	ObjAddRef=S
	ObjBindMethod=SSSSSSSSSSSSSSSSSSSS
	ObjClone=S
	ObjDelete=SSS
	Object=SSSSSSSSSSSSSSSSSSSS
	ObjGetAddress=SS
	ObjGetCapacity=SS
	ObjHasKey=SS
	ObjInsertAt=SSSSSSSSSSSSSSSSSSSS
	ObjLength=S
	ObjMaxIndex=S
	ObjMinIndex=S
	ObjNewEnum=S
	ObjPop=S
	ObjPush=SSSSSSSSSSSSSSSSSSSS
	ObjRawSet=SSS
	ObjRelease=S
	ObjRemoveAt=SSS
	ObjSetCapacity=SSS
	OnClipboardChange=SS
	OnExit=SS
	OnMessage=SSS
	Ord=S
	OutputDebug=S
	Pause=SS
	PixelGetColor=SSS
	PixelSearch=OOSSSSSSS
	PostMessage=SSSSSSSS
	ProcessClose=S
	ProcessExist=S
	ProcessSetPriority=SS
	ProcessWait=SS
	ProcessWaitClose=SS
	Random=SS
	RandomSeed=S
	RegDelete=SS
	RegDeleteKey=S
	RegExMatch=SSSS
	RegExReplace=SSSSSS
	RegisterCallback=SSSS
	RegRead=SS
	RegWrite=SSSS
	Reload=
	Round=SS
	RTrim=SS
	Run=SSSO
	RunAs=SSS
	RunWait=SSSO
	Send=S
	SendEvent=S
	SendInput=S
	SendLevel=S
	SendMessage=SSSSSSSSS
	SendMode=S
	SendPlay=S
	SendRaw=S
	SetCapsLockState=S
	SetControlDelay=S
	SetDefaultMouseSpeed=S
	SetKeyDelay=SSS
	SetMouseDelay=SS
	SetNumLockState=S
	SetRegView=S
	SetScrollLockState=S
	SetStoreCapsLockMode=S
	SetTimer=SSS
	SetTitleMatchMode=S
	SetWinDelay=S
	SetWorkingDir=S
	Shutdown=S
	Sin=S
	Sleep=S
	Sort=SS
	SoundBeep=SS
	SoundGet=SSS
	SoundPlay=SS
	SoundSet=SSSS
	SplitPath=SOOOOO
	Sqrt=S
	StatusBarGetText=SSSSS
	StatusBarWait=SSSSSSSS
	StrGet=SSS
	StringCaseSense=S
	StrLen=S
	StrLower=SS
	StrPut=SSSS
	StrReplace=SSSOS
	StrSplit=SSS
	StrUpper=SS
	SubStr=SSS
	Suspend=S
	SysGet=S
	Tan=S
	Thread=SSS
	ToolTip=SSSS
	TrayTip=SSS
	Trim=SS
	Type=S
	VarSetCapacity=SSS
	WinActivate=SSSS
	WinActivateBottom=SSSS
	WinActive=SSSS
	WinClose=SSSSS
	WinExist=SSSS
	WinGetClass=SSSS
	WinGetControls=SSSS
	WinGetControlsHwnd=SSSS
	WinGetCount=SSSS
	WinGetExStyle=SSSS
	WinGetID=SSSS
	WinGetIDLast=SSSS
	WinGetList=SSSS
	WinGetMinMax=SSSS
	WinGetPID=SSSS
	WinGetPos=OOOOSSSS
	WinGetProcessName=SSSS
	WinGetProcessPath=SSSS
	WinGetStyle=SSSS
	WinGetText=SSSS
	WinGetTitle=SSSS
	WinGetTransColor=SSSS
	WinGetTransparent=SSSS
	WinHide=SSSS
	WinKill=SSSSS
	WinMaximize=SSSS
	WinMinimize=SSSS
	WinMinimizeAll=
	WinMinimizeAllUndo=
	WinMove=SSSSSSSS
	WinMoveBottom=SSSS
	WinMoveTop=SSSS
	WinRedraw=SSSS
	WinRestore=SSSS
	WinSetAlwaysOnTop=SSSSS
	WinSetEnabled=SSSSS
	WinSetExStyle=SSSSS
	WinSetRegion=SSSSS
	WinSetStyle=SSSSS
	WinSetTitle=SSSSS
	WinSetTransColor=SSSSS
	WinSetTransparent=SSSSS
	WinShow=SSSS
	WinWait=SSSSS
	WinWaitActive=SSSSS
	WinWaitClose=SSSSS
	WinWaitNotActive=SSSSS
	)"

	return vText1 "`n" vText2
}

;==================================================

JEE_CvtJoin(vParams, oArray*)
{
	vOutput := ""
	Loop, Parse, vParams
	{
		vTemp := JEE_Cvt(oArray[A_Index], A_LoopField)
		if (vTemp = "")
			continue
		if (JEE_SubStr(vOutput, -1) = Chr(34))
		&& (JEE_SubStr(vTemp, 1, 1) = Chr(34))
			vOutput := SubStr(vOutput, 1, -1) SubStr(vTemp, 2)
		else
			vOutput .= " " vTemp
	}
	return LTrim(vOutput)
}

;==================================================

JEE_ExpTrim(vText)
{
	while (SubStr(vText, 1, 2) = Chr(34) " ")
		vText := Chr(34) SubStr(vText, 3)
	while (JEE_SubStr(vText, -2) = " " Chr(34))
		vText := SubStr(vText, 1, -2) Chr(34)
	return vText
}

;==================================================

; ;===============
; ;e.g.
; vText := JEE_GetSelectedText()
; ;===============

; ;===============
; ;e.g. get selected text simple alternative
; Clipboard := ""
; SendInput, ^c
; ClipWait, 3
; if ErrorLevel
; {
; 	MsgBox, % "error: failed to retrieve clipboard text"
; 	return
; }
; vText := Clipboard
; ;===============

JEE_GetSelectedText(vWait:=3)
{
	hWnd := WinGetID("A")
	vCtlClassNN := ControlGetFocus("ahk_id " hWnd)
	if (RegExReplace(vCtlClassNN, "\d") = "Edit")
		vText := ControlGetSelected(vCtlClassNN, "ahk_id " hWnd)
	else
	{
		ClipSaved := ClipboardAll
		Clipboard := ""
		SendInput("^c")
		ClipWait(vWait)
		if ErrorLevel
		{
			ToolTip("ClipWait failed (" A_ThisHotkey ")")
			Clipboard := ClipSaved
			ClipSaved := ""
			Sleep(1000)
			ToolTip()
			Exit() ;terminate the thread that launched this function
		}
		vText := Clipboard
		Clipboard := ClipSaved
		ClipSaved := ""
	}
	return vText
}

;==================================================

JEE_ObjList(oArray, vSep:=" ", vRecurse:=1)
{
	if !vRecurse
	{
		for vKey, vValue in oArray
			vOutput .= vKey vSep vValue "`r`n"
		return SubStr(vOutput, 1, -2)
	}
	for vKey, vValue in oArray
	{
		;note: vRecurse is used to indicate depth, i.e. how many tabs to use
		if IsObject(vValue)
			vOutput .= JEE_StrRept("`t", vRecurse-1) vKey vSep "OBJECT`r`n" %A_ThisFunc%(vValue, vSep, vRecurse+1) "`r`n"
		else
			vOutput .= JEE_StrRept("`t", vRecurse-1) vKey vSep vValue "`r`n"
	}
	return SubStr(vOutput, 1, -2)
}

;==================================================

JEE_ObjPopBlank(oArray)
{
	Loop, % oArray.Length()
	{
		if (oArray[oArray.Length()] = "")
			oArray.Pop()
		else
			break
	}
}

;==================================================

JEE_ObjShuffleFrom(oArray, vOrd, vSep:="")
{
	oArray2 := []
	Loop, Parse, vOrd, % vSep
		if oArray.HasKey(A_LoopField)
			oArray2[A_Index] := oArray[A_LoopField]
	return oArray2
}

;==================================================

JEE_StrCount(ByRef vText, vNeedle, vCaseSen:=0)
{
	local vSCS, vCount
	if (vNeedle = "")
		return
	vSCS := A_StringCaseSense
	StringCaseSense(vCaseSen ? "On" : "Off")
	StrReplace(vText, vNeedle, "", vCount) ;seemed to be faster than line below
	;StrReplace(vText, vNeedle, vNeedle, vCount)
	StringCaseSense(vSCS)
	return vCount
}

;==================================================

JEE_StrJoin(vSep, oArray*)
{
	vOutput := ""
	;if an ObjCount function was available,
	;this would help with estimating the needed capacity
	VarSetCapacity(vOutput, 1000000*2)
	Loop, % oArray.MaxIndex()-1
		vOutput .= oArray[A_Index] vSep
	vOutput .= oArray[oArray.MaxIndex()]
	return vOutput
}

;==================================================

JEE_StrRept(vText, vNum)
{
	if (vNum <= 0)
		return
	return StrReplace(Format("{:" vNum "}", ""), " ", vText)
	;return StrReplace(Format("{:0" vNum "}", 0), 0, vText)
}

;==================================================

JEE_StrUnused(vNum, oArray*)
{
	vText := ""
	VarSetCapacity(vText, 1000*oArray.Length()*2)
	Loop, % oArray.Length()
		vText .= oArray[A_Index]
	vCount := 0
	Loop, 65535
		if !InStr(vText, Chr(A_Index))
		{
			vOutput .= Chr(A_Index)
			vCount++
			if (vCount = vNum)
				break
		}
	;return StrSplit(vOutput)
	return vOutput
}

;==================================================

;JEE_Compare2Strings
;JEE_StrCompare
;JEE_CompareStringsWinMerge
;JEE_StrCompareWinMerge
;JEE_WinMergeStrCompare
JEE_WinMergeCompareStrings(vText1, vText2, vOpt:="", vPathExe:="", vDirOut:="")
{
	global vPathWinMerge
	static vGetExePathWinMerge := Func("JEE_GetExePathWinMerge")
	;local oFile,vPathTemp1,vPathTemp2
	if (vText1 == vText2)
	{
		JEE_MsgBox("same (case sensitive)")
		return
	}
	;if (vText1 = vText2)
	;	MsgBox, % "same (case insensitive)"
	if !FileExist(vPathExe)
	&& !FileExist(vPathExe := vPathWinMerge)
	&& (!vGetExePathWinMerge || !FileExist(vPathExe := %vGetExePathWinMerge%()))
	{
		JEE_MsgBox("error: WinMerge not found")
		return
	}
	if (vDirOut = "")
		vDirOut := A_ScriptDir "\Temp"
	if !FileExist(vDirOut)
		DirCreate(vDirOut)
	Loop, 2
	{
		vPathTemp%A_Index% := vDirOut "\z wc" A_Index ".txt"
		oFile := FileOpen(vPathTemp%A_Index%, "w")
		oFile.Length := 0 ;empty file
		oFile.Encoding := "UTF-8"
		oFile.Write(Chr(0xFEFF) vText%A_Index%)
		oFile.Close()
	}
	;/s single instance
	;/wl opens the left side as read-only
	;/wr opens the right side as read-only
	if InStr(vOpt, "ro")
		Run(Chr(34) vPathExe Chr(34) " /s /wl /wr " Chr(34) vPathTemp1 Chr(34) " " Chr(34) vPathTemp2 Chr(34))
	else
		Run(Chr(34) vPathExe Chr(34) " /s " Chr(34) vPathTemp1 Chr(34) " " Chr(34) vPathTemp2 Chr(34))
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: AHK v1 to AHK v2 converter

25 Feb 2018, 14:15

problem, cmd,% expr doesn't work, but cmd, % expr does, and also cmd % expr. Also cmd,expr doesn't convert, but cmd expr and cmd, expr does. Note the difference in spaces.

Anyways, initial testing is positive. Very well done, and thanks for sharing :clap:. I look forward to the continuation.

Cheers :salute:.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: AHK v1 to AHK v2 converter

26 Feb 2018, 05:41

- Hello Helgef, thanks very much for testing and for the great feedback. I was hoping for some feedback just to know that someone had got it to work.

INSTALLATION
- Essentially it involves 4 scripts. I will put it up on my website as a zip at some point, for easier installation.

USAGE
- I mainly intended the script to convert the selected text. (Default hotkey: Ctrl+Q.)
- The script can also input/output files.
- There is a secondary hotkey that simply identifies lines that aren't converted. (Default hotkey: Ctrl+Win+W.)
- I had two ideas in mind re. checking conversion, (a) use a tool like WinMerge to check each before/after line individually, (b) maintain a log of unique converted lines, so you don't have to review the same conversion multiple times, and so that if at some future point, a conversion was discovered to be faulty, it will be possible to retrospectively identify and correct those lines. E.g.

Code: Select all

[1]WinGet, hWnd, ID, A
[2]hWnd := WinGetID("A")
You could run an updated converter on each '[1]' line to see if you get the same '[2]' line.

BACKGROUND
- I had heard that some major work had been done re. conversion. However, I had done a lot of work on conversion previously, so I felt that I had a good grasp of most of the issues, such that, in the long run, it would be easier to write my own script, than it would be to modify someone else's.
- Writing the script yourself may sound harder, but the advantage is that you do not have to wait for someone else.
- In theory, other scripts could have been useful to learn from, but the only thing I wanted information on was how to handle continuation sections.
- I wasn't sure how important adding special handling for 'can be an expression' parameters would be, it turns out that it's very beneficial, by supporting them, it means you have to do virtually no retrospective parameter corrections. It does involve having massive lists of parameter types, and it's quite difficult to get a perfect list of which parameters in a command are/aren't 'can be an expression'. Generally speaking for 'can be an expression' parameters, no conversion takes place.

RESTRUCTURING
- Having now converted all of my main scripts and libraries, my feeling was 'I'm never going to look at it again', although a few days later, I've already forgotten all of the hard work that went into it, and I'm thinking of one or two basic but major simplifications for the script.
- I think I might separate the bulk of the script into a 'convert this line' function. That would make it easier to use the code in other scripts.
- Having a separate function would also make it easier to convert something like this:

Code: Select all

;before
IfEqual, var, value, Cmd, arg1, arg2
;after
if (var = "value")
	Cmd(arg1, arg2)
Because that effectively involves two separate line conversions.
- I may also move the code for handling the Control/Drive/Process/Win commands into the same location as the handling for all other commands that have special conversion requirements, it should make the script a lot easier to follow/maintain.
- Also, I've been thinking about some basic first steps re. conversion in reverse i.e. AHK v2 -> AHK v1.
- Now that I've converted all of my scripts to AHK v2, it's a problem to share any of my functions in AHK v1 form on the forum.
- I feel better now after having talked a lot about conversion in the 'conversion logic' thread, having some actual code to show.

TO IMPROVE
- Handle 'Cmd,Arg'. I made some efforts to handle whitespace in a variety of 'if' statements shortly before submitting the script, but I will explore further handling for whitespace in commands.
- I could add handling for 'var ='/'var :=' -> 'var := ""'. I didn't, perhaps because there was a risk of incorrectly converting lines that marked the start of continuation sections.
- There may be some issues with Loop at present, this is because I had 3 modes in mind:

Code: Select all

Loop Parse, vText, `n, `r ;AHK v1 minimal force expression
Loop Parse, vText, % "`n", % "`r" ;AHK v1 everything force expression (almost like AHK v2)
Loop Parse, vText, "`n", "`r" ;AHK v2
Also, my script is optimised for handling function-style parameters, I haven't done too much work on command-style parameters yet, although it would be a key feature of AHK v2 -> AHK v1 conversion.
- Any good code to identify a valid hotkey label would be great if someone can help me with it.
- I recently found out that the SoundSet 'NewSetting' parameter is not a standard 'can be an expression' parameter, I guess that it must have special handling for '+10' etc. Any minor deviations from standard parameter types like this, are something I really need to know about.
- Do post any errors in the script to this thread.

REINVENTING THE WHEEL
- It's funny potentially reinventing everything. I would have used ideas from elsewhere if I'd known them, instead I ended up inventing things as and when I needed them.
- One key function was 'ObjPopBlank', to remove any empty keys from the end of an array.
- Another was 'CvtJoin' to stitch parameters together.

SUBSTR IS FIDDLY (AS ARE INSTR/REGEXMATCH)
- There is not really an easy solution re. what to do with SubStr, e.g. to signify that one SubStr line *is* AHK v2 ready, and that one line isn't. Especially when it may not be possible to completely convert a script automatically, so there is temporary phase where conversion is incomplete.
- For me the most practical solution was this: wherever StartingPos was ambiguous (not a positive number), I changed the function from 'SubStr' to 'JEE_SubStr', and added/subtracted 1 if necessary. JEE_SubStr is a two-way compatible function, with the AHK v2 behaviour in both AHK v1 and AHK v2. E.g. StringRight can be converted to JEE_SubStr.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: AHK v1 to AHK v2 converter

26 Feb 2018, 09:30

jeeswg wrote:

Code: Select all

;before
IfEqual, var, value, Cmd, arg1, arg2
;after
if (var = "value")
	Cmd(arg1, arg2)
Because that effectively involves two separate line conversions.
see here for some caveats i found with same line if statements:

https://github.com/mmikeww/AHK-v2-scrip ... /issues/25

that converter pretty much already does "one line at a time". not sure why you don't want to collaborate and instead build your own, but your choice

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: AHK v1 to AHK v2 converter

26 Feb 2018, 10:03

- Hello guest3456, ah, so they can be chained. I guess a function can check for IfXXX, and then check again etc, as you pointed out.
- You're welcome to use ideas/code from my script for your converter. You probably don't need anything, but you might find it interesting to look at handling for any awkward commands.
- If you have any areas where you would recommend a different approach, or if there are any elements you like, it would be interesting to know. Or any general comments about your experiences writing a converter. The classic point being that things start out as a Rube Goldberg machine, and then get tidied up. Also, when problems recur often enough, they become a function, but then you have to be careful retrofitting old code to use the function.
- I try to make my scripts quite readable, so hopefully things are reasonably easy to follow. It can be quite entertaining reading someone else's attempts at solving the problems.
- I mention about 'conversion obstacles' here, i.e. impediments to both one-way conversion, and two-way compatible conversion.
Wish List 2.0 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 13&t=36789

- Re. collaboration. Conversion is a fundamental thing in programming, and it benefits anyone to become good at it, so, even if I could get someone else to do it for me, it's a useful skill to have. I wanted to understand the process from start to finish.
- For me, I wanted to do some things in a specific way, I would have spent more time debating than coding. At other times I've played second fiddle.
- For me, the best form of collaboration is the forum itself, people can help fill in the random gaps in my knowledge, and vice versa.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
Location: Middle of the round cube
Contact:

Re: AHK v1 to AHK v2 converter

27 Mar 2018, 08:08

Hi, I just tried this out a bit. It was complicated to get the script and the 3 lib files together from 2 forum threads (and not the first post in the threads) to the right places. Would be relatively easy for you and much easier for everyone if you simply provided a zip file.. So it is attached here to give you the idea...
converter.zip
(36.63 KiB) Downloaded 252 times
The converter seems to fail to convert commands in line with a hotkey like this:

Code: Select all

^Numpad0::Run, taskmgr ; Run Task Manager
Maybe you don't have any hotkeys like that, so I thought I'd let you know...
try it and see
...
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: AHK v1 to AHK v2 converter

27 Mar 2018, 12:09

- Thanks derz00. Someone messaged me suggesting to make the files more easily available, and I had intended to do this anyway, and had hoped to do it last weekend. (It might seem simple but I wanted to: use correct modified dates, do some checks, possibly do a readme, and figure out sensible naming conventions for the zip and its files, and make sure all files were UTF-8.)
- You are right re. hotkey one-liners. I had said above: 'Any good code to identify a valid hotkey label would be great if someone can help me with it.' I mentioned it here a while back:
parsing AutoHotkey scripts - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=40471
- I need to be sure that I correctly identify where hotkey labels begin/end in order to do this, perhaps other people that have worked on parsers or source code could point me to some info on this. Otherwise I could try to identify at least a wide range of common hotkeys as a start.
- [EDIT:] I have some AHK v1 code which covers most of these hotkey one-liners:

Code: Select all

	JEE_CvtSplitLine(vTemp, vWhitespace, vCode, vComments)
	if InStr(vCode, "::")
	&& !(vCode ~= "::.*::")
	&& RegExMatch(vCode, "O)^([^:]*)::(.*)$", o)
		vWhitespace .= o.1 "::", vCode := o.2
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
mora145
Posts: 57
Joined: 25 Jun 2022, 15:31

Re: AHK v1 to AHK v2 converter

26 Jun 2022, 23:04

Thank you for your code. I had been looking for something similar for a long time. I am still new.

One question: Does it only work for AutoHotKey version 1? Is there any plan for v2?
AHK_user
Posts: 515
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: AHK v1 to AHK v2 converter

27 Jun 2022, 03:49

mora145 wrote:
26 Jun 2022, 23:04
Thank you for your code. I had been looking for something similar for a long time. I am still new.

One question: Does it only work for AutoHotKey version 1? Is there any plan for v2?
There is a similar project in V2:
viewtopic.php?f=6&t=25100&hilit=convertor

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 35 guests