def opt(name, desc = "", opts = {}, &b)
    raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name

    ## fill in :type
    opts[:type] = # normalize
      case opts[:type]
      when :boolean, :bool then :flag
      when :integer        then :int
      when :integers       then :ints
      when :double         then :float
      when :doubles        then :floats
      when Class
        case opts[:type].name
        when 'TrueClass',
             'FalseClass'  then :flag
        when 'String'      then :string
        when 'Integer'     then :int
        when 'Float'       then :float
        when 'IO'          then :io
        when 'Date'        then :date
        else
          raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
        end
      when nil             then nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
        opts[:type]
      end

    ## for options with :multi => true, an array default doesn't imply
    ## a multi-valued argument. for that you have to specify a :type
    ## as well. (this is how we disambiguate an ambiguous situation;
    ## see the docs for Parser#opt for details.)
    disambiguated_default = if opts[:multi] && opts[:default].kind_of?(Array) && !opts[:type]
      opts[:default].first
    else
      opts[:default]
    end

    type_from_default =
      case disambiguated_default
      when Integer     then :int
      when Numeric     then :float
      when TrueClass,
           FalseClass  then :flag
      when String      then :string
      when IO          then :io
      when Date        then :date
      when Array
        if opts[:default].empty?
          if opts[:type]
            raise ArgumentError, "multiple argument type must be plural" unless MULTI_ARG_TYPES.include?(opts[:type])
            nil
          else
            raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
          end
        else
          case opts[:default][0]    # the first element determines the types
          when Integer then :ints
          when Numeric then :floats
          when String  then :strings
          when IO      then :ios
          when Date    then :dates
          else
            raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
          end
        end
      when nil         then nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
      end

    raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default

    opts[:type] = opts[:type] || type_from_default || :flag

    ## fill in :long
    opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
    opts[:long] = case opts[:long]
      when /^--([^-].*)$/ then $1
      when /^[^-]/        then opts[:long]
      else                     raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
    end
    raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]

    ## fill in :short
    opts[:short] = opts[:short].to_s if opts[:short] && opts[:short] != :none
    opts[:short] = case opts[:short]
      when /^-(.)$/          then $1
      when nil, :none, /^.$/ then opts[:short]
      else                   raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
    end

    if opts[:short]
      raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
      raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
    end

    ## fill in :default for flags
    opts[:default] = false if opts[:type] == :flag && opts[:default].nil?

    ## autobox :default for :multi (multi-occurrence) arguments
    opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].kind_of?(Array)

    ## fill in :multi
    opts[:multi] ||= false
    opts[:callback] ||= b if block_given?
    opts[:desc] ||= desc
    @long[opts[:long]] = name
    @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
    @specs[name] = opts
    @order << [:opt, name]
  end
def opt(name, desc = "", opts = {}, &b)
    raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name

    ## fill in :type
    opts[:type] = # normalize
      case opts[:type]
      when :boolean, :bool then :flag
      when :integer        then :int
      when :integers       then :ints
      when :double         then :float
      when :doubles        then :floats
      when Class
        case opts[:type].name
        when 'TrueClass',
             'FalseClass'  then :flag
        when 'String'      then :string
        when 'Integer'     then :int
        when 'Float'       then :float
        when 'IO'          then :io
        when 'Date'        then :date
        else
          raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
        end
      when nil             then nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
        opts[:type]
      end

    ## for options with :multi => true, an array default doesn't imply
    ## a multi-valued argument. for that you have to specify a :type
    ## as well. (this is how we disambiguate an ambiguous situation;
    ## see the docs for Parser#opt for details.)
    disambiguated_default = if opts[:multi] && opts[:default].kind_of?(Array) && !opts[:type]
      opts[:default].first
    else
      opts[:default]
    end

    type_from_default =
      case disambiguated_default
      when Integer     then :int
      when Numeric     then :float
      when TrueClass,
           FalseClass  then :flag
      when String      then :string
      when IO          then :io
      when Date        then :date
      when Array
        if opts[:default].empty?
          if opts[:type]
            raise ArgumentError, "multiple argument type must be plural" unless MULTI_ARG_TYPES.include?(opts[:type])
            nil
          else
            raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
          end
        else
          case opts[:default][0]    # the first element determines the types
          when Integer then :ints
          when Numeric then :floats
          when String  then :strings
          when IO      then :ios
          when Date    then :dates
          else
            raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
          end
        end
      when nil         then nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
      end

    raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default

    opts[:type] = opts[:type] || type_from_default || :flag

    ## fill in :long
    opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
    opts[:long] = case opts[:long]
      when /^--([^-].*)$/ then $1
      when /^[^-]/        then opts[:long]
      else                     raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
    end
    raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]

    ## fill in :short
    opts[:short] = opts[:short].to_s if opts[:short] && opts[:short] != :none
    opts[:short] = case opts[:short]
      when /^-(.)$/          then $1
      when nil, :none, /^.$/ then opts[:short]
      else                   raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
    end

    if opts[:short]
      raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
      raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
    end

    ## fill in :default for flags
    opts[:default] = false if opts[:type] == :flag && opts[:default].nil?

    ## autobox :default for :multi (multi-occurrence) arguments
    opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].kind_of?(Array)

    ## fill in :multi
    opts[:multi] ||= false
    opts[:callback] ||= b if block_given?
    opts[:desc] ||= desc
    @long[opts[:long]] = name
    @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
    @specs[name] = opts
    @order << [:opt, name]
  end
Initializes the parser, and instance-evaluates any block given.
 Define an option. +name+ is the option name, a unique identifier
 for the option that you will use internally, which should be a
 symbol or a string. +desc+ is a string description which will be
 displayed in help messages.

 Takes the following optional arguments:

 [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
 [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value.
 [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
 [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
 [+:required+] If set to +true+, the argument must be provided on the commandline.
 [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)

 Note that there are two types of argument multiplicity: an argument
 can take multiple values, e.g. "--arg 1 2 3". An argument can also
 be allowed to occur multiple times, e.g. "--arg 1 --arg 2".

 Arguments that take multiple values should have a +:type+ parameter
 drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
 value of an array of the correct type (e.g. [String]). The
 value of this argument will be an array of the parameters on the
 commandline.

 Arguments that can occur multiple times should be marked with
 +:multi+ => +true+. The value of this argument will also be an array.
 In contrast with regular non-multi options, if not specified on
 the commandline, the default value will be [], not nil.

 These two attributes can be combined (e.g. +:type+ => +:strings+,
 +:multi+ => +true+), in which case the value of the argument will be
 an array of arrays.

 There's one ambiguous case to be aware of: when +:multi+: is true and a
 +:default+ is set to an array (of something), it's ambiguous whether this
 is a multi-value argument as well as a multi-occurrence argument.
 In thise case, Trollop assumes that it's not a multi-value argument.
 If you want a multi-value, multi-occurrence argument with a default
 value, you must specify +:type+ as well.