opusdev/vector-similarity-api
1
1var concatMap = require('concat-map');2var balanced = require('balanced-match');3 4module.exports = expandTop;5 6var escSlash = '\0SLASH'+Math.random()+'\0';7var escOpen = '\0OPEN'+Math.random()+'\0';8var escClose = '\0CLOSE'+Math.random()+'\0';9var escComma = '\0COMMA'+Math.random()+'\0';10var escPeriod = '\0PERIOD'+Math.random()+'\0';11 12function numeric(str) {13 return parseInt(str, 10) == str14 ? parseInt(str, 10)15 : str.charCodeAt(0);16}17 18function escapeBraces(str) {19 return str.split('\\\\').join(escSlash)20 .split('\\{').join(escOpen)21 .split('\\}').join(escClose)22 .split('\\,').join(escComma)23 .split('\\.').join(escPeriod);24}25 26function unescapeBraces(str) {27 return str.split(escSlash).join('\\')28 .split(escOpen).join('{')29 .split(escClose).join('}')30 .split(escComma).join(',')31 .split(escPeriod).join('.');32}33 34 35// Basically just str.split(","), but handling cases36// where we have nested braced sections, which should be37// treated as individual members, like {a,{b,c},d}38function parseCommaParts(str) {39 if (!str)40 return [''];41 42 var parts = [];43 var m = balanced('{', '}', str);44 45 if (!m)46 return str.split(',');47 48 var pre = m.pre;49 var body = m.body;50 var post = m.post;51 var p = pre.split(',');52 53 p[p.length-1] += '{' + body + '}';54 var postParts = parseCommaParts(post);55 if (post.length) {56 p[p.length-1] += postParts.shift();57 p.push.apply(p, postParts);58 }59 60 parts.push.apply(parts, p);61 62 return parts;63}64 65function expandTop(str) {66 if (!str)67 return [];68 69 // I don't know why Bash 4.3 does this, but it does.70 // Anything starting with {} will have the first two bytes preserved71 // but *only* at the top level, so {},a}b will not expand to anything,72 // but a{},b}c will be expanded to [a}c,abc].73 // One could argue that this is a bug in Bash, but since the goal of74 // this module is to match Bash's rules, we escape a leading {}75 if (str.substr(0, 2) === '{}') {76 str = '\\{\\}' + str.substr(2);77 }78 79 return expand(escapeBraces(str), true).map(unescapeBraces);80}81 82function identity(e) {83 return e;84}85 86function embrace(str) {87 return '{' + str + '}';88}89function isPadded(el) {90 return /^-?0\d/.test(el);91}92 93function lte(i, y) {94 return i <= y;95}96function gte(i, y) {97 return i >= y;98}99 100function expand(str, isTop) {101 var expansions = [];102 103 var m = balanced('{', '}', str);104 if (!m || /\$$/.test(m.pre)) return [str];105 106 var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);107 var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);108 var isSequence = isNumericSequence || isAlphaSequence;109 var isOptions = m.body.indexOf(',') >= 0;110 if (!isSequence && !isOptions) {111 // {a},b}112 if (m.post.match(/,(?!,).*\}/)) {113 str = m.pre + '{' + m.body + escClose + m.post;114 return expand(str);115 }116 return [str];117 }118 119 var n;120 if (isSequence) {121 n = m.body.split(/\.\./);122 } else {123 n = parseCommaParts(m.body);124 if (n.length === 1) {125 // x{{a,b}}y ==> x{a}y x{b}y126 n = expand(n[0], false).map(embrace);127 if (n.length === 1) {128 var post = m.post.length129 ? expand(m.post, false)130 : [''];131 return post.map(function(p) {132 return m.pre + n[0] + p;133 });134 }135 }136 }137 138 // at this point, n is the parts, and we know it's not a comma set139 // with a single entry.140 141 // no need to expand pre, since it is guaranteed to be free of brace-sets142 var pre = m.pre;143 var post = m.post.length144 ? expand(m.post, false)145 : [''];146 147 var N;148 149 if (isSequence) {150 var x = numeric(n[0]);151 var y = numeric(n[1]);152 var width = Math.max(n[0].length, n[1].length)153 var incr = n.length == 3154 ? Math.abs(numeric(n[2]))155 : 1;156 var test = lte;157 var reverse = y < x;158 if (reverse) {159 incr *= -1;160 test = gte;161 }162 var pad = n.some(isPadded);163 164 N = [];165 166 for (var i = x; test(i, y); i += incr) {167 var c;168 if (isAlphaSequence) {169 c = String.fromCharCode(i);170 if (c === '\\')171 c = '';172 } else {173 c = String(i);174 if (pad) {175 var need = width - c.length;176 if (need > 0) {177 var z = new Array(need + 1).join('0');178 if (i < 0)179 c = '-' + z + c.slice(1);180 else181 c = z + c;182 }183 }184 }185 N.push(c);186 }187 } else {188 N = concatMap(n, function(el) { return expand(el, false) });189 }190 191 for (var j = 0; j < N.length; j++) {192 for (var k = 0; k < post.length; k++) {193 var expansion = pre + N[j] + post[k];194 if (!isTop || isSequence || expansion)195 expansions.push(expansion);196 }197 }198 199 return expansions;200}201 202 