Skip to content

Commit

Permalink
ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
jonschlinkert committed Apr 1, 2019
1 parent 7b1abf0 commit 60eb988
Show file tree
Hide file tree
Showing 15 changed files with 590 additions and 149 deletions.
24 changes: 12 additions & 12 deletions bench/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ bench.skip = name => {
return skip;
};

// bench('expand - set')
// .add(' braces', () => braces.expand('foo/{a,b,c}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar'))
// .run();
bench('expand - set')
.add(' braces', () => braces.expand('foo/{a,b,c}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a,b,c}/bar'))
.run();

// bench('expand - nested sets')
// .add(' braces', () => braces.expand('foo/{a,b,{x,y,z}}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar'))
// .run();
bench('expand - nested sets')
.add(' braces', () => braces.expand('foo/{a,b,{x,y,z}}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a,b,{x,y,z}}/bar'))
.run();

// bench('expand - range')
// .add(' braces', () => braces.expand('foo/{a..z}/bar'))
// .add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar'))
// .run();
bench('expand - range')
.add(' braces', () => braces.expand('foo/{a..z}/bar'))
.add('minimatch', () => minimatch.braceExpand('foo/{a..z}/bar'))
.run();

bench('compile regex - set')
.add(' braces', () => braces.makeRe(parse('foo/{a,b,c}/bar')))
Expand Down
6 changes: 6 additions & 0 deletions examples/compile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

const compile = require('../lib/compile');
const parse = require('../lib/parse');
console.log(compile(parse('{a,b,c}')));
console.log(compile(parse('{01..09}')));
39 changes: 35 additions & 4 deletions lib/compile.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,47 @@
'use strict';

const fill = require('fill-range');
const utils = require('./utils');

const compile = (ast, options = {}) => {
let walk = (node, parent = {}) => {
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
let invalidBlock = utils.isInvalidBrace(parent);
let invalidNode = node.invalid === true && options.escapeInvalid === true;
let invalid = invalidBlock === true || invalidNode === true;
let prefix = options.escapeInvalid === true ? '\\' : '';
let output = '';

if (node.isOpen === true) {
return prefix + node.value;
}
if (node.isClose === true) {
return prefix + node.value;
}

if (node.type === 'open') {
return invalid ? (prefix + node.value) : '(';
}

if (node.type === 'close') {
return invalid ? (prefix + node.value) : ')';
}

if (node.type === 'comma') {
return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
}

if (node.value) {
if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
return '\\' + node.value;
}
return node.value;
}

if (node.nodes && node.ranges > 0) {
let args = utils.reduce(node.nodes);
let range = fill(...args, { ...options, wrap: false, toRegex: true });
if (range.length !== 0) {
return args.length > 1 && range.length > 1 ? `(${range})` : range;
}
}

if (node.nodes) {
for (let child of node.nodes) {
output += walk(child, node);
Expand All @@ -27,3 +54,7 @@ const compile = (ast, options = {}) => {
};

module.exports = compile;

const parse = require('./parse');
console.log(compile(parse('{a,,,,}'), { escapeInvalid: true }));
console.log('(a|)');
89 changes: 43 additions & 46 deletions lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,57 @@
'use strict';

const path = require('path');
const isWindows = process.platform === 'win32' || path.sep === '\\';

module.exports = {
MAX_LENGTH: 1024 * 64,

// Digits
CHAR_0: '0', /* 0 */
CHAR_9: '9', /* 9 */
CHAR_0: '0', /* 0 */
CHAR_9: '9', /* 9 */

// Alphabet chars.
CHAR_UPPERCASE_A: 'A', /* A */
CHAR_LOWERCASE_A: 'a', /* a */
CHAR_UPPERCASE_Z: 'Z', /* Z */
CHAR_LOWERCASE_Z: 'z', /* z */
CHAR_UPPERCASE_A: 'A', /* A */
CHAR_LOWERCASE_A: 'a', /* a */
CHAR_UPPERCASE_Z: 'Z', /* Z */
CHAR_LOWERCASE_Z: 'z', /* z */

CHAR_LEFT_PARENTHESES: '(', /* ( */
CHAR_RIGHT_PARENTHESES: ')', /* ) */
CHAR_LEFT_PARENTHESES: '(', /* ( */
CHAR_RIGHT_PARENTHESES: ')', /* ) */

CHAR_ASTERISK: '*', /* * */
CHAR_ASTERISK: '*', /* * */

// Non-alphabetic chars.
CHAR_AMPERSAND: '&', /* & */
CHAR_AT: '@', /* @ */
CHAR_BACKSLASH: '\\', /* \ */
CHAR_BACKTICK: '`', /* ` */
CHAR_CARRIAGE_RETURN: '\r', /* \r */
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
CHAR_COLON: ':', /* : */
CHAR_COMMA: ',', /* , */
CHAR_DOLLAR: '$', /* . */
CHAR_DOT: '.', /* . */
CHAR_DOUBLE_QUOTE: '"', /* " */
CHAR_EQUAL: '=', /* = */
CHAR_EXCLAMATION_MARK: '!', /* ! */
CHAR_FORM_FEED: '\f', /* \f */
CHAR_FORWARD_SLASH: '/', /* / */
CHAR_HASH: '#', /* # */
CHAR_HYPHEN_MINUS: '-', /* - */
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
CHAR_LEFT_CURLY_BRACE: '{', /* { */
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
CHAR_LINE_FEED: '\n', /* \n */
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
CHAR_PERCENT: '%', /* % */
CHAR_PLUS: '+', /* + */
CHAR_QUESTION_MARK: '?', /* ? */
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
CHAR_AMPERSAND: '&', /* & */
CHAR_AT: '@', /* @ */
CHAR_BACKSLASH: '\\', /* \ */
CHAR_BACKTICK: '`', /* ` */
CHAR_CARRIAGE_RETURN: '\r', /* \r */
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
CHAR_COLON: ':', /* : */
CHAR_COMMA: ',', /* , */
CHAR_DOLLAR: '$', /* . */
CHAR_DOT: '.', /* . */
CHAR_DOUBLE_QUOTE: '"', /* " */
CHAR_EQUAL: '=', /* = */
CHAR_EXCLAMATION_MARK: '!', /* ! */
CHAR_FORM_FEED: '\f', /* \f */
CHAR_FORWARD_SLASH: '/', /* / */
CHAR_HASH: '#', /* # */
CHAR_HYPHEN_MINUS: '-', /* - */
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
CHAR_LEFT_CURLY_BRACE: '{', /* { */
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
CHAR_LINE_FEED: '\n', /* \n */
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
CHAR_PERCENT: '%', /* % */
CHAR_PLUS: '+', /* + */
CHAR_QUESTION_MARK: '?', /* ? */
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
CHAR_SEMICOLON: ';', /* ; */
CHAR_SINGLE_QUOTE: '\'', /* ' */
CHAR_SPACE: ' ', /* */
CHAR_TAB: '\t', /* \t */
CHAR_UNDERSCORE: '_', /* _ */
CHAR_VERTICAL_LINE: '|', /* | */
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF', /* \uFEFF */
CHAR_SEMICOLON: ';', /* ; */
CHAR_SINGLE_QUOTE: '\'', /* ' */
CHAR_SPACE: ' ', /* */
CHAR_TAB: '\t', /* \t */
CHAR_UNDERSCORE: '_', /* _ */
CHAR_VERTICAL_LINE: '|', /* | */
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
};
4 changes: 2 additions & 2 deletions lib/expand.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const compile = require('./compile');
const stringify = require('./stringify');
const utils = require('./utils');

const append = (queue = '', stash = '', enclose = false) => {
Expand Down Expand Up @@ -34,7 +34,7 @@ const expand = (ast, options = {}) => {
node.queue = [];

if (node.invalid || node.dollar) {
parent.queue.push(append(parent.queue.pop(), compile(node, options)));
parent.queue.push(append(parent.queue.pop(), stringify(node, options)));
return;
}

Expand Down
70 changes: 26 additions & 44 deletions lib/parse.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
'use strict';

const utils = require('./utils');

/**
* Constants
*/

const {
CHAR_BACKSLASH, /* \ */
CHAR_BACKTICK, /* ` */
CHAR_COMMA, /* , */
CHAR_DOLLAR, /* $ */
CHAR_DOT, /* . */
CHAR_LEFT_CURLY_BRACE, /* { */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_LEFT_SQUARE_BRACKET, /* [ */
CHAR_BACKSLASH, /* \ */
CHAR_BACKTICK, /* ` */
CHAR_COMMA, /* , */
CHAR_DOT, /* . */
CHAR_LEFT_CURLY_BRACE, /* { */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_LEFT_SQUARE_BRACKET, /* [ */
CHAR_RIGHT_SQUARE_BRACKET, /* ] */
CHAR_DOUBLE_QUOTE, /* " */
CHAR_SINGLE_QUOTE, /* ' */
CHAR_DOUBLE_QUOTE, /* " */
CHAR_SINGLE_QUOTE, /* ' */
CHAR_NO_BREAK_SPACE,
CHAR_ZERO_WIDTH_NOBREAK_SPACE
} = require('./constants');

/**
* Append node to the block.queue
*/

const append = (block, node) => {
if (!block.queue) return;

if (node.nodes) {
block.queue.push(node.queue);
return;
}

let last = block.queue[block.queue.length - 1];

if ((node.type === 'comma' || node.type === 'range')) {
block.queue.push(node.value);
return;
}

if (node.type === 'text' && node.value) {
if (typeof last !== 'string' || last === ',') {
block.queue.push(node.value);
} else {
block.queue[block.queue.length - 1] += node.value;
}
}
};

/**
* parse
*/
Expand All @@ -70,12 +39,17 @@ const parse = (input, options = {}) => {

const advance = () => input[index++];
const push = node => {
if (node.type === 'text' && prev.type === 'dot') {
prev.type = 'text';
}

if (prev && prev.type === 'text' && node.type === 'text') {
prev.value += node.value;
return;
}

block.nodes.push(node);
node.prev = prev;
prev = node;
return node;
};
Expand Down Expand Up @@ -141,17 +115,23 @@ const parse = (input, options = {}) => {
let open = value;
let next;

if (options.keepQuotes !== true) {
value = '';
}

while (index < length && (next = advance())) {
value += next;

if (next === CHAR_BACKSLASH) {
value += advance();
value += next + advance();
continue;
}

if (next === open) {
if (options.keepQuotes === true) value += next;
break;
}

value += next;
}

push({ type: 'text', value });
Expand All @@ -173,8 +153,8 @@ const parse = (input, options = {}) => {

if (value === CHAR_LEFT_CURLY_BRACE) {
depth++;
let dollar = prev.value && prev.value.slice(-1) === '$';

let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
let brace = {
type: 'brace',
open: true,
Expand Down Expand Up @@ -236,6 +216,7 @@ const parse = (input, options = {}) => {
}

if (prev.type === 'dot') {
block.range = [];
prev.value += value;
prev.type = 'range';

Expand Down Expand Up @@ -279,7 +260,8 @@ const parse = (input, options = {}) => {
block.nodes.forEach(node => {
if (!node.nodes) {
node.invalid = true;
node[node.type] = true;
if (node.type === 'open') node.isOpen = true;
if (node.type === 'close') node.isClose = true;
node.type = 'text';
}
});
Expand Down
2 changes: 2 additions & 0 deletions lib/stringify.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const utils = require('./utils');

module.exports = (ast, options = {}) => {
let stringify = (node, parent = {}) => {
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
Expand Down
11 changes: 10 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ exports.encloseBrace = node => {

exports.isInvalidBrace = block => {
if (block.type !== 'brace') return false;
if (block.invalid === true) return true;
if (block.invalid === true || block.dollar) return true;
if ((block.commas >> 0 + block.ranges >> 0) === 0) {
block.invalid = true;
return true;
Expand All @@ -61,6 +61,15 @@ exports.isOpenOrClose = node => {
return node.open === true || node.close === true;
};

/**
* Reduce an array of text nodes.
*/

exports.reduce = nodes => nodes.reduce((acc, node) => {
if (node.type === 'text') acc.push(node.value);
return acc;
}, []);

/**
* Flatten an array
*/
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
}
},
"dependencies": {
"fill-range": "^6.0.0",
"to-regex": "^3.0.2"
}
}
Loading

0 comments on commit 60eb988

Please sign in to comment.