;-- TIME_STAMP 2020-05-23 22:49:57 v 0.1 ; ============================================================ ; ; ; ; Inspired by the Nim command line parser ; ; https://nim-lang.github.io/Nim/parseopt.html#initOptParser ; ; ; ; ============================================================ ; #cs CMD-LINE RULES Basics: • Parameters will delimited by one space. (key=val key2=val key3=val) • Values with spaces inside has to be encapsulated with string delimiters. (key="val with space") • For the value assignment, you can use "=" or ":". (key=val | key:val) • Only for assignment with ":" its allowed to have one optional space before the value. (key:val | key: val) Types: CMD_SHORTOPTION • Starts with "-" (-k=val) • Name (the key) has a length of only 1 character!! If a longer key will used, it will ignored with one exception: If the passed value is a string with space(s) inside, the part behind the 1st space will recognized as CMD_ARGUMENT, which are summarized by the following contiguous non-space characters. • [SPECIAL CASE] "-key" A short option parameter without assignment can be longer than one character. CMD_LONGOPTION • Starts with "--" (--key=val) • Identifier has a length of minimum 1 character. • [SPECIAL CASE] "--" alone The "--" option, commonly used to denote that every token that follows is an argument, is interpreted as a long option, and its name is the empty string. CMD_ARGUMENT • All other non-space charcters (arg | /abc) Options: Parameters can declared as "ParamNoVal". This means that only a key is expected. Since this is a parameter with no value, it is always a Boolean parameter (passed or not). If a value is passed anyway, it is ignored. To declare the parameter as "ParamNoVal", you initialize the parser with a list of this paramter(s). It's one list for ShortNoVal and another one for LongNoVal. FUNCTIONS: _CmdLineParser_Init() Initializes the parser. Reads the command line and creates a separate structure for each parameter. _CmdLineParser_Next() Returnes the next parameter structure from stack. _CmdLineParser_GetParamByKey() Gets a parameter structure by its key _CmdLineParser_GetParamByIndex() Gets a parameter structure by its index (0-based) #ce ; CmdLineKind: Global Const $CMDLINEPARSE_END = 0xDEADBEEF Global Const $CMDLINEPARSE_ARGUMENT = 1 Global Const $CMDLINEPARSE_LONGOPTION = 2 Global Const $CMDLINEPARSE_SHORTOPTION = 3 Global $_ga_tCmdLineParam[1] Global $_gt_OptParser ; #FUNCTION# ==================================================================================================================== ; Name ..........: _CmdLineParser_Init ; Description ...: Initializes the parser. Reads the command line and creates a separate structure for each parameter. ; ...............: Parameter structure: ; ...............: .index (0-based index of the parameter) ; ...............: .pos (start position of the parameter in the command line) ; ...............: .kind ($CMDLINEPARSE_ARGUMENT | $CMDLINEPARSE_LONGOPTION | $CMDLINEPARSE_SHORTOPTION) ; ...............: .param (the full param, i.e.: --file="my file path") ; ...............: .key (the key, i.e.: file) ; ...............: .val (the value, i.e.: "my file path") ; Parameters ....: $_sShortNoVal [optional] Comma separated list with short parameters, which have no values, i.e.: "f,c". (Default = "") ; ...............: $_sLongNoVal [optional] Comma separated list with long parameters, which have no values, i.e.: "corner,angle". (Default = "") ; Return values .: The Parser structure: ; ...............: .cmdline The full command line ; ...............: .cmdcount The count of parameters in command line ; Author ........: BugFix ; =============================================================================================================================== Func _CmdLineParser_Init($_sShortNoVal="", $_sLongNoVal="") Local $cmdLine = $CmdLineRaw If $cmdLine = '' Then Return SetError(1,0,0) Local $aTmp, $aCmds, $iCmdCount Local $aListShortNoVal = ObjCreate("System.Collections.ArrayList") Local $aListLongNoVal = ObjCreate("System.Collections.ArrayList") If $_sShortNoVal <> '' Then $aTmp = StringSplit($_sShortNoVal, ',') For $i = 1 To $aTmp[0] $aListShortNoVal.Add($aTmp[$i]) Next EndIf If $_sLongNoVal <> '' Then $aTmp = StringSplit($_sLongNoVal, ',') For $i = 1 To $aTmp[0] $aListLongNoVal.Add($aTmp[$i]) Next EndIf $aCmds = __GetParamArray($cmdLine, $aListShortNoVal, $aListLongNoVal) ; [[sParam(--key=val), iKind, iPos, Key, Val]] $iCmdCount = UBound($aCmds) ReDim $_ga_tCmdLineParam[$iCmdCount] ; create parameter Dll-Struct Item For $i = 0 To $iCmdCount -1 ; $Index, $_pos, $_kind, $_key, $_val, $_param $_ga_tCmdLineParam[$i] = __tParam_Create($i, $aCmds[$i][2], $aCmds[$i][1], $aCmds[$i][3], $aCmds[$i][4], $aCmds[$i][0]) Next ; create parser Dll-Struct $_gt_OptParser = __tOptParser_Create($cmdLine, $iCmdCount) Return $_gt_OptParser EndFunc ;==>_CmdLineParser_Init ; #FUNCTION# ==================================================================================================================== ; Name ..........: _CmdLineParser_Next ; Description ...: Returnes the next parameter structure from stack. ; Return values .: Success Structure of next parameter ; ...............: Failure $CMDLINEPARSE_END set @error = 1, end of stack is reached. ; Author ........: BugFix ; Remarks .......: If $CMDLINEPARSE_END was returned, with next call of the function it starts again with the first parameter. ; =============================================================================================================================== Func _CmdLineParser_Next() Local Static $bIter = False Local Static $Iter If Not $bIter Then $Iter = __IteratorNum(0, UBound($_ga_tCmdLineParam) -1, 1) $bIter = True EndIf Local $iCurrentCmd = $Iter() If $iCurrentCmd = Null Then $bIter = False Return SetError(1, 0, $CMDLINEPARSE_END) Else Return $_ga_tCmdLineParam[$iCurrentCmd] EndIf EndFunc ;==>_CmdLineParser_Next ; #FUNCTION# ==================================================================================================================== ; Name ..........: _CmdLineParser_GetParamByKey ; Description ...: Gets a parameter structure by its key ; Parameters ....: $_sKey The searched key ; Return values .: Success The parameter structure ; ...............: Failure 0 set @error = 1, key not found ; Author ........: BugFix ; =============================================================================================================================== Func _CmdLineParser_GetParamByKey($_sKey) Local $t, $iCount = $_gt_OptParser.cmdcount If $iCount > 0 Then For $i = 1 To $iCount $t = $_ga_tCmdLineParam[$i-1] If $t.key = $_sKey Then Return $t Next EndIf Return SetError(1,0,0) EndFunc ;==>_CmdLineParser_FindOpt ; #FUNCTION# ==================================================================================================================== ; Name ..........: _CmdLineParser_GetParamByIndex ; Description ...: Gets a parameter structure by its index (0-based) ; Parameters ....: $_iIndex The index of the searched parameter ; Return values .: Success The parameter structure ; ...............: Failure 0 set @error = 1, Index out of range ; Author ........: BugFix ; =============================================================================================================================== Func _CmdLineParser_GetParamByIndex($_iIndex) If $_iIndex < 0 Or $_iIndex > $_gt_OptParser.cmdcount -1 Then Return SetError(1,0,0) Else Return $_ga_tCmdLineParam[$_iIndex] EndIf EndFunc ;==>_CmdLineParser_FindOpt #Region - helper functions ; creates the parameter structure Func __tParam_Create($_index, $_pos, $_kind, $_key, $_val, $_param) Local $iLenK = StringLen($_key) +1, $iLenV = StringLen($_val) +1, $iLenP = StringLen($_param) +1 Local $tag = "struct;int pos;int index;int kind;wchar key[" & $iLenK & "];wchar val[" & $iLenV & "];wchar param[" & $iLenP & "];endstruct;" Local $t = DllStructCreate($tag) $t.index = $_index $t.pos = $_pos $t.kind = $_kind If $_key <> '' Then $t.key = $_key If $_val <> '' Then $t.val = $_val $t.param = $_param Return $t EndFunc ;==>__tParam_Create ; creates the parser structure Func __tOptParser_Create($_cmdLine, $_countParam) Local $tag = "struct;" & _ "wchar cmdline[" & StringLen($CmdLineRaw) +1 & "];" & _ "int cmdcount;" & _ "endstruct;" Local $t = DllStructCreate($tag) $t.cmdline = $_cmdLine $t.cmdcount = $_countParam Return $t EndFunc ;==>__tOptParser_Create ; selects all parameter from command line Func __GetParamArray($_sLine, $_aListShortNoVal, $_aListLongNoVal) Local $sPatternParam = _ '(?x)(?(DEFINE)' & _ ' (? "(?> [^"]+ | "" )*"' & _ ; double quoted string ' |' & _ " '(?> [^']+ | '' )*" & _ ; single quoted string"' ' )' & _ ' (? (?<= ^|\s)' & _ ; start of the line or after a space ' )' & _ ' (? (?= \s|$)' & _ ; before a space or line end ' )' & _ ' (? (?&StartOrSpace) -- (?= \s)' & _ ; all following params are read as argument ' )' & _ ' (? (? (?&StartOrSpace) -- [^-\s:=]+' & _ ; long param w.o. value ' )' & _ ' (? (? (?&LongParamNoVal) (: \s? | =) ((?&String) | \S+ )' & _ ; long param w. value ' )' & _ ' (? (?&StartOrSpace) [^-]\S* (?&SpaceOrEnd)' & _ ; all other none-space ' )' & _ ' (? (?&ShortParam) | (?&LongParam) | (?&ShortParamNoVal) |' & _ ' (?&LongParamNoVal)(?&SpaceOrEnd) | (?&Argument) | (?&ArgumentsOnlyMarker)' & _ ' )' & _ ')' & _ '(?&AllParam)' Local $aRes = StringRegExp($_sLine, $sPatternParam, 3) Local $aParam[UBound($aRes)][5] ; [[sParam, iKind, iPos, Key, Val]] Local $bArgsFollows = False, $aMatch Local $iPosSum = 0 For $i = 0 To UBound($aParam) -1 $aParam[$i][0] = $aRes[$i] $aParam[$i][1] = __GetParamType($aRes[$i]) $aParam[$i][2] = $iPosSum +1 $iPosSum += StringLen($aParam[$i][0]) +1 ; +1 following space Switch $aParam[$i][1] Case $CMDLINEPARSE_ARGUMENT $aParam[$i][3] = $aParam[$i][0] Case $CMDLINEPARSE_LONGOPTION If $bArgsFollows Then $aParam[$i][1] = $CMDLINEPARSE_ARGUMENT $aParam[$i][3] = $aParam[$i][0] Else If $aParam[$i][0] = '--' Then $bArgsFollows = True Else $aMatch = StringRegExp($aParam[$i][0], '--(\w+)(?:=|:\s?)?(.*)', 3) If IsArray($aMatch) Then $aParam[$i][3] = $aMatch[0] If Not $_aListLongNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1] EndIf EndIf EndIf Case $CMDLINEPARSE_SHORTOPTION If $bArgsFollows Then $aParam[$i][1] = $CMDLINEPARSE_ARGUMENT $aParam[$i][3] = $aParam[$i][0] Else $aMatch = StringRegExp($aParam[$i][0], '^-(?:(\w+$|\w)(?:=|:\s?)?(.*))', 3) If IsArray($aMatch) Then $aParam[$i][3] = $aMatch[0] If Not $_aListShortNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1] EndIf EndIf EndSwitch ;== DEBUG == ;~ ConsoleWrite('> Param full: ' & $aParam[$i][0] & @TAB & ' Type(1-arg/2-long/3-short): ' & $aParam[$i][1] & @CRLF) ;~ ConsoleWrite('- Pos: ' & $aParam[$i][2] & @TAB & ' Key: ' & $aParam[$i][3] & @TAB & ' Val: ' & $aParam[$i][4] & @CRLF) ;== /DEBUG == Next Return $aParam EndFunc ;==>__GetParamArray ; detects the kind of a param, marked with "-", "--" or nothing Func __GetParamType($_param, $_bLiteral=False) Local $aConstLiteral[] = ['', 'CMD_ARGUMENT', 'CMD_LONGOPTION', 'CMD_SHORTOPTION'] If StringRegExp($_param, '^--') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_LONGOPTION] : $CMDLINEPARSE_LONGOPTION) If StringRegExp($_param, '^-') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_SHORTOPTION] : $CMDLINEPARSE_SHORTOPTION) Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_ARGUMENT] : $CMDLINEPARSE_ARGUMENT) EndFunc ;==>__GetParamType ; returnes by each call the next number from a given range Func __IteratorNum($_start=Null, $_end=Null, $_create=0) Local Static $a[1], $index If $_create Then $index = -1 Local $n = 0 ReDim $a[$_end +1 -($_start)] For $i = $_start To $_end $a[$n] = $i $n += 1 Next Return __IteratorNum Else $index += 1 Return $index < UBound($a) ? $a[$index] : Null EndIf EndFunc ;==>__IteratorNum #EndRegion