Несколько раз приходилось используя код AutoIt3 переписывать его в PureBasic. Начинал с простейших замен, потом понял что непродуктивно ручками и сделал несколько регулярных выражений. Каждый раз писал рег.выр. заново, так как не сохранял и добавил их в свою программу RegExpPB. Теперь пошёл ещё дальше, решил написать скрипт, чтобы все они применились за раз. Очередной раз решил преобразовать скрипт экспорта реестра в код PureBasic и подумал почему бы параллельно не дописывать каждую замену с список замен, заодно и для будущих преобразований.

Ну и вот что на данный момент:

Код:
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

Конечно, некоторые коды не преобразовать, надо делать инклуд и импортировать в него функции с одинаковым функционалом, так как прямой замены нет. Аналогично в списке есть несколько функций, которые просто подменяют имена, но требуется доработка передаваемых параметров, но уже не требуется искать аналог, а прыгнуть в справку и посмотреть как переставить параметры или проверить соответствие формату.

Для тех кто знает AutoIt3 это упрощает много монотонной работы.

Отредактировано AZJIO (27.09.2022 16:50:28)