# File lib/sass/tree/visitors/perform.rb, line 11
  def self.perform_arguments(callable, args, keywords, splat)
    desc = "#{callable.type.capitalize} #{callable.name}"
    downcase_desc = "#{callable.type} #{callable.name}"

    begin
      unless keywords.empty?
        unknown_args = Sass::Util.array_minus(keywords.keys,
          callable.args.map {|var| var.first.underscored_name})
        if callable.splat && unknown_args.include?(callable.splat.underscored_name)
          raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} cannot be used as a named argument.")
        elsif unknown_args.any?
          description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
          raise Sass::SyntaxError.new("#{desc} doesn't have #{description} #{unknown_args.map {|name| "$#{name}"}.join ', '}.")
        end
      end
    rescue Sass::SyntaxError => keyword_exception
    end

    # If there's no splat, raise the keyword exception immediately. The actual
    # raising happens in the ensure clause at the end of this function.
    return if keyword_exception && !callable.splat

    if args.size > callable.args.size && !callable.splat
      takes = callable.args.size
      passed = args.size
      raise Sass::SyntaxError.new(
        "#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
        "but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
    end

    splat_sep = :comma
    if splat
      args += splat.to_a
      splat_sep = splat.separator if splat.is_a?(Sass::Script::List)
      # If the splat argument exists, there won't be any keywords passed in
      # manually, so we can safely overwrite rather than merge here.
      keywords = splat.keywords if splat.is_a?(Sass::Script::ArgList)
    end

    keywords = keywords.dup
    env = Sass::Environment.new(callable.environment)
    callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
      if value && keywords.include?(var.underscored_name)
        raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} both by position and by name.")
      end

      value ||= keywords.delete(var.underscored_name)
      value ||= default && default.perform(env)
      raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
      env.set_local_var(var.name, value)
    end

    if callable.splat
      rest = args[callable.args.length..-1]
      arg_list = Sass::Script::ArgList.new(rest, keywords.dup, splat_sep)
      arg_list.options = env.options
      env.set_local_var(callable.splat.name, arg_list)
    end

    yield env
  rescue Exception => e
  ensure
    # If there's a keyword exception, we don't want to throw it immediately,
    # because the invalid keywords may be part of a glob argument that should be
    # passed on to another function. So we only raise it if we reach the end of
    # this function *and* the keywords attached to the argument list glob object
    # haven't been accessed.
    #
    # The keyword exception takes precedence over any Sass errors, but not over
    # non-Sass exceptions.
    if keyword_exception &&
        !(arg_list && arg_list.keywords_accessed) &&
        (e.nil? || e.is_a?(Sass::SyntaxError))
      raise keyword_exception
    elsif e
      raise e
    end
  end