AU3toPB

Everything else that doesn't fall into one of the other PB categories.
AZJIO
Addict
Addict
Posts: 1315
Joined: Sun May 14, 2017 1:48 am

AU3toPB

Post by AZJIO »

Converting AutoIt3 script to PureBasic code.

I often had to rewrite the simple AutoIt3 code into PureBasic code and it was time consuming if done manually, so I decided to automate. In the beginning, I made regular expressions and didn't save them as it was a temporary task. But when this happened several times, I decided to write it in the form of a converter. Of course, this is not a full-fledged converter, and even most of the code must be done manually, but at least eliminate routine work.

Code: Select all

EnableExplicit

Global Error_Procedure = 0
Global Text.string

Define Text$, File$, Format, CountP

Structure ReplaceGr
  pos.i
  ngr.i
  group.s
EndStructure

; https://www.purebasic.fr/english/viewtopic.php?p=575871
Procedure RegexReplace2(RgEx, *Result.string, Replace0$, Escaped = 0)
	Protected i, CountGr, Pos, Offset = 1
	Protected Replace$
	Protected NewList item.s()
	Protected LenT, *Point
; 	Static RE2
; 	Static RE3
	Protected RE2
	Protected NewList ReplaceGr.ReplaceGr()

	CountGr = CountRegularExpressionGroups(RgEx)
	; ограничение групп, только обратные ссылки \1 .. \9
	If CountGr > 9
		CountGr = 9
	EndIf

	If ExamineRegularExpression(RgEx, *Result\s)

		; Поиск Esc-символов в поле замены регвыр
		If Escaped
			Replace0$ = ReplaceString(Replace0$, "\r", #CR$)
			Replace0$ = ReplaceString(Replace0$, "\n", #LF$)
			Replace0$ = ReplaceString(Replace0$, "\t", #TAB$)
			Replace0$ = ReplaceString(Replace0$, "\f", #FF$)
		EndIf

		; Поиск ссылок на группы в поле замены регвыр
		RE2 = CreateRegularExpression(#PB_Any, "\\\d")
		If RE2
			If ExamineRegularExpression(RE2, Replace0$)
				While NextRegularExpressionMatch(RE2)
					If AddElement(ReplaceGr())
						ReplaceGr()\pos = RegularExpressionMatchPosition(RE2) ; позиция
						ReplaceGr()\ngr = ValD(Right(RegularExpressionMatchString(RE2), 1)) ; номер группы
						ReplaceGr()\group = RegularExpressionMatchString(RE2) ; текст группы
					EndIf
				Wend
			EndIf
			FreeRegularExpression(RE2) ; убрать строку при Static
		EndIf
		If Not ListSize(ReplaceGr())
			*Result\s = ReplaceRegularExpression(RgEx, *Result\s, Replace0$)
			ProcedureReturn
		EndIf
; 		Сортировка по позиции, чтобы делать замены с конца и не нарушались ранее найденные позиции
		SortStructuredList(ReplaceGr(), #PB_Sort_Descending, OffsetOf(ReplaceGr\pos), TypeOf(ReplaceGr\pos))

		While NextRegularExpressionMatch(RgEx)
			Pos = RegularExpressionMatchPosition(RgEx)
			Replace$ = Replace0$

			ForEach ReplaceGr()
				If ReplaceGr()\ngr
					Replace$ = ReplaceString(Replace$, ReplaceGr()\group, RegularExpressionGroup(RgEx, ReplaceGr()\ngr), #PB_String_CaseSensitive, ReplaceGr()\pos, 1)
				Else
					Replace$ = ReplaceString(Replace$, ReplaceGr()\group, RegularExpressionMatchString(RgEx), #PB_String_CaseSensitive, ReplaceGr()\pos, 1) ; обратная ссылка \0
				EndIf
			Next
			; item() = часть строки между началом и первым совпадением или между двумя совпадениями + результат подстановки групп

			If AddElement(item())
				item() = Mid(*Result\s, Offset, Pos - Offset) + Replace$
			EndIf
			Offset = Pos + RegularExpressionMatchLength(RgEx)
		Wend
		If AddElement(item())
			item() = Mid(*Result\s, Offset)
		EndIf

		; Формирования текстового списка
		; Debug "Count = " + Str(ListSize(item()))
; 		Count = ListSize(item())
		LenT = 0
		ForEach item()
			LenT + Len(item()) ; вычисляем длину данных для вмещения частей текста
		Next

		*Result\s = Space(LenT) ; создаём строку забивая её пробелами
		*Point = @*Result\s    ; Получаем адрес строки
		ForEach item()
			CopyMemoryString(item(), @*Point) ; копируем очередной путь в указатель
		Next
		; Конец => Формирования текстового списка

		FreeList(item()) ; удаляем список, хотя в функции наверно это не требуется
	EndIf
EndProcedure

; FlagsRE
; 16 - поддержка групп - флиг исключён
; 32 - поддержка \n\r\t\f
Procedure RegExpReplace(*s.string, regexp$, replace$, FlagsRE = 0)
	Protected Escaped, nRE
	Error_Procedure = 0
; 	If Not Asc(*s\s)
	If *s\s = ""
		Error_Procedure = 1
		ProcedureReturn
	EndIf

	nRE = CreateRegularExpression(#PB_Any, regexp$, FlagsRE)
	If nRE
		Escaped = FlagsRE & 32 ; флаг метасимволов \n\r\t\f
; 		If FlagsRE & 16 Or Escaped
			RegexReplace2(nRE, *s, replace$, Escaped)
; 		Else
; 			*s\s = ReplaceRegularExpression(nRE, *s\s, replace$)
; 		EndIf
		FreeRegularExpression(nRE)
	Else
		Error_Procedure = 2
		*s\s = RegularExpressionError()
	EndIf
EndProcedure


; Открытие файла из ком строки или диалога
CountP = CountProgramParameters()
If CountP
	File$ = ProgramParameter(0)
Else
	File$ = OpenFileRequester("", GetCurrentDirectory(), "AutoIt3 (*.au3)|*.au3", 0)
EndIf

If Not (Asc(File$) And FileSize(File$) > 0)
	End
EndIf

; чтение файла
#File = 0
If ReadFile(#File, File$)
	Format = ReadStringFormat(#File)
	Text\s = ReadString(#File, Format | #PB_File_IgnoreEOL)
; 	Text\s = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
	CloseFile(#File)
EndIf

If Not Asc(Text\s)
	End
EndIf

RegExpReplace(@Text, "(\$)(\w+)", "\2\1") ; переменные
RegExpReplace(@Text, "(?mi)^\h*Func\h*(?=\w+\h*\()", "Procedure ") ; процедуры
RegExpReplace(@Text, "(?mi)^\h*EndFunc", "EndProcedure") ; 
RegExpReplace(@Text, "\bReturn\b", "ProcedureReturn") ; 
RegExpReplace(@Text, "\bLocal\b", "Protected") ; 
RegExpReplace(@Text, "\bContinueLoop\b", "Continue") ; ContinueCase(?)
RegExpReplace(@Text, "\bExitLoop\b", "Break") ; 
RegExpReplace(@Text, "\bEnum\b", "Enumeration") ; 
RegExpReplace(@Text, "\bExit\b", "End") ; 
RegExpReplace(@Text, "\[(\d+)\]", "(\1)") ; элементы массива
ReplaceString(Text\s, "&", "+", #PB_String_InPlace) ; конкатенакция строк
ReplaceString(Text\s, "'", Chr(34), #PB_String_InPlace) ; апостроф на кавычки
RegExpReplace(@Text, "([+/*-])=", "\1") ; арифметической действие без приравнивания
RegExpReplace(@Text, "\bi\$", "i") ; счётчик в циклах всегда число
RegExpReplace(@Text, "(?mi)(^\h*)Do\b", "\1Repeat") ; циклы
RegExpReplace(@Text, "(?mi)(^\h*If .+?) Then\r?$", "\1") ; условие в несколько строк, убирается "Then"
RegExpReplace(@Text, "(?mi)^(\h*)(If .+?) Then ([^\r\n;]+?)\r?$", "\1\2\r\n\t\1\3\r\n\1EndIf", 32) ; условие в одну строку преобразуется в многострочное
RegExpReplace(@Text, "(?mi)^\h*Global\h+Const\h+(\w+?)\$\h*=\h*0x([\dA-F]+)\r?$", "#\1 = $\2") ; константы 16-ричные
RegExpReplace(@Text, "(?mi)^\h*Global\h+Const\h+(\w+?)\$\h*=\h*([\dA-F]+)\r?$", "#\1 = \2")	   ; константы 10-ричные
RegExpReplace(@Text, "(?mi)^(\h*)Switch\b", "\1Select")
RegExpReplace(@Text, "(?mi)^(\h*)EndSwitch\b", "\1EndSelect")
RegExpReplace(@Text, "(?mi)^(\h*)Case Else\b", "\1Default")
RegExpReplace(@Text, "(?mi)\bTrue\b", "#True")
RegExpReplace(@Text, "(?mi)\bFalse\b", "#False")
RegExpReplace(@Text, "\b([A-Z][A-Z_\d]*?)\$(?=[^\w])", "#\1") ; неявное правило - переменные в верхнем регистре являются константами.

; если переменная приравнивается к числу то удаляем $ в имени переменной,
; но проблема, что надо все переменые в заданном пространстве переименовывать
; так что это ненадёжно
; RegExpReplace(@Text, "(\w+)\$(?=\h*=\h*-?\d)", "\1")

Text\s = ReplaceString(Text\s, "@error", "g_Error_Procedure") ; глобальная. В начале функции задаётся в 0. Флаг ошибки.
Text\s = ReplaceString(Text\s, "@extended", "g_Extended_Procedure") ; глобальная. В начале функции задаётся в 0. Флаг дополнительного значения.
Text\s = ReplaceString(Text\s, "#include-once", "") ; инклуды 1 раз включает
Text\s = ReplaceString(Text\s, "#include ", "XIncludeFile ") ; инклуды

; Функции
RegExpReplace(@Text, "\bMsgBox\h*\(([^;,]+?),\h*([^;,]+?),\h*([^;,]+?)\h*\)", "MessageRequester(\2, \3, \1)")
RegExpReplace(@Text, "\bInputBox(?=\h*\()", "InputRequester")
RegExpReplace(@Text, "\bSleep(?=\h*\()", "Delay")
RegExpReplace(@Text, "\bStringInStr(?=\h*\()", "FindString")
RegExpReplace(@Text, "\bStringUpper(?=\h*\()", "UCase")
RegExpReplace(@Text, "\bStringLower(?=\h*\()", "LCase")
RegExpReplace(@Text, "\bStringLen(?=\h*\()", "Len")
RegExpReplace(@Text, "\bStringMid(?=\h*\()", "Mid")
RegExpReplace(@Text, "\bNumber(?=\h*\()", "Val")
RegExpReplace(@Text, "\bString(?=\h*\()", "Str")
RegExpReplace(@Text, "\b(Run|ShellExecute)(?=\h*\()", "RunProgram")
RegExpReplace(@Text, "\bFileCopy(?=\h*\()", "CopyFile")
RegExpReplace(@Text, "\bFileDelete(?=\h*\()", "DeleteFile")
RegExpReplace(@Text, "\bFileExists(?=\h*\()", "FileSize")
RegExpReplace(@Text, "\bFileGetSize(?=\h*\()", "FileSize")
RegExpReplace(@Text, "\bFileSetAttrib(?=\h*\()", "SetFileAttributes")
RegExpReplace(@Text, "\bFileGetAttrib(?=\h*\()", "GetFileAttributes")
RegExpReplace(@Text, "\bDirCopy(?=\h*\()", "CopyDirectory")
RegExpReplace(@Text, "\bDirCreate(?=\h*\()", "CreateDirectory")
RegExpReplace(@Text, "\bDirRemove(?=\h*\()", "DeleteDirectory")
RegExpReplace(@Text, "\bFileSetTime(?=\h*\()", "SetFileDate")
RegExpReplace(@Text, "\bFileChangeDir(?=\h*\()", "SetCurrentDirectory")
RegExpReplace(@Text, "\bStringReplace(?=\h*\()", "ReplaceString")
RegExpReplace(@Text, "\bFileMove(?=\h*\()", "RenameFile")
RegExpReplace(@Text, "\bStringReverse(?=\h*\()", "ReverseString")
RegExpReplace(@Text, "\bTimerInit(?=\h*\()", "ElapsedMilliseconds")
RegExpReplace(@Text, "\bClipGet(?=\h*\()", "GetClipboardText")
RegExpReplace(@Text, "\bClipPut(?=\h*\()", "SetClipboardText")
RegExpReplace(@Text, "\bMemGetStats(?=\h*\()", "MemoryStatus")
; макросы
RegExpReplace(@Text, "@WorkingDir", "GetCurrentDirectory()")
RegExpReplace(@Text, "@TempDir", "GetTemporaryDirectory()")
RegExpReplace(@Text, "@DesktopWidth", "DesktopWidth(0)")
RegExpReplace(@Text, "@DesktopHeight", "DesktopHeight(0)")
RegExpReplace(@Text, "@ScriptDir", "GetPathPart(ProgramFilename())")
RegExpReplace(@Text, "@TAB", "#TAB$")
RegExpReplace(@Text, "@CRLF", "#CRLF$")
RegExpReplace(@Text, "@LF", "#LF$")
RegExpReplace(@Text, "@CR", "#CR$")
RegExpReplace(@Text, "@OSVersion", "OSVersion()")
RegExpReplace(@Text, "@UserName", "UserName()")
; RegExpReplace(@Text, "1111", "2222") ; 
; RegExpReplace(@Text, "1111", "2222") ; 


; Начало DllCall
Procedure Split(String.s, Array StringArray.s(1), Separator.s = " ")
  
  Protected S.String, *S.Integer = @S
  Protected.i asize, i, p, slen
  asize = CountString(String, Separator)
  slen = Len(Separator)
  ReDim StringArray(asize)
  
  *S\i = @String
  While i < asize
    p = FindString(S\s, Separator)
    StringArray(i) = PeekS(*S\i, p - 1)
    *S\i + (p + slen - 1) << #PB_Compiler_Unicode
    i + 1
  Wend
  StringArray(i) = S\s
  *S\i = 0
  
EndProcedure

Procedure.s ParseParam(string$)
	Protected Dim Param.s(1)
	Protected res$, i
	Split(string$, Param(), ",")
	For i = 1 To ArraySize(Param()) Step 2
		res$ + Param(i) + ","
	Next
	res$ = LTrim(res$)
	ProcedureReturn RTrim(res$, ",")
EndProcedure

Procedure FindDllCall(*s.string)
	Protected nRE, Groups, FuncName$, Param$
	nRE = CreateRegularExpression(#PB_Any, ~"(?i)DllCall\\h*\\([^,]+?,[^,]+?,\\h*\"(\\w+?)\"\\h*,(.+?)\\)", 0)
	If nRE
		If ExamineRegularExpression(nRE, *s\s)
			While NextRegularExpressionMatch(nRE)
				FuncName$ = RegularExpressionGroup(nRE, 1) + "_(" + ParseParam(RegularExpressionGroup(nRE, 2)) + ")"
; 				Debug FuncName$
				*s\s = ReplaceString(*s\s, RegularExpressionMatchString(nRE), FuncName$)
			Wend
		EndIf
		FreeRegularExpression(nRE)
	EndIf
EndProcedure

FindDllCall(@Text)
; Конец DllCall



Text\s = "EnableExplicit" + #CRLF$ + #CRLF$ + Text\s

#File = 0
If CreateFile(#File, File$ + ".pb")
	WriteStringFormat(#File, #PB_UTF8)
	WriteString(#File, Text\s, #PB_UTF8)
	CloseFile(#File)
EndIf
Last edited by AZJIO on Tue Sep 27, 2022 2:50 pm, edited 2 times in total.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: AU3toPB

Post by ChrisR »

Hi AZJIO,
Just in case you haven't seen it, there is a great work done by Ward to help port Autoit programs to PureBasic:
PureAutoIt - Complete Autoit Functions In Purebasic
AZJIO
Addict
Addict
Posts: 1315
Joined: Sun May 14, 2017 1:48 am

Re: AU3toPB

Post by AZJIO »

Hi ChrisR

Yes, I know, I sometimes try to cut something out of there. But many things are not optimized, for example StringSplit uses StringField. It is much better to use Split by wilbert.
I cut out part of the code from the "Send" function from there, it's too long there. Some things don't exist at all (ControlTreeView)
I have 50 of my programs, many of them are hard to redo. And there is also an archive with a script of 2000 pieces. There are ready-made libraries for working with the registry. A lot of things I would like to convert to PureBasic.

I added DllCall parsing to the above code
Here is another person collecting functions
Post Reply