Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Changeset 3415

Show
Ignore:
Timestamp:
04/07/08 17:09:18 (8 months ago)
Author:
larsivi
Message:

Close to final version of Arguments, the ArgParser? replacement. refs #748, thanks a bunch darrylb!

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/tango/util/Arguments.d

    r3349 r3415  
    11/******************************************************************************* 
    2     copyright:  Copyright (c) 2007 Darryl Bleau. All rights reserved. 
    3  
     2    copyright:  Copyright (c) 2008 Darryl Bleau. All rights reserved. 
    43    license:    BSD style: $(LICENSE) 
    5  
    6     version:    Oct2007 
     4    version:    Feb2008 
    75    author:     Darryl B, Jeff D 
    8  
     6     
    97    History: 
    108    --- 
     
    1210    Sep2006  Darryl Bleau  Original C module 
    1311    Sep2007  Jeff Davey    Ported to D, added comments. 
    14     Oct2007  Darryl Bleau  Added validation delegates/functions, more comments. 
     12    Oct2007  Darryl Bleau  Added validation delegates/functions, additional  
     13                             comments. 
     14    Feb2008  Darryl Bleau  Entirely rewritten, addressing issues brought up  
     15                             with initial design. 
    1516    --- 
    1617*******************************************************************************/ 
    1718 
    1819/******************************************************************************* 
    19  
    20     This module is used to parse arguments, and give easy access to them. 
    21  
     20Arguments is a module for parsing argument strings, such as command line  
     21arguments passed to main(). It is an extrememly flexible module, able to  
     22accomodate a wide variety of desired parsing behavior. Arguments follows a  
     23declarative paradigm, requiring the programmer to tell it some basic information 
     24about the arguments and any possible parameters. However, it also will parse a  
     25given string using a default set of actions that covers most basic use cases. 
    2226*******************************************************************************/ 
    2327 
    2428module tango.util.Arguments; 
    2529 
    26 /*********************************************************************** 
    27  
    28     This exception is thrown during argument validation. 
    29  
    30 ***********************************************************************/ 
    31  
    32 public class ArgumentException : Exception 
    33 
    34     /*********************************************************************** 
    35  
    36         The reason the exception was thrown 
    37  
    38     ***********************************************************************/ 
    39  
    40     enum ExceptionReason 
     30import tango.text.Util; 
     31 
     32/******************************************************************************* 
     33    The Arguments class is used to parse a given argument string, and  
     34    encapsulate all found arguments and parameters. For example, this module  
     35    parses command line strings such as: "-a -b -c --long --argument=parameter". 
     36 
     37    Arguments can be short or long, and can optionally be passed parameters.  
     38    Parameters can also be passed implicitly (that is, not belonging to a  
     39    particular argument). 
     40 
     41    Example: 
     42    --- 
     43    char[][] cmdl = ["programname", "-z", "--lights:off", "-zz"]; 
     44    Arguments args = new Arguments(cmdl[1..$]); 
     45    if ((args.contains("lights")) && (args["lights"].length)) 
    4146    { 
    42         /*********************************************************************** 
    43           An invalid parameter was passed 
    44          ***********************************************************************/ 
    45  
    46         INVALID_PARAMETER, 
    47         /*********************************************************************** 
    48           A parameter wasn't passed to an argument when it was expected 
    49          ***********************************************************************/ 
    50  
    51         MISSING_PARAMETER, 
    52         /*********************************************************************** 
    53           An argument was missing 
    54          ***********************************************************************/ 
    55  
    56         MISSING_ARGUMENT 
    57     } 
    58  
    59     private char[] _name; 
    60     private char[] _parameter; 
    61     private ExceptionReason _reason; 
    62  
    63     /*********************************************************************** 
    64  
    65       The name of the specific argument 
    66  
    67      ***********************************************************************/ 
    68  
    69     char[] name() { return _name; } 
    70  
    71     /*********************************************************************** 
    72  
    73       The parameter to the argument 
    74  
    75      ***********************************************************************/ 
    76  
    77     char[] parameter() { return _parameter; } 
    78  
    79     /*********************************************************************** 
    80  
    81       The enum to the reason it failed 
    82  
    83      ***********************************************************************/ 
    84  
    85     ExceptionReason reason() {  return _reason; } 
    86  
    87     this(char[] msg, char[] name, char[] parameter, ExceptionReason reason) 
    88     { 
    89         _name = name; 
    90         _parameter = parameter; 
    91         _reason = reason; 
    92         super(msg); 
    93     } 
    94 
    95  
    96 /*********************************************************************** 
    97  
    98     The Arguments class is used to parse arguments and encapsulate the 
    99     parameters passed by an application user. 
    100  
    101     The command line arguments into an array of found arguments and their 
    102     respective parameters (if any). 
    103  
    104     Arguments can be short (-), long (--), or implicit. Arguments can 
    105     optionally be passed parameters. For example, this module 
    106     parses command lines such as: "-a -b -c --long --argument=parameter" 
    107  
    108     Example: 
    109     --- 
    110     char[][] arguments = [ "programname", "-a:on", "-abc:on", "--this=good", "-x:on" ]; 
    111     Arguments args = new Arguments(arguments); 
    112     if (args) 
    113     { 
    114         args.addValidation("b", true, false); 
    115         args.addValidation("c", true, true); 
    116         args.addValidation("x", false, true); 
    117         args.addValidation("this", false, true); 
    118         args.addValidation("this", (char[] a) { (return a.length < 5); }); 
    119         try 
    120         { 
    121             args.validate(); 
    122             return Test.Status.Success; 
    123         } 
    124         catch (ArgumentException ex) 
    125         { 
    126             messages ~= Stdout.layout.convert("{}: {} - {}", ex.name, ex.msg,  
    127                         ex.reason == ArgumentException.ExceptionReason.INVALID_PARAMETER ?  
    128                         "invalid parameter" : ex.reason == ArgumentException.ExceptionReason.MISSING_PARAMETER ?  
    129                         "missing parameter" : "missing argument"); 
    130         } 
    131     } 
    132     --- 
    133  
    134     Syntax: 
    135     --- 
    136     Short Argument - -[x][:=]?[ parameter]... 
    137     Long Argument - --[long][:=]?[ parameter]... 
    138     Implicit Arguments - [implicitName]... Multiple implicit arguments are allowed. 
    139     --- 
    140  
    141     Usage: 
    142  
    143     Short options can be grouped in a single dash. The following are equivalent. 
    144     --- 
    145     - "myprogram -a -b -c" 
    146     - "myprogram -abc" 
    147     --- 
    148  
    149     Arguments can be passed with space, '=', or ':'. The following are equivalent. 
    150     --- 
    151     - "myprogram -c arg" 
    152     - "myprogram -c=arg" 
    153     - "myprogram -c:arg" 
    154     --- 
    155  
    156     As are these. 
    157     --- 
    158     - "myprogram --long arg" 
    159     - "myprogram --long=arg" 
    160     - "myprogram --long:arg" 
    161     --- 
    162  
    163     Arguments can contain either '=' or ':', but not both. For example. 
    164     the following results in the argument 'long' being set to 'arg=other' 
    165     and 'arg:other', respectively. 
    166     --- 
    167     - "myprogram --long:arg=other" 
    168     - "myprogram --long=arg:other" 
    169     --- 
    170  
    171     Blank dashes are ignored. The following are all equivalent. 
    172     --- 
    173     - "myprogram -c -- -a" 
    174     - "myprogram -c - -a" 
    175     - "myprogram - - - -a -- -c" 
    176     --- 
    177  
    178     In the absence of implicit arguments, short options can be infered when 
    179     they come first. Given no implicit arguments, the following are equivalent. 
    180     --- 
    181     - "myprogram abc" 
    182     - "myprogram -abc" 
    183     - "myprogram -a -b -c" 
    184     --- 
    185  
    186     Short options are case sensitive, while long options are not. The following 
    187     are equivalent. 
    188     --- 
    189     - "myprogram -a -A -LONG" 
    190     - "myprogram -a -A -Long" 
    191     - "myprogram -a -A -long" 
    192     --- 
    193  
    194     In the event of multiple definitions of an argument, any parameters given 
    195     will be concatenated. The following are equivalent. 
    196     --- 
    197     - "myprogram -a one two three" 
    198     - "myprogram -a one -a two -a three" 
    199     - "myprogram -a:one two -a=three" 
    200     --- 
    201  
    202     Multiple parameters can be iterated through using via the opIndex operator. 
    203     For example, given: 
    204     --- 
    205     - "myprogram -collect one two three '4 5 6'" 
    206     --- 
    207     args["collect"] will return a char[][] array ["one", "two", "three", "4 5 6"]. 
    208  
    209     Implicit arguments can be defined by passing in an implicit arguments array, 
    210     which may look something like: ["first", "second"]. 
    211     Given implicit arguments, any non-argument command line parameters will be 
    212     automatically assigned to the implicit arguments, 
    213     in the order they were given. 
    214     For example, given the implicit arguments ["first", "second"] and command line: 
    215     --- 
    216     - "myprogram hello there bob" 
    217     --- 
    218     The argument 'first' will be assigned 'hello', and the argument 'second' will 
    219     be assigned both 'there' and 'bob'. 
    220  
    221     Any intervening arguments will end the assignment. For example, given: 
    222     --- 
    223     - "myprogram hello there bob -a how are you" 
    224     --- 
    225     'first' is assigned 'hello', 'second' is assigned 'there' and 'bob', and 'a' 
    226     is assigned 'how', 'are', and 'you'. 
    227  
    228     Implicit arguments also allows programs to support non-option arguments, 
    229     given implicit arguments ["actions"], and a command line such as: 
    230     --- 
    231     - "myprogram mop sweep get_coffee -time:now" 
    232     --- 
    233     args["actions"] will contain ["mop", "sweep", "get_coffee"], and 
    234     args["time"] will contain "now". 
    235  
    236 ***********************************************************************/ 
     47        if (args["lights"] == "off") 
     48            lights.enabled = false; // turn the lights off 
     49        sleep(args.count("z")); // sleep for the indicated period 
     50    } 
     51    --- 
     52     
     53    Terminology: 
     54    --- 
     55    - Short Argument: Single character arguments, potentially grouped under a  
     56          single prefix. (-a, -abc) 
     57    - Long Argument: Multiple character arguments, always contained under  
     58          their own prefix. (--file, --help) 
     59    - Parameter: A parameter to a particular argument, identified by a  
     60          parameter delimiter or whitespace with no prefix (--arg=parameter, -arg parameter) 
     61    - Implicit Parameter: A given parameter that doesn't match to a particular  
     62          argument, accessible using the null index (args[null]). (file1.txt file2.txt) 
     63    --- 
     64 
     65    Behavior: 
     66 
     67    Default: 
     68 
     69    Arguments defaults to a basic parse behavior which provides for handling  
     70    of most common argument strings. You can invoke this default parse behavior 
     71    by simply passing the argument string to the class constructor. 
     72 
     73    Short Arguments can be grouped under a single prefix. The following are  
     74    equivalent. 
     75    --- 
     76    - "-a -b -c" 
     77    - "-abc" 
     78    --- 
     79 
     80    Argument Parameters can be identified via being space-separated from their  
     81    argument, or via either a '=' or a ':'. The following are equivalent. 
     82    --- 
     83    - "-c arg" 
     84    - "-c=arg" 
     85    - "-c:arg" 
     86    --- 
     87 
     88    Only the first found parameter will be skipped over as a delimiter. The  
     89    following are equivalent. 
     90    --- 
     91    - "-c =blah" 
     92    - "-c==blah" 
     93    - "-c:=blah" 
     94    --- 
     95 
     96    Empty prefixes are ignored. The following are equivalent. 
     97    --- 
     98    - "-c -- -a" 
     99    - "-c - -a" 
     100    - "- - - - -a -- -c" 
     101    --- 
     102 
     103    Multiple parameters to a particular argument are appended to the array for  
     104    that argument. The following are equivalent. 
     105    --- 
     106    - "-a one two three" 
     107    - "-a one -a two -a three" 
     108    - "-a:one two -a=three" 
     109    --- 
     110 
     111    Implicit parameters are assigned to the [null] index of the class. 
     112    --- 
     113    - "file1 file2 file3" 
     114    --- 
     115 
     116    Defined: 
     117 
     118    Accepted arguments and the particular behavior to apply to them can be  
     119    defined via the .define method. .define returns a Arguments.Definition,  
     120    which can then be chained with additional methods for further definition. 
     121    The prefixes for both short and long arguments can be overwritten or  
     122    appended to, as can the parameter delimiters, by setting the .prefixLong,  
     123    .prefixShort, and/or delimiters arrays as appropriate. 
     124     
     125    Null prefixes can be used to specify 'word sentence' behavior. 
     126    --- 
     127    args.prefixLong = [null]; 
     128    --- 
     129 
     130    Will parse each of the items as if they were Arguments prefixed with "--"  
     131    in the default behavior. 
     132    --- 
     133    myProgram one two three 
     134    --- 
     135 
     136    Null delimiters can be used to specify 'arguments smushed up with  
     137    parameters' behavior. 
     138    --- 
     139    args.define("f").parameters.delimiters([null]); 
     140    --- 
     141 
     142    Will parse the following as args["f"] = ["file"]. 
     143    --- 
     144    myProgram -ffile 
     145    --- 
     146 
     147    You can also define argument aliases. 
     148    --- 
     149    args.define("a").aliases(["b"]); 
     150    --- 
     151 
     152    Given that, the following would be equivalent. 
     153    --- 
     154    - "-a" 
     155    - "-b" 
     156    --- 
     157 
     158    You can also specify that an argument has default parameters. 
     159    --- 
     160    args.define("x").defaults(["hello"]); 
     161    --- 
     162 
     163    In which case, the following would be equivalent. 
     164    --- 
     165    - "-a -x hello" 
     166    - "-a" 
     167    --- 
     168 
     169    Finally, you may also specify limiting behavior, that a particular argument 
     170    is required, conflicts with another argument, requires another argument,  
     171    that a callback should be called when encountering the argument, or that a  
     172    particular validation routine should be called on all defined parameters  
     173    for an argument. 
     174    --- 
     175    args.define("x").required; 
     176    args.define("x").conflicts("b"); 
     177    args.define("x").requires("a"); 
     178    args.define("x").callback( ... ); 
     179    args.define("x").validation( ... ); 
     180    --- 
     181 
     182    Note that requires and conflicts are order-sensitive, if you want, for  
     183    example, "a" and "b" to be mutually exclusive, you would need to define  
     184    that explicitly. 
     185    --- 
     186    args.define("a").conflicts("b"); 
     187    args.define("b").conflicts("a"); 
     188    --- 
     189 
     190    Samples: 
     191 
     192    Sample definitions (tar-like). 
     193    --- 
     194    args.prefixShort = [null]; 
     195    args.define("f").parameters; 
     196    args.define("v"); 
     197    args.define("x"); 
     198    args.define("z"); 
     199    args.parse(["zxfv", "file1.tar", "file2.tar"]); 
     200    --- 
     201 
     202    Sample definitions (dsss-like). 
     203    --- 
     204    args.prefixLong = [null]; 
     205    args.define("net").parameters(0,2); 
     206    args.parse(["net", "install", "tango"]); 
     207    --- 
     208 
     209    Sample definitions (ls-like). 
     210    --- 
     211    args.define("a"); 
     212    args.define("l"); 
     213    args.parse(["-al", "blah.txt"]); 
     214    args.parse(["blah.txt", "-al"]); 
     215    --- 
     216 
     217    Sample definitions (complex). 
     218    --- 
     219    args.define("x").defaults(["on"]).aliases(["X"]).conflicts("a").requires("b").required; 
     220    args.define("C").callback(delegate void(char[] n, char[] p){ ... }); 
     221    args.define("V").validation(delegate bool(char[][] p, out char[] ip){ ... }); 
     222    --- 
     223     
     224    Ideas: 
     225    Some thoughts about future progression of the module. 
     226    --- 
     227    -Forcing arguments to lowercase. (define("x").lowercase, -X gives "x" in args) 
     228    -User defined callback when encountering an undefined argument. 
     229    -Standard help text generation 
     230    --- 
     231*******************************************************************************/ 
    237232 
    238233public class Arguments 
    239234{ 
    240     /// Function used to validate multiple parameters at once. 
    241     alias bool function(char[][] params, inout char[] invalidParam) validationFunctionMulti; 
    242     /// Delegate used to validate multiple parameters at once. 
    243     alias bool delegate(char[][] params, inout char[] invalidParam) validationDelegateMulti; 
    244     /// Function used to validate single parameters at a time. 
    245     alias bool function(char[] param) validationFunction; 
    246     /// Delegate used to validate single parameters at a time. 
    247     alias bool delegate(char[] param) validationDelegate; 
     235    /// All Arguments exceptions are derived from this 
     236    class ParseException : Exception 
     237    { 
     238        private char[] _argument; 
     239        /// The name of the particular argument 
     240        char[] argument() { return _argument; } 
     241        this(char[] msg, char[] argument) 
     242        { 
     243            _argument = argument; 
     244            super(msg); 
     245        } 
     246    } 
     247 
     248    /// Thrown when a parameter for a given argument is determined to be invalid 
     249    class InvalidParameterException : ParseException 
     250    { 
     251        private char[] _parameter; 
     252        /// The invalid parameter 
     253        char[] parameter() { return _parameter; } 
     254        this(char[] msg, char[] argument, char[] parameter) 
     255        { 
     256            _parameter = parameter; 
     257            super(msg, argument); 
     258        } 
     259    } 
     260 
     261    /// Thrown when the minimum defined number of parameters for a given  
     262    /// argument are not discovered 
     263    class InsufficientParameterException : ParseException 
     264    { 
     265        private int _count; 
     266        private int _expected; 
     267        /// The discovered parameter count 
     268        int count() { return _count; } 
     269        /// The expected parameter count 
     270        int expected() { return _expected; } 
     271        this(char[] msg, char[] argument, int count, int expected) 
     272        { 
     273            _count = count; 
     274            _expected = expected; 
     275            super(msg, argument); 
     276        } 
     277    } 
     278 
     279    /// Thrown when a previously discovered argument was defined to conflict  
     280    /// with this one 
     281    class ConflictingArgumentException : ParseException 
     282    { 
     283        private char[] _conflict; 
     284        /// The name of the conflicting argument 
     285        char[] conflict() { return _conflict; } 
     286        this(char[] msg, char[] argument, char[] conflict) 
     287        { 
     288            _conflict = conflict; 
     289            super(msg, argument); 
     290        } 
     291    } 
     292 
     293    /// Thrown when an argument requires a previously defined argument which  
     294    /// was not discovered 
     295    class MissingPrerequisiteException : ParseException 
     296    { 
     297        private char[] _requirement; 
     298        /// The name of the missing prerequisite argument 
     299        char[] requirement() { return _requirement; } 
     300        this(char[] msg, char[] argument, char[] requirement) 
     301        { 
     302            _requirement = requirement; 
     303            super(msg, argument); 
     304        } 
     305    } 
     306 
     307    /// Thrown when an argument is defined as being required but is not  
     308    /// discovered 
     309    class MissingArgumentException : ParseException 
     310    { 
     311        this(char[] msg, char[] argument) 
     312        { 
     313            super(msg, argument); 
     314        } 
     315    } 
    248316 
    249317    private char[][][char[]] _args; 
    250     private char[] _program; 
    251     private struct validation 
     318    private char[][char[]] _aliases; 
     319    private int[char[]] _count; 
     320 
     321    private class Parameters 
     322    { 
     323        char[][] opIndex(char[] key) 
     324        { 
     325            char[][] rtn = null; 
     326            if (key in _args) 
     327                rtn = _args[key]; 
     328            return rtn; 
     329        } 
     330    } 
     331    /// Provides char[][] access of all discovered parameters for a particular 
     332    /// argument (via .parameters["argName"]) 
     333    Parameters parameters; 
     334 
     335    /// Delegate intended to be called back when the defined argument is  
     336    /// discovered. 
     337    alias void delegate(char[] name, char[] parameter) argumentCallback; 
     338    /// Delegate to be used to perform validation on all parameters given for  
     339    /// the defined argument. 
     340    alias bool delegate(char[][] parameters, out char[] invalidParameter) argumentValidation; 
     341 
     342    /// This class represents the definition of one particular argument. 
     343    class Definition 
    252344    { 
    253         validationFunction[] validF; 
    254         validationDelegate[] validD; 
    255         validationFunctionMulti[] validFM; 
    256         validationDelegateMulti[] validDM; 
    257         bool required; 
    258         bool paramRequired; 
    259     } 
    260     private validation[char[]] _validations; 
    261  
    262     private char[] parseLongArgument(char[] arg) 
    263     { 
    264         char[] rtn; 
    265  
    266         int locate(char[] arg, char c) { 
    267             foreach (i, a; arg) 
    268                 if (a is c) 
    269                     return i; 
    270             return arg.length; 
    271         } 
    272  
    273  
    274         if (arg) 
    275         { 
    276             int equalDelim = locate(arg, '='); 
    277             int colonDelim = locate(arg, ':'); 
    278             int paramPos = ((equalDelim != arg.length) &&  
    279                            (colonDelim != arg.length)) ?  
    280                            (equalDelim < colonDelim ? equalDelim : colonDelim) :  
    281                            (equalDelim != arg.length) ? equalDelim : colonDelim; 
    282             if (paramPos != arg.length) 
    283             { 
    284                 char[] argName = arg[0 .. paramPos]; 
    285                 char[] value = arg[(paramPos + 1) .. arg.length]; 
    286                 setArg(argName, value); 
    287                 rtn = argName; 
    288             } 
    289             else 
    290             { 
    291                 setArg(arg, null); 
    292                 rtn = arg; 
    293             } 
    294         } 
    295         return rtn; 
    296     } 
    297  
    298     private void setArg(char[] arg, char[] value) 
    299     { 
    300         if (arg) 
    301         { 
    302             if ((arg in _args) || (value !is null)) 
    303                 _args[arg] ~= value; 
    304             else 
    305                 _args[arg] = null; 
    306         } 
    307     } 
    308  
    309     private char[] parseShortArgument(char[] arg) 
    310     { 
    311         char[] rtn; 
    312  
    313         if (arg) 
    314         { 
    315             char[] argName; 
    316             uint i; 
    317             for (i = 0; i < arg.length; i++) 
    318             { 
    319                 if (arg[i] != '=' && arg[i] != ':') 
     345        private char[] _name; 
     346        /// The name of the particular argument. 
     347        char[] name() { return _name; } 
     348        private int _parameterMin; 
     349        /// The defined minumum number of parameters this argument requires. 
     350        int parameterMin() { return _parameterMin; } 
     351        private int _parameterMax; 
     352        /// The defined maximum number of parameters this argument will consume. 
     353        int parameterMax() { return _parameterMax; } 
     354        private bool _isRequired; 
     355        /// Whether this argument is required. 
     356        bool isRequired() { return _isRequired; } 
     357        private char[][] _definedDelimiters; 
     358        /// A set of defined parameter delimiters for this argument. 
     359        char[][] definedDelimiters() { return _definedDelimiters; } 
     360        private char[][] _definedAliases; 
     361        /// A set of defined aliases for this argument. 
     362        char[][] definedAliases() { return _definedAliases; } 
     363        private char[][] _definedConflicts; 
     364        /// A set of defined conflicting arguments to this argument. Note that  
     365        /// this is order-sensitive. 
     366        char[][] definedConflicts() { return _definedConflicts; } 
     367        private char[][] _definedPrerequisites; 
     368        /// A set of defined required arguments to this argument. Note that  
     369        /// they are also order-sensitive. 
     370        char[][] definedPrerequisites() { return _definedPrerequisites; } 
     371        private argumentCallback[] _callbacks; 
     372        /// A set of defined callback functions to be called when this argument 
     373        /// is discovered. 
     374        argumentCallback[] callbacks() { return _callbacks; } 
     375        private argumentValidation[] _validations; 
     376        /// A set of defined validation functions to be called on the  
     377        /// parameters for this argument. 
     378        argumentValidation[] validations() { return _validations; } 
     379        private char[][] _defaultParameters; 
     380        /// A set of default parameters that will represent this argument if  
     381        /// it is not discovered during parsing. 
     382        char[][] defaultParameters() { return _defaultParameters; } 
     383 
     384        /// Defines both a minumum and maximum number of parameters that this 
     385        /// argument will take. Set max to -1 for unlimited. 
     386        Definition parameters(int min, int max) 
     387        { 
     388            this._parameterMin = min; 
     389            this._parameterMax = max; 
     390            return this; 
     391        } 
     392 
     393        /// Defines a definite number of parameters that this argument must  
     394        /// take, implies both min and max of the given requirement. 
     395        Definition parameters(int req) 
     396        { 
     397            this._parameterMin = req; 
     398            this._parameterMax = req; 
     399            return this; 
     400        } 
     401 
     402        // Defines that this argument will consume any and all parameters  
     403        /// following it that don't belong to another argument. 
     404        Definition parameters() 
     405        { 
     406            this._parameterMin = 0; 
     407            this._parameterMax = -1; 
     408            return this; 
     409        } 
     410 
     411        /// Sets the default parameters that will be assigned to this argument  
     412        /// if it is not discovered during parsing. 
     413        Definition defaults(char[][] defaultParameters) 
     414        { 
     415            this._defaultParameters = defaultParameters; 
     416            return this; 
     417        } 
     418 
     419        /// Sets the set of valid delimiters for this argument. Set to [null]  
     420        /// for '-ffile' behavior. Supercedes any defined Arguments.delimiters,  
     421        /// for this argument only. 
     422        Definition delimiters(char[][] delimiters) 
     423        { 
     424            this._definedDelimiters ~= delimiters; 
     425            return this; 
     426        } 
     427 
     428        /// Defines that this argument must be discovered during parsing. 
     429        Definition required() 
     430        { 
     431            this._isRequired = true; 
     432            return this; 
     433        } 
     434 
     435        /// Defines a set of aliases which will also correspond to this argument.  
     436        /// Note that only the root defined argument is accessible via the  
     437        /// Arguments index[] following parse. 
     438        Definition aliases(char[][] aliases) 
     439        { 
     440            this._definedAliases ~= aliases; 
     441            foreach(char[] thisAlias; aliases) 
     442                _aliases[thisAlias] = this.name; 
     443            return this; 
     444        } 
     445 
     446        /// Defines a set of arguments which, if found before this one is  
     447        /// discovered, will conflict with this one. Note that for mutually  
     448        /// exclusive conflicts, you need to declare both directions. 
     449        Definition conflicts(char[][] conflictingArguments) 
     450        { 
     451            this._definedConflicts ~= conflictingArguments; 
     452            return this; 
     453        } 
     454 
     455        /// Defines a set of arguments which must be discovered before this  
     456        /// one is. 
     457        Definition prerequisites(char[][] requiredArguments) 
     458        { 
     459            this._definedPrerequisites ~= requiredArguments; 
     460            return this; 
     461        } 
     462 
     463        /// Defines a callback function that will be called when this argument  
     464        /// is discovered. 
     465        Definition callback(argumentCallback cb) 
     466        { 
     467            this._callbacks ~= cb; 
     468            return this; 
     469        } 
     470 
     471        /// Defines a validation function which will be called on any and all  
     472        /// found parameters for this argument. 
     473        Definition validation(argumentValidation av) 
     474        { 
     475            this._validations ~= av; 
     476            return this; 
     477        } 
     478 
     479        /*********************************************************************** 
     480        Constructor 
     481        Params: 
     482            name = name of this argument. 
     483        ***********************************************************************/ 
     484        this(char[] name) 
     485        { 
     486            this._name = name; 
     487        } 
     488    } 
     489    /// A set of defined argument Definitions, indexed by their respective name. 
     490    Definition[char[]] definitions; 
     491 
     492    /// The set of prefixes which define a short argument. 
     493    char[][] prefixShort = ["-"]; 
     494    /// The set of prefixes which define a long argument. 
     495    char[][] prefixLong = ["--"]; 
     496    /// The set of delimiters which identify a parameter to an argument. 
     497    char[][] delimiters = [":", "="]; 
     498 
     499    private char[][]* _prefixCompare(char[] candidate, char[][][] prefixes, out char[]* prefix) 
     500    { // returns the prefix array that contains the longest prefix match for our  
     501      // candidate, and sets out var to the matching prefix. 
     502        char[][]* rtn = null; 
     503        char[]* matchingPrefix = null; 
     504        for (uint p = 0; p < prefixes.length; p++) 
     505        { 
     506            for (uint i = 0; i < prefixes[p].length; i++) 
     507            { 
     508                if (candidate.length >= prefixes[p][i].length) 
    320509                { 
    321                     argName = arg[i .. i + 1]; 
    322                     setArg(argName, null); 
    323                 } 
    324                 else if (((arg.length) - i) > 1) 
    325                 { 
    326                     setArg(argName, arg[i+1 .. arg.length]); 
    327                     break; 
    328                 } 
    329             } 
    330             rtn = argName; 
    331         } 
    332         return rtn; 
    333     } 
    334  
    335     /*********************************************************************** 
    336  
    337         Allows external argument assignment, works the same as command line 
    338         in that it appends to any values already assigned to the given key. 
    339  
    340         Params: 
    341             value = assigned value 
    342             key = key to assign to 
    343  
    344     ***********************************************************************/ 
    345  
    346     void opIndexAssign(char[] value, char[] key) 
    347     { 
    348         setArg(key, value); 
    349     } 
    350  
    351     /*********************************************************************** 
    352  
    353         Allows removal of keys from the arguments. Useful if you want to replace 
    354         values for a given key rather than to append to them. 
    355  
    356         Params: 
    357             key = key to remove values from. 
    358  
    359     ***********************************************************************/ 
    360  
    361     void remove(char[] key) 
    362     { 
    363         _args[key] = null; 
    364     } 
    365  
    366     /*********************************************************************** 
    367  
    368         Directly access an argument's parameters via opIndex operator as an 
    369         array. 
    370         This is to cover something like: param1 "parm with space" param2 
    371  
    372     ***********************************************************************/ 
    373  
    374     char[][] opIndex(char[] key) 
    375     { 
    376         char[][] rtn = null; 
    377         if (key && (key in _args)) 
    378             rtn = _args[key]; 
    379         return rtn; 
    380     } 
    381  
    382     /*********************************************************************** 
    383  
    384         Operator is used to check if the argument exists 
    385  
    386     ***********************************************************************/ 
    387  
    388     bool opIn_r(char[] key) 
    389     { 
    390         bool rtn = false; 
    391         if (key) 
    392             rtn = (key in _args) !is null; 
    393         return rtn; 
    394     } 
    395  
    396     /*********************************************************************** 
    397  
    398         Adds a validation to the arguments 
    399  
    400         Params: 
    401             argument = the argument name 
    402             required = specifies if this argument is required 
    403             paramRequired = specifies if this argument requires a parameter 
    404  
    405     ***********************************************************************/ 
    406  
    407     void addValidation(char[] argument, bool required, bool paramRequired) 
    408     { 
    409         if (argument) 
    410         { 
    411             validation* val = getValidation(argument); 
    412             if (val !is null) 
    413             { 
    414                 val.required = required; 
    415                 val.paramRequired = paramRequired; 
    416             } 
    417         } 
    418     } 
    419  
    420     /*********************************************************************** 
    421  
    422         Adds a validation to the arguments 
    423  
    424         Params: 
    425             argument = the argument name 
    426             validF = a validation function for single parameters 
    427  
    428     ***********************************************************************/ 
    429  
    430     void addValidation(char[] argument, validationFunction validF) 
    431     { 
    432         if (argument && validF) 
    433         { 
    434             validation* val = getValidation(argument); 
    435             if (val !is null) 
    436                 val.validF ~= validF; 
    437         } 
    438     } 
    439  
    440     /*********************************************************************** 
    441  
    442         Adds a validation to the arguments 
    443  
    444         Params: 
    445             argument = the argument name 
    446             validD = a validation delegate for single parameters 
    447  
    448     ***********************************************************************/ 
    449  
    450     void addValidation(char[] argument, validationDelegate validD) 
    451     { 
    452         if (argument && validD) 
    453         { 
    454             validation* val = getValidation(argument); 
    455             if (val !is null) 
    456                 val.validD ~= validD; 
    457         } 
    458     } 
    459  
    460     /*********************************************************************** 
    461  
    462         Adds a validation to the arguments 
    463  
    464         Params: 
    465             argument = the argument name 
    466             validF = a validation function for multiple parameters 
    467  
    468     ***********************************************************************/ 
    469  
    470     void addValidation(char[] argument, validationFunctionMulti validFM) 
    471     { 
    472         if (argument && validFM) 
    473         { 
    474             validation* val = getValidation(argument); 
    475             if (val !is null) 
    476                 val.validFM ~= validFM; 
    477         } 
    478     } 
    479  
    480     /*********************************************************************** 
    481  
    482         Adds a validation to the arguments 
    483  
    484         Params: 
    485             argument = the argument name 
    486             validD = a validation delegate for multiple parameters 
    487  
    488     ***********************************************************************/ 
    489  
    490     void addValidation(char[] argument, validationDelegateMulti validDM) 
    491     { 
    492         if (argument && validDM) 
    493         { 
    494             validation* val = getValidation(argument); 
    495             if (val !is null) 
    496                 val.validDM ~= validDM; 
    497         } 
    498     } 
    499  
    500     private validation* getValidation(char[] argument) 
    501     { 
    502         validation* rtn = null; 
    503         if (!(argument in _validations)) 
    504         { 
    505             validation newValidation; 
    506             _validations[argument] = newValidation; 
    507         } 
    508         if (argument in _validations) 
    509             rtn = &(_validations[argument]); 
    510         return rtn; 
    511     } 
    512  
    513     /*********************************************************************** 
    514  
    515         Validates the parsed arguments. 
    516  
    517         Throws ArgumentException if it finds something wrong. 
    518  
    519     ***********************************************************************/ 
    520  
    521     void validate() 
    522     { 
    523         foreach(char[] argument, validation val; _validations) 
    524         { 
    525             if (val.required && !(argument in _args)) 
    526                 throw new ArgumentException("Argument required.", argument, null, ArgumentException.ExceptionReason.MISSING_ARGUMENT); 
    527  
    528             if (val.paramRequired && (argument in _args) && (_args[argument].length == 0)) 
    529                 throw new ArgumentException("Parameter required.", argument, null, ArgumentException.ExceptionReason.MISSING_PARAMETER); 
    530  
    531             if ((argument in _args) && (_args[argument].length > 0)) 
    532             { 
    533                 char[] invalidParameter = null; 
    534                 foreach(validationFunctionMulti validFM; val.validFM) 
    535                     if (!validFM(_args[argument], invalidParameter)) 
    536                         break; 
    537                 if (invalidParameter is null) 
    538                 { 
    539                     foreach(validationDelegateMulti validDM; val.validDM) 
    540                         if (!validDM(_args[argument], invalidParameter)) 
     510                    if (prefixes[p][i] == candidate[0..(prefixes[p][i].length)]) 
     511                    { 
     512                        if ((matchingPrefix is null) || (prefixes[p][i].length > (*matchingPrefix).length)) 
     513                        { 
     514                            matchingPrefix = &prefixes[p][i]; 
     515                            rtn = &prefixes[p]; 
    541516                            break; 
    542                     if (invalidParameter is null) 
    543                     { 
    544                         foreach(char[] arg; _args[argument]) 
    545                         { 
    546                             foreach(validationFunction validF; val.validF) 
    547                             { 
    548                                 if (!validF(arg)) 
    549                                 { 
    550                                     invalidParameter = arg; 
    551                                     break; 
    552                                 } 
    553                             } 
    554                             if (invalidParameter is null) 
    555                             { 
    556                                 foreach(validationDelegate validD; val.validD) 
    557                                 { 
    558                                     if (!validD(arg)) 
    559                                     { 
    560                                         invalidParameter = arg; 
    561                                         break; 
    562                                     } 
    563                                 } 
    564                             } 
    565517                        } 
    566518                    } 
    567519                } 
    568                 if (invalidParameter !is null) 
    569                     throw new ArgumentException("Invalid parameter.", argument, invalidParameter, ArgumentException.ExceptionReason.INVALID_PARAMETER); 
    570             } 
    571         } 
    572     } 
    573  
    574     /*********************************************************************** 
    575  
    576             Parse the arguments according to the passed implicitArg list 
    577             and aliases. 
    578  
    579     ***********************************************************************/ 
    580  
    581  
    582     void parse(char[][] arguments, char[][] implicitArgs, char[][][] aliases) 
    583     { 
    584         char[] lastArgumentSet; 
    585         uint currentImplicitArg = 0; 
    586         for (uint i = 1; i < arguments.length; i++) 
    587         { 
    588             char[] currentArgument = arguments[i]; 
    589             if (currentArgument) 
    590             { 
    591                 if (currentArgument[0] == '-') 
     520            } 
     521        } 
     522        prefix = matchingPrefix; 
     523        return rtn; 
     524    } 
     525 
     526    private void _addArgument(char[] argumentName, char[][]* seenArguments, int* unsatisfiedParameter