Parameter list parser with delimiter and value list support

Share your advanced PureBasic knowledge/code with the community.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Parameter list parser with delimiter and value list support

Post by Mistrel »

For command line parameters I wanted a very quick and easy way to add rules and also to parse all of the parameter values for me. I've written something like this before "one the fly" but it's always been specific to the application. This one is portable.

Code: Select all

Structure ParameterList_Rules
  Key.s
  Left.s
  KeyDelim.s
  ListDelim.s
  MaxList.l
EndStructure

Structure Glob_ParameterList
  List Rules.ParameterList_Rules()
EndStructure

Structure ParameterList_MapList
  List ValueList.s()
EndStructure

Global Glob_ParameterList.Glob_ParameterList

Declare ParameterList_AddRule(Key.s, Left.s, KeyDelim.s="", ListDelim.s="", MaxList=0)
Declare.s ParameterList_Parse(Map MapOut.ParameterList_MapList(), CaseSensitive.b=#False)

Procedure ParameterList_AddRule(Key.s, Left.s, KeyDelim.s="", ListDelim.s="", MaxList=0)
  AddElement(Glob_ParameterList\Rules())
  
  Glob_ParameterList\Rules()\Key.s=Key.s
  Glob_ParameterList\Rules()\Left.s=Left.s
  Glob_ParameterList\Rules()\KeyDelim.s=KeyDelim.s
  Glob_ParameterList\Rules()\ListDelim.s=ListDelim.s
  Glob_ParameterList\Rules()\MaxList=MaxList
  
  ProcedureReturn #True
EndProcedure

Procedure.s ParameterList_Parse(Map MapOut.ParameterList_MapList(), CaseSensitive.b=#False)
  Protected ParamCount
  Protected Param.s
  Protected KeyNoLCase.s
  Protected Key.s
  Protected Left.s
  Protected KeyDelim.s
  Protected ListDelim.s
  Protected Field.s
  Protected MaxList
  Protected ParamLen
  Protected LeftLen
  Protected KeyLen
  Protected LeftMatch
  Protected ParamKey.s
  Protected KeyMatch
  Protected ParamValue.s
  Protected i
  Protected n

  ;/ Empty the list to be used for output
  ClearMap(MapOut())
  
  ParamCount=CountProgramParameters()
  
  ;/ Nothing to return
  If Not ParamCount
    ProcedureReturn ""
  EndIf
  
  For i=0 To ParamCount-1
    Param.s=ProgramParameter(i)
    
    ForEach Glob_ParameterList\Rules()
      LeftMatch=#False
      KeyMatch=#False
      
      ParamValue.s=""
      
      KeyNoLCase.s=Glob_ParameterList\Rules()\Key.s
      Key.s=Glob_ParameterList\Rules()\Key.s
      Left.s=Glob_ParameterList\Rules()\Left.s
      KeyDelim.s=Glob_ParameterList\Rules()\KeyDelim.s
      ListDelim.s=Glob_ParameterList\Rules()\ListDelim.s
      MaxList=Glob_ParameterList\Rules()\MaxList
      
      If Not CaseSensitive
        Key.s=LCase(Key.s)
      EndIf
      
      ParamLen=Len(Param.s)
      LeftLen=Len(Left.s)
      KeyLen=Len(Key.s)
      
      ;/ Don't access the string beyond its available bounds
      If LeftLen>ParamLen
        Continue
      EndIf
      
      If Left(Param.s,LeftLen)=Left.s
        LeftMatch=#True
        
        ;/ Don't access the string beyond its available bounds
        If LeftLen+KeyLen>ParamLen
          Continue
        EndIf
        
        ParamKey.s=Mid(Param.s,LeftLen+1,KeyLen)
        
        If Not CaseSensitive
          ParamKey.s=LCase(ParamKey.s)
        EndIf
        
        If ParamKey.s=Key.s
          KeyMatch=#True
          
          If Not KeyDelim.s
            ;/ If no delimiter is expected then map the key and continue
            MapOut(Key.s)
            Break
          EndIf
          
          ;/ If the param delimiter is a space then evaluate the next
          ;/ parameter and increment the loop by 1
          If KeyDelim.s=" "
            If Not i+1>ParamCount-1
              ParamValue.s=ProgramParameter(i+1)
              i+1
            EndIf
          Else
            ParamValue.s=StringField(Mid(Param.s,LeftLen+1,Len(Param.s)-LeftLen),2,KeyDelim.s)
          EndIf
          
          ParamValue.s=Trim(ParamValue.s)
          
          If ListDelim.s
            For n=1 To CountString(ParamValue.s,ListDelim.s)+1
              Field.s=Trim(StringField(ParamValue.s,n,ListDelim.s))
              
              ;/ Do not exceed the maximum list size for the value list of a parameter
              If n>MaxList
                ProcedureReturn "Too many paramters specified in the value list for "+Left.s+KeyNoLCase.s+" - limit of "+Str(MaxList)
              EndIf
              
              ;/ Only add values which are not blank strings
              If Field.s
                AddElement(MapOut(Key.s)\ValueList.s())
                MapOut(Key.s)\ValueList.s()=Field.s
              EndIf
            Next n
            Break
          Else
            ;/ Only add values which are not blank strings
            If ParamValue.s
              AddElement(MapOut(Key.s)\ValueList.s())
              MapOut(Key.s)\ValueList.s()=ParamValue.s
              Break
            EndIf
          EndIf
        EndIf
      EndIf
    Next
    
    ;/ Report errors if a field left or key cannot be resolved
    If Not LeftMatch Or Not KeyMatch
      ProcedureReturn "Unknown or unexpected field: "+Param.s
    EndIf
  Next i
EndProcedure
Example code:

Code: Select all

Define Error.s
NewMap MapOut.ParameterList_MapList()

;/ Command line: /I:lib1.dll,lib2.dll,lib3.dll,lib4.dll /E:lib2.dll,lib4.dll /R /O:GDK3D_.dll -t TestT --y TestY --mM TestCase
ParameterList_AddRule("I","/",":",",",255)
ParameterList_AddRule("E","/",":",",",255)
ParameterList_AddRule("R","/")
ParameterList_AddRule("O","/",":")
ParameterList_AddRule("t","-"," ")
ParameterList_AddRule("y","--"," ")
ParameterList_AddRule("mM","--"," ")

Error.s=ParameterList_Parse(MapOut(),#True)

If Error.s
  Debug Error.s
  End
EndIf

ForEach MapOut()
  Debug MapKey(MapOut())
  ForEach MapOut()\ValueList.s()
    Debug MapOut()\ValueList.s()
  Next
Next
Output:
E
lib2.dll
lib4.dll

I
lib1.dll
lib2.dll
lib3.dll
lib4.dll

O
GDK3D_.dll

R

t
TestT

y
TestY

mM
TestCase
Just loop through the map and do a switch/case test for the parameters you want. If the parameter list parser is not case sensitive then all of the keys will be lower-case.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Parameter list parser with delimiter and value list supp

Post by Kwai chang caine »

Thanks MISTREL 8)

1/ I don't understand but i have no debug line :shock:
The MapOut() is empty, it's perhaps normal ...

2/ Another thing, have you not thinking at the optional possibility to manage also "Under parameter", like a tree, it would be perhaps a plus for your code :
E
lib2.dll
lib4.dll
-O
--GDK3D_.dll
--lib1.dll
--R
---lib2.dll
- -lib3.dll
---lib4.dll
---t
----TestT
----y
-----TestY
-----M
------TestCase
Again thanks for sharing :wink:
ImageThe happiness is a road...
Not a destination
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Parameter list parser with delimiter and value list supp

Post by Mistrel »

A tree doesn't make any sense as parameters can be evaluated in any order. Just query the map in the order you want to process them after they have been parsed.

Also, you probably don't see anything because you didn't feed anything in as command line parameters. You can either do this in the project options from the IDE or by compiling to an executable and passing the parameters manually.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Parameter list parser with delimiter and value list supp

Post by Kwai chang caine »

Thanks MISTREL :wink:
ImageThe happiness is a road...
Not a destination
User avatar
Tenaja
Addict
Addict
Posts: 1948
Joined: Tue Nov 09, 2010 10:15 pm

Re: Parameter list parser with delimiter and value list supp

Post by Tenaja »

Mistrel,
Thanks for sharing this. I may have use for it in the future, but for the current project I was going to use the simpler CommandLineParameter(Switch.s) in this thread:
http://purebasic.fr/english/viewtopic.php?f=12&t=5731

However, neither yours nor the other one has the provision for passing a filename containing spaces. (If you wrap it in quotes, the quote must be the first character.) By any chance have you already worked up a solution for that?
Post Reply