#include <Array.au3>

Opt("MustDeclareVars", 1)

Demo_1()

Func Demo_1()
  Local $sFilePath

;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei PassIt2.au3"
;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei CallTipViewer.au3"
;   $sFilePath = "C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.au3"
;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei File to Base64 String Code Generator.au3"
;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei GUIScrollbars_Ex.au3"
;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei GuiAVI.au3"
  $sFilePath = @ScriptDir & "\_test file.au3"
;   $sFilePath = @ScriptDir & "\Test files\_Test-Datei Minimal.au3"

  Local $hTimer = TimerInit()

  Local $iRounds = 10
  For $i = 1 To $iRounds
    Local $aFuncList = ReadFunctionHeads($sFilePath)
  Next

  If @error Then
    MsgBox(0, "Error", @error & " = file not opened in read mode or other error")
  Else
    ConsoleWrite(@CRLF & '! Average execution time: ' _
      & TimerDiff($hTimer) / $iRounds & ' ms. Rounds: ' & $iRounds & @CRLF & @CRLF)

    Local $sDemoHint = _
      "        ---- Die Spitzklammern dienen nur zur Verdeutlichung," _
      & " ob Leerzeichen vor oder hinter der Syntax-Zeile stehen. ----        "
      ; The angle brackets only serve to clarify whether spaces are before or after the syntax line.

    _ArrayDisplay($aFuncList, "File:  " & $sFilePath, "", 0, Default, $sDemoHint)
  EndIf
EndFunc


; ==============================================================================
; ReadFunctionHeads
;
; Author:         Professor Bernd.
; Date:           2020-07-04.
; Syntax:         ReadFunctionHeads(Const $_sFilePath)
; Parameters:     $_sFilePath - the file path as a string
; Return values:  Success - an array with function names and parameter lists
;                 Failure - @error
; Description:
;
;   Routine to read function names and their parameter list from the function
;   declarations of a file.
;
;   All valid function headers of a file are read. The function names and
;   the associated parameter list are filtered and displayed in the usual
;   AutoIt help notation.
;
;   - - - -
;
;   Routine zum Auslesen von Funktions-Namen und deren Parameter-Liste aus den
;   Funktions-Deklarationen einer Datei.
;
;   Ausgelesen werden alle gültigen Funktions-Köpfe einer Datei. Daraus werden
;   die Funktions-Namen mit zugehöriger Parameter-Liste gefiltert und in einer
;   aus der AutoIt Hilfe gewohnten Schreibweise dargestellt.
; ------------------------------------------------------------------------------
Func ReadFunctionHeads(Const $_sFilePath)

  ; Datei komplett einlesen. (Schnellere Methode von AspirinJunkie.)
  ; Read in the complete file. (Faster method of AspirinJunkie.)
  ; Datei auf jeden Fall mit @CRLF abschließen, damit die folgenden Erkennungen
  ;  auch in der letzten Zeile funktionieren.
  ; In any case, close the file with @CRLF so that the following recognitions
  ;  also work in the last line.
  Local $sAllLines = _FileReadFast($_sFilePath) & @CRLF

  If $sAllLines = @CRLF Then Return SetError(1, 0, '')

  ; Entferne #Region- und #EndRegion-Direktiven.
  ; Remove #Region and #EndRegion directives.
  ; Author: DXRW4E, 2013-02-11.
  ; https://www.autoitscript.com/forum/topic/148213-parse-all-includes-in-a-script-file/?do=findComment&comment=1052998
  ; Modified by Professor Bernd, 2020-07-06.
;   $sAllLines = StringRegExpReplace($sAllLines, '(?i)\R\h*(?:#Region|#EndRegion)(?!\w)[^\R]*\R', @CRLF)
  ; Modified by Professor Bernd, 2020-07-10.
;   $sAllLines = StringRegExpReplace($sAllLines, '(?mi)^\h*(?:#Region|#EndRegion)(?!\w)[^\R]*?\R', "")
  ; Erklärung, warum die RegExp von DXRW4E schneller sein könnte.
  ; https://www.autoitscript.com/forum/topic/167309-how-to-remove-from-a-string-all-between-and-pairs/?do=findComment&comment=1224207
  ; oder (Author: Professor Bernd, 2020-07-09.)
  ; - Wahrscheinlich sicherste Möglichkeit, um auch "#Region _" abzufangen*. 2020-07-10.
  $sAllLines = StringRegExpReplace($sAllLines, '(?mi)^\h*(?:#Region|#EndRegion).*\R', "")
  ;
  ; * Beispiel was von den anderen Pattern NICHT gefunden/entfernt wird:
  ; #Region Hier wird gefaltet _
  ; #cs
  ;   ; Kommentar
  ; #ce
  ; func Test_1($_sMyString)
  ;   ; some code
  ;   MsgBox(0, "Hello", $_sMyString)
  ; endfunc
  ; #EndRegion

  ; Entferne einzeilige Kommentare.
  ; Strip single line comments.
  ; Author: mikell, 2013-12-13.
  ; https://www.autoitscript.com/forum/topic/157255-regular-expression-challenge-for-stripping-single-comments/?do=findComment&comment=1138772
  $sAllLines = StringRegExpReplace($sAllLines, '(?m)^((?:[^''";]*([''"]).*?\g2)*[^;]*);?.*$', '$1')

  ; Entferne einfache und verschachtelte Block-Kommentare.
  ; Remove simple and nested block comments (#cs or #comment-start & #ce or #comment-end).
  ; Author: AspirinJunkie, 2020-06-29.
  ; https://autoit.de/thread/87036-verschachtelte-block-kommentare-entfernen-mit-regexp/?postID=700789#post700789
  $sAllLines = StringRegExpReplace($sAllLines, '(?xmi) (^\h*\# (?>cs|comments-start)\b (?sU:(?>(?R)|.)*) ^\h*\#(?>ce|comments-end)\b.*\R )', '')

  ; Entferne Zeilenfortsetzungszeichen " _", nachfolgenden Leerraum und den Zeilenumbruch.
  ; Remove line continuation characters " _", following whitespace and the newline sequence.
  $sAllLines = StringRegExpReplace($sAllLines, '\h+_\h*\R+?', ' ')

;     ; Ersetzen aller "' & '" Kombinationen, die durch das Entfernen von " _"
;     ;  entstanden sind, wenn Strings über 2 Zeilen fortgesetzt wurden.
;     ;  Leider scheint das etwas Zeit zu kosten, für wenig Nutzen. 2020-07-03.
;     ; Replacing all "' & '" combinations that were created by removing " _"
;     ;  when strings were continued over 2 lines. Unfortunately this seems
;     ;  to take some time, for small benefit. 2020-07-03.
;     $sAllLines = StringRegExpReplace($sAllLines, '"\h*&\h*''(.*)''', '\1"')
;     $sAllLines = StringRegExpReplace($sAllLines, "'\h*&\h*""(.*)""", "\1'")

  ; Entferne alle Zeilen, die nicht mit "Func " oder "EndFunc" beginnen.
  ; Remove all lines that do not begin with "Func " or "EndFunc".
  ; Based on: "Delete all lines that do not have the contents of $sSearch4 present."
  ; Author: Malkey, 2017-11-30.
  ; https://www.autoitscript.com/forum/topic/191371-autoit-delete-all-lines-from-a-txt-file-except-some/?do=findComment&comment=1372776
  ; Modified by Professor Bernd, 2020-06-28, 2020-07-03.
  $sAllLines = StringRegExpReplace($sAllLines, _
    '(?mi)^\h*Func\h+.*\R?(*SKIP)(*F)|^\h*EndFunc.*\R?(*SKIP)(*F)|^.*\R?', "")

  ; Zeilen so formatieren, dass die Syntax verdeutlicht wird.
  ; --------------------------------------------------------------------------

  ; Leerzeichen vor und hinter folgenden Zeichen einfügen (aber nur wenn sie nicht in
  ; Anführungzeichen stehen): Runde Klammern, Gleich-Zeichen, AutoIt Operatoren.
  ; Insert spaces before and after the following characters (but only if they are not in
  ; quotation marks): Parentheses, equal signs, AutoIt operators.
  ; Author: AspirinJunkie, 2020-07-01.
  ; https://autoit.de/thread/87036-regexp-verschachtelte-block-kommentare-entfernen-char-nicht-in-string-ersetzen/?postID=700817#post700817
; 	$sAllLines = StringRegExpReplace($sAllLines, _
;     '(?x)( (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \h* (?: <> | [&\+\-\*\/=><]? = | [\(\)\*+\-\/&^<>?:] | \b(?i:And|Or|Not)\b) \h*)', ' \1 ')
  ; Without "And, Or, Not".
	$sAllLines = StringRegExpReplace($sAllLines, _
    '(?x)( (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \h* (?: <> | [&\+\-\*\/=><]? = | [\(\)\*+\-\/&^<>?:] ) \h*)', ' \1 ')

  ; Formatiere "And", "Or" und "Not" (aber nur wenn sie nicht in Anführungzeichen stehen).
  ; Format "And", "Or" and "Not" (but only if they are not in quotation marks).
  ; Author: AspirinJunkie, 2020-07-01.
  ; https://autoit.de/thread/87036-regexp-verschachtelte-block-kommentare-entfernen-char-nicht-in-string-ersetzen/?postID=700826#post700826
  ; Modified by Professor Bernd, 2020-07-02.
  $sAllLines = StringTrimRight(StringRegExpReplace($sAllLines & ':and=And:or=Or:not=Not', _
    '(?sxi)(?: (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \b(and|or|not)\b ) (?=.*:\1=(\w+)\b)', '\2'), 22)
;   ; Gleiche Anweisung, andere Schreibweise (Single und Double-Quotes in 2 Strings getrennt, statt jedes Zeichen zu escapen.
;   ; Same instruction, different spelling (single and double quotes separated into 2 strings instead of escaping each character.
;   $sAllLines = StringTrimRight(StringRegExpReplace($sAllLines & ':and=And:or=Or:not=Not', _
;     '(?sxi)(?: (?>"(?> [^"]+ | "" )*" |' & " '(?> [^']+ | '' )*') (*SKIP)(*FAIL) | \b(and|or|not)\b ) (?=.*:\1=(\w+)\b)", '\2'), 22)
;
;   ; ToDo: Testen ob die escapte Variante schneller ist.

  ; Formatiere "Const" und "ByRef" (aber nur wenn sie nicht in Anführungzeichen stehen).
  ; Format "Const" and "ByRef" (but only if they are not in quotation marks).
  ; Modified with pattern from AspirinJunkie by Professor Bernd, 2020-06-30.
  ; Nur Treffer außerhalb von Quotes ersetzen.
	$sAllLines = StringRegExpReplace($sAllLines, _
    '(?xi)( (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \bconst\b )', ' Const ')
	$sAllLines = StringRegExpReplace($sAllLines, _
    '(?xi)( (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \bbyref\b )', ' ByRef ')

  ; Formatiere Kommas folgendermaßen (aber nur wenn sie nicht in Anführungzeichen stehen):
  ;  Vor dem Komma alle Leerraum entfernen, hinter dem Komma ein Leerzeichen einfügen.
  ; Format commas as follows (but only if they are not in quotation marks):
  ;  Remove all whitespace before the comma, insert a space after the comma.
  $sAllLines = StringRegExpReplace($sAllLines, _
    '(?x)( (?>"(?> [^"]+ | "" )*" | ''(?> [^'']+ | '''' )*'') (*SKIP)(*FAIL) | \h*, )', ', ')

  ; Mehrfachen Leerraum durch ein einzelnes Leerzeichen ersetzen.
  ; (Dabei werden auch Tabs durch Leerzeichen ersetzt, aber keine Zeilenumbrüche.)
  ; Replace multiple whitespace with a single space.
  $sAllLines = StringRegExpReplace($sAllLines, "(\h+)", ' ')
  ;                                                                          |
  ; --------------------------------------------------------------------------

  ; Zeilen mit gültigem Funktions-Kopf in ein Array speichern, wenn die nächste
  ; Zeile mit "EndFunc" beginnt. Dabei String "Func " entfernen.
  ; Hinweis; Das funktioniert NUR, weil es an dieser Stelle nur noch Zeilen gibt,
  ;          die mit "Func " oder "EndFunc" beginnen! 2020-06-28, 2020-07-03.
  ; Store lines with a valid function header in an array if the next line
  ; starts with "EndFunc". Thereby remove string "Func ".
  ; Hint: This works ONLY because at this point there are only lines beginning
  ;       with "Func " or "EndFunc"! 2020-06-28, 2020-07-03.
;   ; Based on code from Bitnugger.
;   ; Modified and "EndFunc" part added by Professor Bernd, 2020-07-03. - (?i) bedeuted case insensitive (Tipp von alpines).
; ;   Local $aFuncs = StringRegExp($sAllLines, _
; ;     '(?i)Func (\w+\h*\(.*\)\h*\R)\h*EndFunc.*\R', $STR_REGEXPARRAYGLOBALMATCH) ; Zeile inkl. @CRLF
;   Local $aFuncs = StringRegExp($sAllLines, _
;     '(?i)Func (\w+\h*\(.*\)\h*)\R\h*EndFunc.*\R', $STR_REGEXPARRAYGLOBALMATCH) ; Zeile ohne @CRLF
  ;
  ; Folgendes Pattern hat die gleiche Funktionalität wie das Pattern oben, plus
  ; zusätzliche Prüfung auf ausbalancierte Klammern (Klammern in Quotes werden
  ; ignoriert). Dadurch werden weitere ungültige Funktions-Köpfe ausgefiltert.
  ; Author: AspirinJunkie, 2020-07-22.
  ; https://autoit.de/thread/87058-regexp-nicht-finden-wenn-nach-suchbegriff-noch-was-kommt/?postID=701034#post701034
  Local $sPattern = _
    '(?mi)^\h*Func\h+((?(DEFINE)(?<brackets>\((?|''(?>[^'']+|'''')*''|"(?>[^"]+|"")*"|[^\(\)\r\n"'']*|(?&brackets))*\)))\w+\h*(?&brackets)\h*(?=\R\h*EndFunc))'

  Local $aFuncs = StringRegExp($sAllLines, $sPattern, $STR_REGEXPARRAYGLOBALMATCH)

      ; Alle einfachen Anführungszeichen durch doppelte Anführungszeichen ersetzen.
      ; => NICHT gut bei z. B. '"' !
      ; Replace all single quotation marks with double quotation marks.
      ; => NOT good with e.g. '"' !
      ;$sAllLines = StringReplace($sAllLines, "'", '"', 0, $STR_NOCASESENSEBASIC)

  ; Array mit den gefundenen Funktions-Köpfen durchlaufen.
  ;  Go through the array with the found function heads.
  ; Die Spitzklammern dienen nur der Verdeutlichung, ob Leerzeichen vor oder hinter der Syntax-Zeile stehen.
  ;  The angle brackets only serve to clarify whether spaces are before or after the syntax line.
  ; Hier kann später der Code stehen, um die Funktionsköpfe einem Dictionary hinzuzufügen. 2020-06-23.
  For $i = 0 To UBound($aFuncs) - 1 Step 1
;     $aFuncs[$i] = '>' & $aFuncs[$i] & '<'
    $aFuncs[$i] = '>' & StringReplace($aFuncs[$i], @CRLF, " @CRLF ") & '<' ; Nur Debug, zum Anzeigen von @CRLF.
  Next

  Return SetError(@error, @extended, @error ? @error : $aFuncs)
EndFunc ; ==> ReadFunctionHeads


; #FUNCTION#============================================================================================================
; Name...........: _FileReadFast
; Description ...: Read text or binary files into a variable. Faster than FileRead at big file sizes
; Syntax.........: _FileReadFast(Const $s_FilePath, [[Const $flag_Encoding = Default], [Const $flag_FileScan = 2]])
; Parameters ....: $s_FilePath - the file path as a string
;                  $flag_Encoding: Default = the file encoding is determined by the function
;                                  0 = file gets handled as a binary file -> return a binary-variable
;                                  other = possible values are the same es the flag parameter of BinaryToString (faster)
;                  $flag_FileScan: The mode-parameter of FileGetEncoding $flag_Encoding = Default
; Return values .: Success - the file content as a string or a binary-variable
;                  Failure - a Null-String and error gets set to:
;                      @error = 1: File doesn't exist
;                             = 2: Couldn't open file for reading
;                             = 3: Couldn't determine file size
;                             = 4: error while reading file
;                      @extended = WinAPI error code
; Author ........: AspirinJunkie
  ; https://autoit.de/thread/84134-csv-datei-zeilen-z%C3%A4hlen-extrem-schnell-gnuwin32/?postID=673171#post673171
; ===============================================================================================================================
Func _FileReadFast(Const $s_FilePath, Const $flag_Encoding = Default, Const $flag_FileScan = 2)

	If Not FileExists($s_FilePath) Then Return SetError(1, 0, "")
	Local $h_DLL_KERNEL32 = DllOpen("kernel32.dll")
	Local $a_Ret
	$a_Ret = DllCall($h_DLL_KERNEL32, "handle", "CreateFileW", "wstr", $s_FilePath, "dword", 0x80000000, "dword", 1, "struct*", Null, "dword", 3, "dword", 0, "ptr", Null)
	If DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0] Then Return SetError(2, DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0], "")
	Local $h_File = $a_Ret[0]
	$a_Ret = DllCall($h_DLL_KERNEL32, "bool", "GetFileSizeEx", "handle", $h_File, "int64*", 0)
	If DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0] Then Return SetError(3, DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0], "")
	Local $d_Bytes = $a_Ret[2]
	Local $t_Buffer = DllStructCreate("byte[" & $d_Bytes & "]")
	DllCall($h_DLL_KERNEL32, 'bool', 'ReadFile', 'handle', $h_File, 'struct*', $t_Buffer, 'dword', $d_Bytes, 'dword*', 0, 'ptr', 0)
	If DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0] Then Return SetError(4, DllCall($h_DLL_KERNEL32, "dword", "GetLastError")[0], "")
	DllCall($h_DLL_KERNEL32, "bool", "CloseHandle", "handle", $h_File)
	DllClose($h_DLL_KERNEL32)

	Switch $flag_Encoding
		Case Default; determine file encoding
			Switch FileGetEncoding($s_FilePath, $flag_FileScan)
				Case 32, 1024 ; UTF-16 LE
					Return BinaryToString(DllStructGetData($t_Buffer, 1), 2)
				Case 64, 2048 ; UTF-16 BE
					Return BinaryToString(DllStructGetData($t_Buffer, 1), 3)
				Case 128, 256 ; UTF-8
					Return BinaryToString(DllStructGetData($t_Buffer, 1), 4)
				Case 512 ; ANSI
					Return BinaryToString(DllStructGetData($t_Buffer, 1), 1)
				Case Else ; handle as binary
					Return DllStructGetData($t_Buffer, 1)
			EndSwitch
		Case 0 ; binary file
			Return DllStructGetData($t_Buffer, 1)
		Case Else ; encoding set by user
			Return BinaryToString(DllStructGetData($t_Buffer, 1), $flag_Encoding)
	EndSwitch
EndFunc   ;==>_FileReadFast


