# File lib/trollop.rb, line 235
  def parse(cmdline = ARGV)
    vals = {}
    required = {}

    opt :version, "Print version and exit" if @version && ! (@specs[:version] || @long["version"])
    opt :help, "Show this message" unless @specs[:help] || @long["help"]

    @specs.each do |sym, opts|
      required[sym] = true if opts.required?
      vals[sym] = opts.default
      vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil
    end

    resolve_default_short_options!

    ## resolve symbols
    given_args = {}
    @leftovers = each_arg cmdline do |arg, params|
      ## handle --no- forms
      arg, negative_given = if arg =~ /^--no-([^-]\S*)$/
        ["--#{$1}", true]
      else
        [arg, false]
      end

      sym = case arg
        when /^-([^-])$/      then @short[$1]
        when /^--([^-]\S*)$/  then @long[$1] || @long["no-#{$1}"]
        else                       raise CommandlineError, "invalid argument syntax: '#{arg}'"
      end

      sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments

      next nil if ignore_invalid_options && !sym
      raise CommandlineError, "unknown argument '#{arg}'" unless sym

      if given_args.include?(sym) && !@specs[sym].multi?
        raise CommandlineError, "option '#{arg}' specified multiple times"
      end

      given_args[sym] ||= {}
      given_args[sym][:arg] = arg
      given_args[sym][:negative_given] = negative_given
      given_args[sym][:params] ||= []

      # The block returns the number of parameters taken.
      num_params_taken = 0

      unless params.empty?
        if @specs[sym].single_arg?
          given_args[sym][:params] << params[0, 1]  # take the first parameter
          num_params_taken = 1
        elsif @specs[sym].multi_arg?
          given_args[sym][:params] << params        # take all the parameters
          num_params_taken = params.size
        end
      end

      num_params_taken
    end

    ## check for version and help args
    raise VersionNeeded if given_args.include? :version
    raise HelpNeeded if given_args.include? :help

    ## check constraint satisfaction
    @constraints.each do |type, syms|
      constraint_sym = syms.find { |sym| given_args[sym] }
      next unless constraint_sym

      case type
      when :depends
        syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}" unless given_args.include? sym }
      when :conflicts
        syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}" if given_args.include?(sym) && (sym != constraint_sym) }
      end
    end

    required.each do |sym, val|
      raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym
    end

    ## parse parameters
    given_args.each do |sym, given_data|
      arg, params, negative_given = given_data.values_at :arg, :params, :negative_given

      opts = @specs[sym]
      if params.empty? && !opts.flag?
        raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
        params << (opts.array_default? ? opts.default.clone : [opts.default])
      end

      vals["#{sym}_given".intern] = true # mark argument as specified on the commandline

      vals[sym] = opts.parse(params, negative_given)

      if opts.single_arg?
        if opts.multi?        # multiple options, each with a single parameter
          vals[sym] = vals[sym].map { |p| p[0] }
        else                  # single parameter
          vals[sym] = vals[sym][0][0]
        end
      elsif opts.multi_arg? && !opts.multi?
        vals[sym] = vals[sym][0]  # single option, with multiple parameters
      end
      # else: multiple options, with multiple parameters

      opts.callback.call(vals[sym]) if opts.callback
    end

    ## modify input in place with only those
    ## arguments we didn't process
    cmdline.clear
    @leftovers.each { |l| cmdline << l }

    ## allow openstruct-style accessors
    class << vals
      def method_missing(m, *_args)
        self[m] || self[m.to_s]
      end
    end
    vals
  end