X-Git-Url: http://nitlanguage.org diff --git a/lib/opts.nit b/lib/opts.nit index 8f2dc48..c93d39a 100644 --- a/lib/opts.nit +++ b/lib/opts.nit @@ -48,6 +48,9 @@ abstract class Option init_opt(help, default, names) end + # Init option `helptext`, `default_value` and `names`. + # + # Also set current `value` to `default`. fun init_opt(help: String, default: VALUE, names: nullable Array[String]) do if names == null then @@ -79,6 +82,7 @@ abstract class Option return text.to_s end + # Pretty print the default value. fun pretty_default: String do var dv = default_value @@ -87,7 +91,7 @@ abstract class Option end # Consume parameters for this option - protected fun read_param(it: Iterator[String]) + protected fun read_param(opts: OptionContext, it: Iterator[String]) do read = true end @@ -97,6 +101,7 @@ end class OptionText super Option + # Init a new OptionText with `text`. init(text: String) is old_style_init do super(text, null, null) redef fun pretty(off) do return to_s @@ -109,9 +114,10 @@ class OptionBool super Option redef type VALUE: Bool + # Init a new OptionBool with a `help` message and `names`. init(help: String, names: String...) is old_style_init do super(help, false, names) - redef fun read_param(it) + redef fun read_param(opts, it) do super value = true @@ -123,9 +129,10 @@ class OptionCount super Option redef type VALUE: Int + # Init a new OptionCount with a `help` message and `names`. init(help: String, names: String...) is old_style_init do super(help, 0, names) - redef fun read_param(it) + redef fun read_param(opts, it) do super value += 1 @@ -135,43 +142,64 @@ end # Option with one parameter (mandatory by default) abstract class OptionParameter super Option + + # Convert `str` to a value of type `VALUE`. protected fun convert(str: String): VALUE is abstract # Is the parameter mandatory? - var parameter_mandatory: Bool = true is writable + var parameter_mandatory = true is writable - redef fun read_param(it) + redef fun read_param(opts, it) do super - if it.is_ok and (it.item.is_empty or it.item.chars.first != '-') then + + var ok = it.is_ok + if ok and not parameter_mandatory and not it.item.is_empty and it.item.chars.first == '-' then + # The next item may looks like a known command + # Only check if `not parameter_mandatory` + for opt in opts.options do + if opt.names.has(it.item) then + # The next item is a known command + ok = false + break + end + end + end + + if ok then value = convert(it.item) it.next else - if parameter_mandatory then - errors.add("Parameter expected for option {names.first}.") - end + errors.add("Parameter expected for option {names.first}.") end end end -# An option with a String as parameter +# An option with a `String` as parameter class OptionString super OptionParameter redef type VALUE: nullable String + # Init a new OptionString with a `help` message and `names`. init(help: String, names: String...) is old_style_init do super(help, null, names) redef fun convert(str) do return str end -# An option with an enum as parameter -# In the code, declaring an option enum (-e) with an enum like `["zero", "one", "two"] -# In the command line, typing `myprog -e one` is giving 1 as value +# An option to choose from an enumeration +# +# Declare an enumeration option with all its possible values as an array. +# Once the arguments are processed, `value` is set as the index of the selected value, if any. class OptionEnum super OptionParameter redef type VALUE: Int + + # Values in the enumeration. var values: Array[String] + # Init a new OptionEnum from `values` with a `help` message and `names`. + # + # `default` is the index of the default value in `values`. init(values: Array[String], help: String, default: Int, names: String...) is old_style_init do assert values.length > 0 self.values = values.to_a @@ -189,6 +217,7 @@ class OptionEnum return id end + # Get the value name from `values`. fun value_name: String do return values[value] redef fun pretty_default @@ -202,11 +231,18 @@ class OptionInt super OptionParameter redef type VALUE: Int + # Init a new OptionInt with a `help` message, a `default` value and `names`. init(help: String, default: Int, names: String...) is old_style_init do super(help, default, names) end - redef fun convert(str) do return str.to_i + redef fun convert(str) + do + if str.is_int then return str.to_i + + errors.add "Expected an integer for option {names.join(", ")}." + return 0 + end end # An option with a Float as parameter @@ -214,6 +250,7 @@ class OptionFloat super OptionParameter redef type VALUE: Float + # Init a new OptionFloat with a `help` message, a `default` value and `names`. init(help: String, default: Float, names: String...) is old_style_init do super(help, default, names) end @@ -227,6 +264,7 @@ class OptionArray super OptionParameter redef type VALUE: Array[String] + # Init a new OptionArray with a `help` message and `names`. init(help: String, names: String...) is old_style_init do values = new Array[String] super(help, values, names) @@ -249,14 +287,12 @@ class OptionContext var rest = new Array[String] # Errors found in the context after parsing - var errors = new Array[String] + var context_errors = new Array[String] private var optmap = new HashMap[String, Option] # Add one or more options to the context - fun add_option(opts: Option...) do - options.add_all(opts) - end + fun add_option(opts: Option...) do options.add_all(opts) # Display all the options available fun usage @@ -277,9 +313,10 @@ class OptionContext end end - # Parse and assign options everywhere in the argument list - fun parse(argv: Collection[String]) + # Parse and assign options in `argv` or `args` + fun parse(argv: nullable Collection[String]) do + if argv == null then argv = args var it = argv.iterator parse_intern(it) end @@ -317,7 +354,7 @@ class OptionContext it.next next_called = true end - option.read_param(it) + option.read_param(self, it) end end if not next_called then it.next @@ -325,7 +362,7 @@ class OptionContext if optmap.has_key(str) then var opt = optmap[str] it.next - opt.read_param(it) + opt.read_param(self, it) else rest.add(it.item) it.next @@ -340,7 +377,7 @@ class OptionContext for opt in options do if opt.mandatory and not opt.read then - errors.add("Mandatory option {opt.names.join(", ")} not found.") + context_errors.add("Mandatory option {opt.names.join(", ")} not found.") end end end @@ -354,10 +391,11 @@ class OptionContext end end - fun get_errors: Array[String] + # Options parsing errors. + fun errors: Array[String] do var errors = new Array[String] - errors.add_all(errors) + errors.add_all context_errors for o in options do for e in o.errors do errors.add(e)