Несколько раз приходилось используя код 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)