# File lib/trollop.rb, line 107
  def opt name, desc="", opts={}
    raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name

    ## fill in :type
    opts[:type] = 
      case opts[:type]
      when :flag, :boolean, :bool; :flag
      when :int, :integer; :int
      when :ints, :integers; :ints
      when :string; :string
      when :strings; :strings
      when :double, :float; :float
      when :doubles, :floats; :floats
      when Class
        case opts[:type].to_s # sigh... there must be a better way to do this
        when 'TrueClass', 'FalseClass'; :flag
        when 'String'; :string
        when 'Integer'; :int
        when 'Float'; :float
        else
          raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
        end
      when nil; nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
      end

    type_from_default =
      case opts[:default]
      when Integer; :int
      when Numeric; :float
      when TrueClass, FalseClass; :flag
      when String; :string
      when Array
        if opts[:default].empty?
          raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
        end
        case opts[:default][0]    # the first element determines the types
        when Integer; :ints
        when Numeric; :floats
        when String; :strings
        else
          raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
        end
      when nil; nil
      else
        raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
      end

    raise ArgumentError, ":type specification and default type don't match" 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 /^--([^-].*)$/
        $1
      when /^[^-]/
        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] unless opts[:short] == :none
    opts[:short] =
      case opts[:short]
      when nil
        c = opts[:long].split(//).find { |c| c !~ INVALID_SHORT_ARG_REGEX && !@short.member?(c) }
        raise ArgumentError, "can't generate a short option name for #{opts[:long].inspect}: out of unique characters" unless c
        c
      when /^-(.)$/
        $1
      when /^.$/
        opts[:short]
      when :none
        nil
      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?

    ## fill in :multi
    opts[:multi] ||= false

    opts[:desc] ||= desc
    @long[opts[:long]] = name
    @short[opts[:short]] = name if opts[:short]
    @specs[name] = opts
    @order << [:opt, name]
  end