11cb0ef41Sopenharmony_civar balanced = require('balanced-match');
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_cimodule.exports = expandTop;
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_civar escSlash = '\0SLASH'+Math.random()+'\0';
61cb0ef41Sopenharmony_civar escOpen = '\0OPEN'+Math.random()+'\0';
71cb0ef41Sopenharmony_civar escClose = '\0CLOSE'+Math.random()+'\0';
81cb0ef41Sopenharmony_civar escComma = '\0COMMA'+Math.random()+'\0';
91cb0ef41Sopenharmony_civar escPeriod = '\0PERIOD'+Math.random()+'\0';
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_cifunction numeric(str) {
121cb0ef41Sopenharmony_ci  return parseInt(str, 10) == str
131cb0ef41Sopenharmony_ci    ? parseInt(str, 10)
141cb0ef41Sopenharmony_ci    : str.charCodeAt(0);
151cb0ef41Sopenharmony_ci}
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_cifunction escapeBraces(str) {
181cb0ef41Sopenharmony_ci  return str.split('\\\\').join(escSlash)
191cb0ef41Sopenharmony_ci            .split('\\{').join(escOpen)
201cb0ef41Sopenharmony_ci            .split('\\}').join(escClose)
211cb0ef41Sopenharmony_ci            .split('\\,').join(escComma)
221cb0ef41Sopenharmony_ci            .split('\\.').join(escPeriod);
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_cifunction unescapeBraces(str) {
261cb0ef41Sopenharmony_ci  return str.split(escSlash).join('\\')
271cb0ef41Sopenharmony_ci            .split(escOpen).join('{')
281cb0ef41Sopenharmony_ci            .split(escClose).join('}')
291cb0ef41Sopenharmony_ci            .split(escComma).join(',')
301cb0ef41Sopenharmony_ci            .split(escPeriod).join('.');
311cb0ef41Sopenharmony_ci}
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci// Basically just str.split(","), but handling cases
351cb0ef41Sopenharmony_ci// where we have nested braced sections, which should be
361cb0ef41Sopenharmony_ci// treated as individual members, like {a,{b,c},d}
371cb0ef41Sopenharmony_cifunction parseCommaParts(str) {
381cb0ef41Sopenharmony_ci  if (!str)
391cb0ef41Sopenharmony_ci    return [''];
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci  var parts = [];
421cb0ef41Sopenharmony_ci  var m = balanced('{', '}', str);
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci  if (!m)
451cb0ef41Sopenharmony_ci    return str.split(',');
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  var pre = m.pre;
481cb0ef41Sopenharmony_ci  var body = m.body;
491cb0ef41Sopenharmony_ci  var post = m.post;
501cb0ef41Sopenharmony_ci  var p = pre.split(',');
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  p[p.length-1] += '{' + body + '}';
531cb0ef41Sopenharmony_ci  var postParts = parseCommaParts(post);
541cb0ef41Sopenharmony_ci  if (post.length) {
551cb0ef41Sopenharmony_ci    p[p.length-1] += postParts.shift();
561cb0ef41Sopenharmony_ci    p.push.apply(p, postParts);
571cb0ef41Sopenharmony_ci  }
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci  parts.push.apply(parts, p);
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  return parts;
621cb0ef41Sopenharmony_ci}
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_cifunction expandTop(str) {
651cb0ef41Sopenharmony_ci  if (!str)
661cb0ef41Sopenharmony_ci    return [];
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  // I don't know why Bash 4.3 does this, but it does.
691cb0ef41Sopenharmony_ci  // Anything starting with {} will have the first two bytes preserved
701cb0ef41Sopenharmony_ci  // but *only* at the top level, so {},a}b will not expand to anything,
711cb0ef41Sopenharmony_ci  // but a{},b}c will be expanded to [a}c,abc].
721cb0ef41Sopenharmony_ci  // One could argue that this is a bug in Bash, but since the goal of
731cb0ef41Sopenharmony_ci  // this module is to match Bash's rules, we escape a leading {}
741cb0ef41Sopenharmony_ci  if (str.substr(0, 2) === '{}') {
751cb0ef41Sopenharmony_ci    str = '\\{\\}' + str.substr(2);
761cb0ef41Sopenharmony_ci  }
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  return expand(escapeBraces(str), true).map(unescapeBraces);
791cb0ef41Sopenharmony_ci}
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_cifunction embrace(str) {
821cb0ef41Sopenharmony_ci  return '{' + str + '}';
831cb0ef41Sopenharmony_ci}
841cb0ef41Sopenharmony_cifunction isPadded(el) {
851cb0ef41Sopenharmony_ci  return /^-?0\d/.test(el);
861cb0ef41Sopenharmony_ci}
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_cifunction lte(i, y) {
891cb0ef41Sopenharmony_ci  return i <= y;
901cb0ef41Sopenharmony_ci}
911cb0ef41Sopenharmony_cifunction gte(i, y) {
921cb0ef41Sopenharmony_ci  return i >= y;
931cb0ef41Sopenharmony_ci}
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_cifunction expand(str, isTop) {
961cb0ef41Sopenharmony_ci  var expansions = [];
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  var m = balanced('{', '}', str);
991cb0ef41Sopenharmony_ci  if (!m) return [str];
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  // no need to expand pre, since it is guaranteed to be free of brace-sets
1021cb0ef41Sopenharmony_ci  var pre = m.pre;
1031cb0ef41Sopenharmony_ci  var post = m.post.length
1041cb0ef41Sopenharmony_ci    ? expand(m.post, false)
1051cb0ef41Sopenharmony_ci    : [''];
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci  if (/\$$/.test(m.pre)) {
1081cb0ef41Sopenharmony_ci    for (var k = 0; k < post.length; k++) {
1091cb0ef41Sopenharmony_ci      var expansion = pre+ '{' + m.body + '}' + post[k];
1101cb0ef41Sopenharmony_ci      expansions.push(expansion);
1111cb0ef41Sopenharmony_ci    }
1121cb0ef41Sopenharmony_ci  } else {
1131cb0ef41Sopenharmony_ci    var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
1141cb0ef41Sopenharmony_ci    var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
1151cb0ef41Sopenharmony_ci    var isSequence = isNumericSequence || isAlphaSequence;
1161cb0ef41Sopenharmony_ci    var isOptions = m.body.indexOf(',') >= 0;
1171cb0ef41Sopenharmony_ci    if (!isSequence && !isOptions) {
1181cb0ef41Sopenharmony_ci      // {a},b}
1191cb0ef41Sopenharmony_ci      if (m.post.match(/,.*\}/)) {
1201cb0ef41Sopenharmony_ci        str = m.pre + '{' + m.body + escClose + m.post;
1211cb0ef41Sopenharmony_ci        return expand(str);
1221cb0ef41Sopenharmony_ci      }
1231cb0ef41Sopenharmony_ci      return [str];
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci    var n;
1271cb0ef41Sopenharmony_ci    if (isSequence) {
1281cb0ef41Sopenharmony_ci      n = m.body.split(/\.\./);
1291cb0ef41Sopenharmony_ci    } else {
1301cb0ef41Sopenharmony_ci      n = parseCommaParts(m.body);
1311cb0ef41Sopenharmony_ci      if (n.length === 1) {
1321cb0ef41Sopenharmony_ci        // x{{a,b}}y ==> x{a}y x{b}y
1331cb0ef41Sopenharmony_ci        n = expand(n[0], false).map(embrace);
1341cb0ef41Sopenharmony_ci        if (n.length === 1) {
1351cb0ef41Sopenharmony_ci          return post.map(function(p) {
1361cb0ef41Sopenharmony_ci            return m.pre + n[0] + p;
1371cb0ef41Sopenharmony_ci          });
1381cb0ef41Sopenharmony_ci        }
1391cb0ef41Sopenharmony_ci      }
1401cb0ef41Sopenharmony_ci    }
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci    // at this point, n is the parts, and we know it's not a comma set
1431cb0ef41Sopenharmony_ci    // with a single entry.
1441cb0ef41Sopenharmony_ci    var N;
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci    if (isSequence) {
1471cb0ef41Sopenharmony_ci      var x = numeric(n[0]);
1481cb0ef41Sopenharmony_ci      var y = numeric(n[1]);
1491cb0ef41Sopenharmony_ci      var width = Math.max(n[0].length, n[1].length)
1501cb0ef41Sopenharmony_ci      var incr = n.length == 3
1511cb0ef41Sopenharmony_ci        ? Math.abs(numeric(n[2]))
1521cb0ef41Sopenharmony_ci        : 1;
1531cb0ef41Sopenharmony_ci      var test = lte;
1541cb0ef41Sopenharmony_ci      var reverse = y < x;
1551cb0ef41Sopenharmony_ci      if (reverse) {
1561cb0ef41Sopenharmony_ci        incr *= -1;
1571cb0ef41Sopenharmony_ci        test = gte;
1581cb0ef41Sopenharmony_ci      }
1591cb0ef41Sopenharmony_ci      var pad = n.some(isPadded);
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci      N = [];
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci      for (var i = x; test(i, y); i += incr) {
1641cb0ef41Sopenharmony_ci        var c;
1651cb0ef41Sopenharmony_ci        if (isAlphaSequence) {
1661cb0ef41Sopenharmony_ci          c = String.fromCharCode(i);
1671cb0ef41Sopenharmony_ci          if (c === '\\')
1681cb0ef41Sopenharmony_ci            c = '';
1691cb0ef41Sopenharmony_ci        } else {
1701cb0ef41Sopenharmony_ci          c = String(i);
1711cb0ef41Sopenharmony_ci          if (pad) {
1721cb0ef41Sopenharmony_ci            var need = width - c.length;
1731cb0ef41Sopenharmony_ci            if (need > 0) {
1741cb0ef41Sopenharmony_ci              var z = new Array(need + 1).join('0');
1751cb0ef41Sopenharmony_ci              if (i < 0)
1761cb0ef41Sopenharmony_ci                c = '-' + z + c.slice(1);
1771cb0ef41Sopenharmony_ci              else
1781cb0ef41Sopenharmony_ci                c = z + c;
1791cb0ef41Sopenharmony_ci            }
1801cb0ef41Sopenharmony_ci          }
1811cb0ef41Sopenharmony_ci        }
1821cb0ef41Sopenharmony_ci        N.push(c);
1831cb0ef41Sopenharmony_ci      }
1841cb0ef41Sopenharmony_ci    } else {
1851cb0ef41Sopenharmony_ci      N = [];
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci      for (var j = 0; j < n.length; j++) {
1881cb0ef41Sopenharmony_ci        N.push.apply(N, expand(n[j], false));
1891cb0ef41Sopenharmony_ci      }
1901cb0ef41Sopenharmony_ci    }
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci    for (var j = 0; j < N.length; j++) {
1931cb0ef41Sopenharmony_ci      for (var k = 0; k < post.length; k++) {
1941cb0ef41Sopenharmony_ci        var expansion = pre + N[j] + post[k];
1951cb0ef41Sopenharmony_ci        if (!isTop || isSequence || expansion)
1961cb0ef41Sopenharmony_ci          expansions.push(expansion);
1971cb0ef41Sopenharmony_ci      }
1981cb0ef41Sopenharmony_ci    }
1991cb0ef41Sopenharmony_ci  }
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci  return expansions;
2021cb0ef41Sopenharmony_ci}
2031cb0ef41Sopenharmony_ci
204