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