I'm making an analog of this tool.
Code: Select all
;- TOP
; AZJIO 2024
; DisableDebugger
EnableExplicit
;- ● #Constants
Enumeration
#SYNTAX_Text
#SYNTAX_Keyword
#SYNTAX_Comment
#SYNTAX_Constant
#SYNTAX_String
#SYNTAX_Function
#SYNTAX_Asm
#SYNTAX_Operator
#SYNTAX_Structure
#SYNTAX_Number
#SYNTAX_Pointer
#SYNTAX_Separator
#SYNTAX_Label
#SYNTAX_Module
EndEnumeration
; #SYNTAX_Text = 0
; #SYNTAX_Keyword = 1
; #SYNTAX_Comment = 2
; #SYNTAX_Constant = 3
; #SYNTAX_String = 4
; #SYNTAX_Function = 5
; #SYNTAX_Asm = 6
; #SYNTAX_Operator = 7
; #SYNTAX_Structure = 8
; #SYNTAX_Number = 9
; #SYNTAX_Pointer = 10
; #SYNTAX_Separator = 11
; #SYNTAX_Label = 12
; #SYNTAX_Module = 13
#Dll = 0
#Input = 0
#Output = 1
;- ● Declare
Declare.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
Declare CommentHandler(i)
Declare RemovingSpaceBeforeCRLF(i)
Structure Token
s.s
type.i
len.i
EndStructure
;- ● Global
Global n
Global ArrSz = 1000
Global Dim Token.Token(ArrSz)
Global flgOpn, InputFile$, OutputFile$, *Buffer, Length, NotSuccess, g_Format
Define i, j, z, tmp, IsFind
Define StartTime
Procedure Callback(*Position, Length, Type)
Protected tmp$, tmp2$, *c.Ascii
If g_Format = #PB_Ascii
Token(n)\s = PeekS(*Position, Length, #PB_Ascii)
ElseIf g_Format = #PB_UTF8
Token(n)\s = PeekS(*Position, Length, #PB_UTF8 | #PB_ByteLength)
EndIf
Token(n)\type = Type
Token(n)\len = Length
n + 1
If n > ArrSz
ArrSz * 2
ReDim Token.Token(ArrSz) ; динамически увелиивает размер массива, чтобы вмещать последующие элементы
EndIf
EndProcedure
;- ● обработка
If OpenLibrary(#Dll, #PB_Compiler_Home + "SDK\Syntax Highlighting\SyntaxHighlighting.dll")
; Добавляем параметоры ком строки чтобы использовать как инструмент
If CountProgramParameters()
InputFile$ = ProgramParameter(0)
If Not (Asc(InputFile$) And FileSize(InputFile$) > 3 And Left(GetExtensionPart(InputFile$), 2) = "pb")
InputFile$ = ""
EndIf
EndIf
If Not Asc(InputFile$)
InputFile$ = OpenFileRequester("Select PB File", "", "*.pb*|*.pb*|All Files|*.*", 0)
EndIf
If Asc(InputFile$)
If MessageRequester("Перезаписать?", "Перезаписать текущий файл? (иначе в буфер обмена и файл с суффиксом _tidy)",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
OutputFile$ = InputFile$
OutputFile$ = GetPathPart(InputFile$) + GetFilePart(InputFile$, #PB_FileSystem_NoExtension) + "_tidy." + GetExtensionPart(InputFile$)
Else
flgOpn = 1
OutputFile$ = GetPathPart(InputFile$) + GetFilePart(InputFile$, #PB_FileSystem_NoExtension) + "_tidy." + GetExtensionPart(InputFile$)
EndIf
If ReadFile(#Input, InputFile$) And CreateFile(#Output, OutputFile$)
g_Format = ReadStringFormat(#Input)
WriteStringFormat(#Output, g_Format)
Length = Lof(#Input) ; в байтах
*Buffer = AllocateMemory(Length)
If *Buffer
ReadData(#Input, *Buffer, Length)
CallFunction(#Dll, "SyntaxHighlight", *Buffer, Length, @Callback(), 0)
FreeMemory(*Buffer)
ArrSz = n - 1
ReDim Token.Token(ArrSz) ; обрезаем до текущего размера
EndIf
CloseFile(#Input)
StartTime = ElapsedMilliseconds()
; Debug ArrSz
; Начинаем обработку
; Так как идёт модернизация пробелов, то это не влияет на код
; удаляется пробел только вокруг запятых и скобкок, которые являются разделителями и не могут создать присоединение чисел и слов
For i = 0 To ArrSz
; Debug Token(i)\len
If Token(i)\len > 2 ; Linux > 1
tmp = Asc(Right(Token(i)\s , 1))
If tmp = #CR Or tmp = #LF
RemovingSpaceBeforeCRLF(i)
EndIf
EndIf
; If Token(i)\len > 2 And FindString(Token(i)\s, #LF$)
; RemovingSpaceBeforeCRLF(i)
; EndIf
Select Token(i)\type
Case #SYNTAX_Separator ; (),
; Debug "|" + Token()\s + "|"
Select Token(i)\s
Case "("
If i + 1 > ArrSz
Continue
EndIf
If Asc(Token(i + 1)\s) = ' '
Token(i + 1)\s = LTrimChar(Token(i + 1)\s, " " + #TAB$)
EndIf
If i - 1 < 0
Continue
EndIf
If Token(i - 1)\type = #SYNTAX_Function And Right(Token(i - 1)\s , 1) = " " ; если предыдущий токен функция то убираем пробелы
Token(i - 1)\s = RTrim(Token(i - 1)\s)
EndIf
Case ")"
If i - 1 < 0
Continue
EndIf
If Right(Token(i - 1)\s , 1) = " "
Token(i - 1)\s = RTrim(Token(i - 1)\s)
EndIf
Case ","
If i + 1 > ArrSz
Continue
EndIf
tmp = Asc(Token(i + 1)\s)
If Not (tmp = ' ' Or tmp = #CR Or tmp = #LF)
Token(i + 1)\s = " " + Token(i + 1)\s
EndIf
If i - 1 < 0
Continue
EndIf
If Right(Token(i - 1)\s , 1) = " "
Token(i - 1)\s = RTrim(Token(i - 1)\s)
EndIf
EndSelect
Case #SYNTAX_Operator ; +-/*=
Select Token(i)\s
Case "=", "<", ">" ; , "<=", ">=", "<<", ">>", "<>" ; комбинированные операторы... с проверкой операторов вперёд и назад
If i + 2 > ArrSz
Continue
EndIf
tmp = Asc(Token(i + 1)\s)
If (Asc(Token(i + 1)\s) = 0 And Token(i + 2)\type <> 7) Or Token(i + 1)\type <> 9 Or (Token(i + 1)\type = 9 And tmp <> ' ' And tmp <> 0 And tmp <> #TAB)
; tmp = Asc(Token(i + 1)\s)
If Not (tmp = ' ' Or tmp = #CR Or tmp = #LF)
Token(i + 1)\s = " " + Token(i + 1)\s
EndIf
EndIf
If i - 2 < 0
Continue
EndIf
tmp = Asc(Token(i - 1)\s)
If (Asc(Token(i - 1)\s) = 0 And Token(i - 2)\type <> 7) Or Token(i - 1)\type <> 9 Or (Token(i + 1)\type = 9 And tmp <> ' ' And tmp <> 0 And tmp <> #TAB)
If Right(Token(i - 1)\s , 1) <> " "
Token(i - 1)\s + " "
EndIf
EndIf
Case "+", "|", "!", "%", "~", "&" ; не комбинированные операторы. Строки ASM являются цельными с "!"
If i + 1 > ArrSz
Continue
EndIf
tmp = Asc(Token(i + 1)\s)
If Not (tmp = ' ' Or tmp = #CR Or tmp = #LF)
Token(i + 1)\s = " " + Token(i + 1)\s
EndIf
If i - 1 < 0
Continue
EndIf
If Right(Token(i - 1)\s , 1) <> " "
Token(i - 1)\s + " "
EndIf
Case "-"
z = 1
For j = 1 To 5
If Token(i - j)\type <> 9 ; #SYNTAX_Number и он же переносы строк и пробелы
z = j
Break
EndIf
Next
; IsFind = FindString(Token(i - z)\s , "=") ; если приравнивание или сравнение
; If Not IsFind
If i - z < 0 ; защита
Continue
EndIf
If Not (FindString(Token(i - z)\s , "=") Or FindString(Token(i - z)\s , "<") Or FindString(Token(i - z)\s , ">") Or FindString(Token(i - z)\s , ","))
If Right(Token(i - 1)\s , 1) <> " "
Token(i - 1)\s + " "
EndIf
If i + 1 > ArrSz
Continue
EndIf
tmp = Asc(Token(i + 1)\s)
If Not (tmp = ' ' Or tmp = #CR Or tmp = #LF)
Token(i + 1)\s = " " + Token(i + 1)\s
EndIf
Else
If i - 1 < 0
Continue
EndIf
If Right(Token(i - 1)\s , 1) <> " "
Token(i - 1)\s + " "
EndIf
EndIf
Case "*"
If i + 1 > ArrSz ; защита
Continue
EndIf
tmp = Asc(Token(i + 1)\s)
If tmp >= '0' And tmp <= '9'
Token(i + 1)\s = " " + Token(i + 1)\s
EndIf
If i - 1 < 0 ; защита
Continue
EndIf
tmp = Asc(Right(Token(i - 1)\s , 1))
If tmp <> ' ' And tmp <> '('
Token(i - 1)\s + " "
EndIf
EndSelect
Case #SYNTAX_Keyword ; if
If Token(i)\len > 11 And FindString(Token(i)\s, "EndProcedure")
; задаём между функциями разрыв в две пустые строки, если он меньше. Бывает лепят вплотную.
tmp = CountString(Token(i)\s, #LF$)
For j = 1 To 5
If Token(i + j)\type = 9 ; #SYNTAX_Number и он же переносы строк, но так как после функции не будет число то это работает
tmp + CountString(Token(i + j)\s, #LF$)
Else
Break
EndIf
Next
If tmp < 3
tmp = 3 - tmp
For j = 1 To tmp
Token(i)\s + #CRLF$
Next
EndIf
EndIf
;- ██ текущий ██
Case #SYNTAX_Comment
CommentHandler(i)
; Case #SYNTAX_Number ; пробелы на самом деле
; If Token(i)\len > 2 ; на Linux больше одного > 1
; tmp = Asc(Right(Token(i)\s , 1))
; If tmp = #CR Or tmp = #LF
; RemovingSpaceBeforeCRLF(i)
; EndIf
; EndIf
; tmp = Asc(Token(i)\s)
; If (tmp = #CR Or tmp = #LF) And i - 1 >= 0
; RemovingSpaceBeforeEnd(i - 1)
; ElseIf Token(i)\len > 3 And FindString(Token(i)\s, #LF$)
; RemovingSpaceBeforeCRLF(i)
; EndIf
EndSelect
Next
; EnableDebugger
Debug "Прошло времени " + Str(ElapsedMilliseconds() - StartTime) + " мсек"
; DisableDebugger
; Запись
For i = 0 To ArrSz
WriteString(#Output, Token(i)\s, g_Format)
; пишем номер (тип) рядом с токеном, чтоб видеть как размещаются пробелы
; WriteString(#Output, Token(i)\s + "|" + Str(Token(i)\type), g_Format)
Next
CloseFile(#Output)
EndIf
EndIf
CloseLibrary(#Dll)
If flgOpn
RunProgram(OutputFile$)
Else
If DeleteFile(InputFile$)
If Not RenameFile(OutputFile$, InputFile$)
NotSuccess = 1
EndIf
EndIf
If NotSuccess
MessageRequester("", "Не удалось обновить файл")
EndIf
EndIf
Else
MessageRequester("", "Failed to open SyntaxHighlighting.dll")
End
EndIf
Procedure RemovingSpaceBeforeCRLF(i)
Protected *c.Character, flgSpace, *c0.Character, required
*c = @Token(i)\s
While *c\c
Select *c\c
Case ' ', #TAB, 160, $2002, $2003, $2004, $2005, $2006, $2009, $200A
If flgSpace = 0
*c0 = *c
EndIf
flgSpace + 1
Case #CR, #LF
If flgSpace
required = 1
Break
EndIf
Default
flgSpace = 0
EndSelect
*c + SizeOf(Character)
Wend
If required
While *c\c
*c0\c = *c\c
*c0 + SizeOf(Character)
*c + SizeOf(Character)
Wend
; If *c\c = 0
*c0\c = 0
; EndIf
EndIf
EndProcedure
Procedure CommentHandler(i)
Protected tmp, *c.Character, Text$, flgSpace, *c0.Character, tmp$
; tmp = CountString(Token(i - 2)\s, #LF$) ; проверяем если в предыдущей строке перенос
; If tmp And Asc(Token(i)\s) = ';' ; перенос начался с начала строки
If i < 2 ; защита
ProcedureReturn
EndIf
If Asc(Token(i - 1)\s) = 0 And CountString(Token(i - 2)\s, #LF$) And Asc(Token(i)\s) = ';' ; перенос начался с начала строки
*c = @Token(i)\s
*c0 = *c
; *c\c = ' '
*c + SizeOf(Character)
flgSpace + 1
While *c\c
; Debug Chr(*c\c)
If *c\c = ' ' Or *c\c = #TAB
flgSpace + 1
Else
; как только пошёл текст, то переставляеместавляем
If flgSpace = < 2
Break ; ничего не делаем, комментарий начался нормально "; "
EndIf
*c0\c = #TAB
*c0 + SizeOf(Character)
*c0\c = #TAB
*c - SizeOf(Character)
*c\c = ' '
*c - SizeOf(Character)
*c\c = ';'
Break
EndIf
*c + SizeOf(Character)
Wend
Else
*c = @Token(i)\s
; *c0 = *c
*c + SizeOf(Character)
While *c\c
Select *c\c
Case ' '
If flgSpace = 0
flgSpace + 1
ElseIf flgSpace = 1
*c0 = *c
flgSpace = 1
ElseIf flgSpace = -1
*c0\c = *c\c
EndIf
Case #TAB
If flgSpace = 0
flgSpace + 1
*c\c = ' '
ElseIf flgSpace = 1
*c0 = *c
flgSpace = 2
ElseIf flgSpace = -1
*c0\c = *c\c
EndIf
Default
If flgSpace = 0 And *c\c = '-'
tmp$ = LTrimChar(Text$, #TAB$ + " ")
If Asc(tmp$) = ';'
Text$ = tmp$
Break
EndIf
EndIf
If flgSpace = 0 Or flgSpace = 1
Break ; ничего не делаем, комментарий начался нормально
EndIf
flgSpace = -1
*c0\c = *c\c
EndSelect
If flgSpace = -1
*c0 + SizeOf(Character)
EndIf
*c + SizeOf(Character)
Wend
If flgSpace = -1
*c0\c = 0 ; если перезаписывали комментарий сдвигая к началу, то надо завершить его нулём
EndIf
EndIf
EndProcedure
; https://www.purebasic.fr/english/viewtopic.php?t=79183
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
Protected *jc0, *c.Character, *jc.Character
If Not Asc(String$)
ProcedureReturn ""
EndIf
*c = @String$
*jc0 = @TrimChar$
While *c\c
*jc = *jc0
While *jc\c
If *c\c = *jc\c
*c\c = 0
Break
EndIf
*jc + SizeOf(Character)
Wend
If *c\c
String$ = PeekS(*c)
Break
EndIf
*c + SizeOf(Character)
Wend
ProcedureReturn String$
EndProcedure