function braces(str, arr, options) {
  if (str === '') {
    return [];
  }

  if (!Array.isArray(arr)) {
    options = arr;
    arr = [];
  }

  var opts = options || {};
  arr = arr || [];

  if (typeof opts.nodupes === 'undefined') {
    opts.nodupes = true;
  }

  var fn = opts.fn;
  var es6;

  if (typeof opts === 'function') {
    fn = opts;
    opts = {};
  }

  if (!(patternRe instanceof RegExp)) {
    patternRe = patternRegex();
  }

  var matches = str.match(patternRe) || [];
  var m = matches[0];

  switch (m) {
    case '\\,':
      return escapeCommas(str, arr, opts);

    case '\\.':
      return escapeDots(str, arr, opts);

    case '\/.':
      return escapePaths(str, arr, opts);

    case ' ':
      return splitWhitespace(str);

    case '{,}':
      return exponential(str, opts, braces);

    case '{}':
      return emptyBraces(str, arr, opts);

    case '\\{':
    case '\\}':
      return escapeBraces(str, arr, opts);

    case '${':
      if (!/\{[^{]+\{/.test(str)) {
        return arr.concat(str);
      } else {
        es6 = true;
        str = tokens.before(str, es6Regex());
      }

  }

  if (!(braceRe instanceof RegExp)) {
    braceRe = braceRegex();
  }

  var match = braceRe.exec(str);

  if (match == null) {
    return [str];
  }

  var outter = match[1];
  var inner = match[2];

  if (inner === '') {
    return [str];
  }

  var segs, segsLength;

  if (inner.indexOf('..') !== -1) {
    segs = expand(inner, opts, fn) || inner.split(',');
    segsLength = segs.length;
  } else if (inner[0] === '"' || inner[0] === '\'') {
    return arr.concat(str.split(/['"]/).join(''));
  } else {
    segs = inner.split(',');

    if (opts.makeRe) {
      return braces(str.replace(outter, wrap(segs, '|')), opts);
    }

    segsLength = segs.length;

    if (segsLength === 1 && opts.bash) {
      segs[0] = wrap(segs[0], '\\');
    }
  }

  var len = segs.length;
  var i = 0,
      val;

  while (len--) {
    var path = segs[i++];

    if (/(\.[^.\/])/.test(path)) {
      if (segsLength > 1) {
        return segs;
      } else {
        return [str];
      }
    }

    val = splice(str, outter, path);

    if (/\{[^{}]+?\}/.test(val)) {
      arr = braces(val, arr, opts);
    } else if (val !== '') {
      if (opts.nodupes && arr.indexOf(val) !== -1) {
        continue;
      }

      arr.push(es6 ? tokens.after(val) : val);
    }
  }

  if (opts.strict) {
    return filter(arr, filterEmpty);
  }

  return arr;
}
Expand `{foo,bar}` or `{1..5}` braces in the
given `string`.

@param  {String} `str`
@param  {Array} `arr`
@param  {Object} `options`
@return {Array}