X-Git-Url: http://nitlanguage.org diff --git a/lib/opts.nit b/lib/opts.nit index 5f409e8..1ac2326 100644 --- a/lib/opts.nit +++ b/lib/opts.nit @@ -11,28 +11,37 @@ # You are allowed to redistribute it and sell it, alone or is a part of # another product. +# Management of options on the command line +module opts + # Super class of all option's class -class Option +abstract class Option # Names for the option (including long and short ones) - readable attr _names: Array[String] + readable var _names: Array[String] # Type of the value of the option type VALUE: nullable Object # Human readable description of the option - readable attr _helptext: String + readable var _helptext: String + + # Gathering errors during parsing + readable var _errors: Array[String] # Is this option mandatory? - readable writable attr _mandatory: Bool + readable writable var _mandatory: Bool + + # Has this option been read? + readable var _read:Bool # Current value of this option - writable attr _value: nullable VALUE + writable var _value: nullable VALUE # Current value of this option - meth value: VALUE do return _value.as(VALUE) + fun value: VALUE do return _value.as(VALUE) # Default value of this option - readable writable attr _default_value: nullable VALUE + readable writable var _default_value: nullable VALUE # Create a new option init init_opt(help: String, default: nullable VALUE, names: nullable Array[String]) @@ -44,18 +53,20 @@ class Option end _helptext = help _mandatory = false + _read = false _default_value = default _value = default + _errors = new Array[String] end # Add new aliases for this option - meth add_aliases(names: String...) do _names.add_all(names) + fun add_aliases(names: String...) do _names.add_all(names) # An help text for this option with default settings - redef meth to_s do return pretty(2) + redef fun to_s do return pretty(2) # A pretty print for this help - meth pretty(off: Int): String + fun pretty(off: Int): String do var text = new Buffer.from(" ") text.append(_names.join(", ")) @@ -67,94 +78,121 @@ class Option return text.to_s end - meth pretty_default: String + fun pretty_default: String do - if default_value != null then - return " ({default_value})" - end + var dv = default_value + if dv != null then return " ({dv})" return "" end # Consume parameters for this option - protected meth read_param(it: Iterator[String]) is abstract + protected fun read_param(it: Iterator[String]) + do + _read = true + end end class OptionText -special Option + super Option init(text: String) do init_opt(text, null, null) - redef meth pretty(off) do return to_s + redef fun pretty(off) do return to_s - redef meth to_s do return helptext + redef fun to_s do return helptext end class OptionBool -special Option + super Option redef type VALUE: Bool init(help: String, names: String...) do init_opt(help, false, names) - redef meth read_param(it) do value = true + redef fun read_param(it) + do + super + value = true + end end class OptionCount -special Option + super Option redef type VALUE: Int init(help: String, names: String...) do init_opt(help, 0, names) - redef meth read_param(it) do value += 1 + redef fun read_param(it) + do + super + value += 1 + end end -# Option with one mandatory parameter -class OptionParameter -special Option - protected meth convert(str: String): VALUE is abstract +# Option with one parameter (mandatory by default) +abstract class OptionParameter + super Option + protected fun convert(str: String): VALUE is abstract + + # Is the parameter mandatory? + readable writable var _parameter_mandatory: Bool - redef meth read_param(it) + redef fun read_param(it) do - if it.is_ok then + super + if it.is_ok and it.item.first != '-' then value = convert(it.item) it.next else - # TODO: What to do? + if _parameter_mandatory then + _errors.add("Parameter expected for option {names.first}.") + end end end - init init_opt(h, d, n) do super + init init_opt(h, d, n) + do + super + _parameter_mandatory = true + end end class OptionString -special OptionParameter + super OptionParameter redef type VALUE: nullable String init(help: String, names: String...) do init_opt(help, null, names) - redef meth convert(str) do return str + redef fun convert(str) do return str end class OptionEnum -special OptionParameter + super OptionParameter redef type VALUE: Int - attr _enum: Array[String] + var _values: Array[String] - init(enum: Array[String], help: String, default: Int, names: String...) + init(values: Array[String], help: String, default: Int, names: String...) do - assert enum.length > 0 - _enum = enum.to_a - init_opt("{help} <{enum.join(", ")}>", default, names) + assert values.length > 0 + _values = values.to_a + init_opt("{help} <{values.join(", ")}>", default, names) end - redef meth convert(str) + redef fun convert(str) do - var id = _enum.index_of(str) + var id = _values.index_of(str) + if id == -1 then + var e = "Unrecognized value for option {_names.join(", ")}.\n" + e += "Expected values are: {_values.join(", ")}." + _errors.add(e) + end return id end - redef meth pretty_default + fun value_name: String = _values[value] + + redef fun pretty_default do if default_value != null then - return " ({_enum[default_value.as(not null)]})" + return " ({_values[default_value.as(not null)]})" else return "" end @@ -162,16 +200,16 @@ special OptionParameter end class OptionInt -special OptionParameter + super OptionParameter redef type VALUE: Int init(help: String, default: Int, names: String...) do init_opt(help, default, names) - redef meth convert(str) do return str.to_i + redef fun convert(str) do return str.to_i end class OptionArray -special OptionParameter + super OptionParameter redef type VALUE: Array[String] init(help: String, names: String...) @@ -180,8 +218,8 @@ special OptionParameter init_opt(help, _values, names) end - attr _values: Array[String] - redef meth convert(str) + var _values: Array[String] + redef fun convert(str) do _values.add(str) return _values @@ -189,12 +227,13 @@ special OptionParameter end class OptionContext - readable attr _options: Array[Option] - readable attr _rest: Array[String] + readable var _options: Array[Option] + readable var _rest: Array[String] + readable var _errors: Array[String] - attr _optmap: Map[String, Option] + var _optmap: Map[String, Option] - meth usage + fun usage do var lmax = 1 for i in _options do @@ -211,18 +250,18 @@ class OptionContext end # Parse ans assign options everywhere is the argument list - meth parse(argv: Collection[String]) + fun parse(argv: Collection[String]) do var it = argv.iterator parse_intern(it) end - protected meth parse_intern(it: Iterator[String]) + protected fun parse_intern(it: Iterator[String]) do var parseargs = true build var rest = _rest - + while parseargs and it.is_ok do var str = it.item if str == "--" then @@ -230,19 +269,42 @@ class OptionContext rest.add_all(it.to_a) parseargs = false else - if _optmap.has_key(str) then - var opt = _optmap[str] - it.next - opt.read_param(it) + # We're looking for packed short options + if str.last_index_of('-') == 0 and str.length > 2 then + var next_called = false + for i in [1..str.length] do + var short_opt = "-" + str[i].to_s + if _optmap.has_key(short_opt) then + var option = _optmap[short_opt] + if option isa OptionParameter then + it.next + next_called = true + end + option.read_param(it) + end + end + if not next_called then it.next else - rest.add(it.item) - it.next + if _optmap.has_key(str) then + var opt = _optmap[str] + it.next + opt.read_param(it) + else + rest.add(it.item) + it.next + end end end end + + for opt in _options do + if opt.mandatory and not opt.read then + _errors.add("Mandatory option {opt.names.join(", ")} not found.") + end + end end - meth add_option(opts: Option...) + fun add_option(opts: Option...) do for opt in opts do _options.add(opt) @@ -254,9 +316,10 @@ class OptionContext _options = new Array[Option] _optmap = new HashMap[String, Option] _rest = new Array[String] + _errors = new Array[String] end - private meth build + private fun build do for o in _options do for n in o.names do @@ -264,4 +327,19 @@ class OptionContext end end end + + fun get_errors: Array[String] + do + var errors: Array[String] = new Array[String] + + errors.add_all(_errors) + + for o in _options do + for e in o.errors do + errors.add(e) + end + end + + return errors + end end