diff --git a/index.js b/index.js index ad6a0fa..6469014 100644 --- a/index.js +++ b/index.js @@ -20,7 +20,27 @@ var CONTROL = '(?:' + [ ].join('|') + ')'; var META = '|&;()<> \\t'; -exports.parse = function parse (s, env) { +var TOKEN = ''; +for (var i = 0; i < 4; i++) { + TOKEN += (Math.pow(16,8)*Math.random()).toString(16); +} + +exports.parse = function (s, env) { + var mapped = parse(s, env); + if (typeof env !== 'function') return mapped; + return mapped.reduce(function (acc, s) { + var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g')); + if (xs.length === 1) return acc.concat(xs[0]); + return acc.concat(xs.filter(Boolean).map(function (x) { + if (RegExp('^' + TOKEN).test(x)) { + return JSON.parse(x.split(TOKEN)[1]); + } + else return x; + })); + }, []); +}; + +function parse (s, env) { var chunker = new RegExp([ '([\'"])((\\\\\\1|[^\\1])*?)\\1', // quotes '(\\\\[' + META + ']|[^\\s' + META + '])+', // barewords @@ -29,7 +49,7 @@ exports.parse = function parse (s, env) { var match = s.match(chunker); if (!match) return []; if (!env) env = {}; - return [].concat.apply([], match.map(function (s) { + return match.map(function (s) { if (/^'/.test(s)) { return s .replace(/^'|'$/g, '') @@ -37,41 +57,11 @@ exports.parse = function parse (s, env) { ; } else if (/^"/.test(s) && typeof env === 'function') { - var res = []; - s = s.replace(/^"|"$/g, ''); - - var begin = 0, bracket = false; - for (var i = 0; i < s.length; i++) { - if (s.charAt(i) !== '$' || s.charAt(i-1) === '\\') continue; - if (s.charAt(i+1) === '{') { - i ++; - bracket = true; - } - if (/^[*@#?$!0_-]$/.test(s.charAt(i+1)) - && (!bracket || (bracket && s.charAt(i+2) === '}'))) { - res.push(s.slice(begin, i)); - var r = env(s.charAt(i+1)); - if (typeof r === 'object') res.push(r); - else res[res.length-1] += r; - i ++; - if (bracket) i++; - begin = i + 1; - continue; - } - for (var j=i+1; j < s.length && /\w/.test(s.charAt(j)); j++); - if (j-(i+1) > 1) { - res.push(s.slice(begin, i)); - var r = env(s.slice(i+1, j)); - if (typeof r === 'object') res.push(r); - else res[res.length-1] += r; - begin = j; - } - } - res.push(s.slice(begin)); - return res.map(function (c) { - if (typeof c === 'object') return c; - return c.replace(/\\([ "'\\$`(){}!#&*|])/g, '$1'); - }); + return s.replace(/^"|"$/g, '') + .replace(/(^|[^\\])\$(\w+|[*@#?$!0_-])/g, getVar) + .replace(/(^|[^\\])\${(\w+|[*@#?$!0_-])}/g, getVar) + .replace(/\\([ "'\\$`(){}!#&*|])/g, '$1') + ; } else if (/^"/.test(s)) { return s.replace(/^"|"$/g, '') @@ -90,9 +80,15 @@ exports.parse = function parse (s, env) { return parse('"' + s + '"', env); } ); - })); + }); function getVar (_, pre, key) { - return pre + String(env[key] || ''); + var r = typeof env === 'function' ? env(key) : env[key]; + if (r === undefined) r = ''; + + if (typeof r === 'object') { + return pre + TOKEN + JSON.stringify(r) + TOKEN; + } + else return pre + r; } };