Skip to content

Commit 60eb988

Browse files
committed
ranges
1 parent 7b1abf0 commit 60eb988

15 files changed

+590
-149
lines changed

bench/index.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,20 @@ bench.skip = name => {
5050
return skip;
5151
};
5252

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

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

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

6868
bench('compile regex - set')
6969
.add(' braces', () => braces.makeRe(parse('foo/{a,b,c}/bar')))

examples/compile.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict';
2+
3+
const compile = require('../lib/compile');
4+
const parse = require('../lib/parse');
5+
console.log(compile(parse('{a,b,c}')));
6+
console.log(compile(parse('{01..09}')));

lib/compile.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,47 @@
11
'use strict';
22

3+
const fill = require('fill-range');
34
const utils = require('./utils');
45

56
const compile = (ast, options = {}) => {
67
let walk = (node, parent = {}) => {
7-
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
8+
let invalidBlock = utils.isInvalidBrace(parent);
89
let invalidNode = node.invalid === true && options.escapeInvalid === true;
10+
let invalid = invalidBlock === true || invalidNode === true;
11+
let prefix = options.escapeInvalid === true ? '\\' : '';
912
let output = '';
1013

14+
if (node.isOpen === true) {
15+
return prefix + node.value;
16+
}
17+
if (node.isClose === true) {
18+
return prefix + node.value;
19+
}
20+
21+
if (node.type === 'open') {
22+
return invalid ? (prefix + node.value) : '(';
23+
}
24+
25+
if (node.type === 'close') {
26+
return invalid ? (prefix + node.value) : ')';
27+
}
28+
29+
if (node.type === 'comma') {
30+
return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
31+
}
32+
1133
if (node.value) {
12-
if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
13-
return '\\' + node.value;
14-
}
1534
return node.value;
1635
}
1736

37+
if (node.nodes && node.ranges > 0) {
38+
let args = utils.reduce(node.nodes);
39+
let range = fill(...args, { ...options, wrap: false, toRegex: true });
40+
if (range.length !== 0) {
41+
return args.length > 1 && range.length > 1 ? `(${range})` : range;
42+
}
43+
}
44+
1845
if (node.nodes) {
1946
for (let child of node.nodes) {
2047
output += walk(child, node);
@@ -27,3 +54,7 @@ const compile = (ast, options = {}) => {
2754
};
2855

2956
module.exports = compile;
57+
58+
const parse = require('./parse');
59+
console.log(compile(parse('{a,,,,}'), { escapeInvalid: true }));
60+
console.log('(a|)');

lib/constants.js

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,57 @@
11
'use strict';
22

3-
const path = require('path');
4-
const isWindows = process.platform === 'win32' || path.sep === '\\';
5-
63
module.exports = {
74
MAX_LENGTH: 1024 * 64,
85

96
// Digits
10-
CHAR_0: '0', /* 0 */
11-
CHAR_9: '9', /* 9 */
7+
CHAR_0: '0', /* 0 */
8+
CHAR_9: '9', /* 9 */
129

1310
// Alphabet chars.
14-
CHAR_UPPERCASE_A: 'A', /* A */
15-
CHAR_LOWERCASE_A: 'a', /* a */
16-
CHAR_UPPERCASE_Z: 'Z', /* Z */
17-
CHAR_LOWERCASE_Z: 'z', /* z */
11+
CHAR_UPPERCASE_A: 'A', /* A */
12+
CHAR_LOWERCASE_A: 'a', /* a */
13+
CHAR_UPPERCASE_Z: 'Z', /* Z */
14+
CHAR_LOWERCASE_Z: 'z', /* z */
1815

19-
CHAR_LEFT_PARENTHESES: '(', /* ( */
20-
CHAR_RIGHT_PARENTHESES: ')', /* ) */
16+
CHAR_LEFT_PARENTHESES: '(', /* ( */
17+
CHAR_RIGHT_PARENTHESES: ')', /* ) */
2118

22-
CHAR_ASTERISK: '*', /* * */
19+
CHAR_ASTERISK: '*', /* * */
2320

2421
// Non-alphabetic chars.
25-
CHAR_AMPERSAND: '&', /* & */
26-
CHAR_AT: '@', /* @ */
27-
CHAR_BACKSLASH: '\\', /* \ */
28-
CHAR_BACKTICK: '`', /* ` */
29-
CHAR_CARRIAGE_RETURN: '\r', /* \r */
30-
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
31-
CHAR_COLON: ':', /* : */
32-
CHAR_COMMA: ',', /* , */
33-
CHAR_DOLLAR: '$', /* . */
34-
CHAR_DOT: '.', /* . */
35-
CHAR_DOUBLE_QUOTE: '"', /* " */
36-
CHAR_EQUAL: '=', /* = */
37-
CHAR_EXCLAMATION_MARK: '!', /* ! */
38-
CHAR_FORM_FEED: '\f', /* \f */
39-
CHAR_FORWARD_SLASH: '/', /* / */
40-
CHAR_HASH: '#', /* # */
41-
CHAR_HYPHEN_MINUS: '-', /* - */
42-
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
43-
CHAR_LEFT_CURLY_BRACE: '{', /* { */
44-
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
45-
CHAR_LINE_FEED: '\n', /* \n */
46-
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
47-
CHAR_PERCENT: '%', /* % */
48-
CHAR_PLUS: '+', /* + */
49-
CHAR_QUESTION_MARK: '?', /* ? */
50-
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
51-
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
22+
CHAR_AMPERSAND: '&', /* & */
23+
CHAR_AT: '@', /* @ */
24+
CHAR_BACKSLASH: '\\', /* \ */
25+
CHAR_BACKTICK: '`', /* ` */
26+
CHAR_CARRIAGE_RETURN: '\r', /* \r */
27+
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
28+
CHAR_COLON: ':', /* : */
29+
CHAR_COMMA: ',', /* , */
30+
CHAR_DOLLAR: '$', /* . */
31+
CHAR_DOT: '.', /* . */
32+
CHAR_DOUBLE_QUOTE: '"', /* " */
33+
CHAR_EQUAL: '=', /* = */
34+
CHAR_EXCLAMATION_MARK: '!', /* ! */
35+
CHAR_FORM_FEED: '\f', /* \f */
36+
CHAR_FORWARD_SLASH: '/', /* / */
37+
CHAR_HASH: '#', /* # */
38+
CHAR_HYPHEN_MINUS: '-', /* - */
39+
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
40+
CHAR_LEFT_CURLY_BRACE: '{', /* { */
41+
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
42+
CHAR_LINE_FEED: '\n', /* \n */
43+
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
44+
CHAR_PERCENT: '%', /* % */
45+
CHAR_PLUS: '+', /* + */
46+
CHAR_QUESTION_MARK: '?', /* ? */
47+
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
48+
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
5249
CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
53-
CHAR_SEMICOLON: ';', /* ; */
54-
CHAR_SINGLE_QUOTE: '\'', /* ' */
55-
CHAR_SPACE: ' ', /* */
56-
CHAR_TAB: '\t', /* \t */
57-
CHAR_UNDERSCORE: '_', /* _ */
58-
CHAR_VERTICAL_LINE: '|', /* | */
59-
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF', /* \uFEFF */
50+
CHAR_SEMICOLON: ';', /* ; */
51+
CHAR_SINGLE_QUOTE: '\'', /* ' */
52+
CHAR_SPACE: ' ', /* */
53+
CHAR_TAB: '\t', /* \t */
54+
CHAR_UNDERSCORE: '_', /* _ */
55+
CHAR_VERTICAL_LINE: '|', /* | */
56+
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
6057
};

lib/expand.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const compile = require('./compile');
3+
const stringify = require('./stringify');
44
const utils = require('./utils');
55

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

3636
if (node.invalid || node.dollar) {
37-
parent.queue.push(append(parent.queue.pop(), compile(node, options)));
37+
parent.queue.push(append(parent.queue.pop(), stringify(node, options)));
3838
return;
3939
}
4040

lib/parse.js

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,24 @@
11
'use strict';
22

3-
const utils = require('./utils');
4-
53
/**
64
* Constants
75
*/
86

97
const {
10-
CHAR_BACKSLASH, /* \ */
11-
CHAR_BACKTICK, /* ` */
12-
CHAR_COMMA, /* , */
13-
CHAR_DOLLAR, /* $ */
14-
CHAR_DOT, /* . */
15-
CHAR_LEFT_CURLY_BRACE, /* { */
16-
CHAR_RIGHT_CURLY_BRACE, /* } */
17-
CHAR_LEFT_SQUARE_BRACKET, /* [ */
8+
CHAR_BACKSLASH, /* \ */
9+
CHAR_BACKTICK, /* ` */
10+
CHAR_COMMA, /* , */
11+
CHAR_DOT, /* . */
12+
CHAR_LEFT_CURLY_BRACE, /* { */
13+
CHAR_RIGHT_CURLY_BRACE, /* } */
14+
CHAR_LEFT_SQUARE_BRACKET, /* [ */
1815
CHAR_RIGHT_SQUARE_BRACKET, /* ] */
19-
CHAR_DOUBLE_QUOTE, /* " */
20-
CHAR_SINGLE_QUOTE, /* ' */
16+
CHAR_DOUBLE_QUOTE, /* " */
17+
CHAR_SINGLE_QUOTE, /* ' */
2118
CHAR_NO_BREAK_SPACE,
2219
CHAR_ZERO_WIDTH_NOBREAK_SPACE
2320
} = require('./constants');
2421

25-
/**
26-
* Append node to the block.queue
27-
*/
28-
29-
const append = (block, node) => {
30-
if (!block.queue) return;
31-
32-
if (node.nodes) {
33-
block.queue.push(node.queue);
34-
return;
35-
}
36-
37-
let last = block.queue[block.queue.length - 1];
38-
39-
if ((node.type === 'comma' || node.type === 'range')) {
40-
block.queue.push(node.value);
41-
return;
42-
}
43-
44-
if (node.type === 'text' && node.value) {
45-
if (typeof last !== 'string' || last === ',') {
46-
block.queue.push(node.value);
47-
} else {
48-
block.queue[block.queue.length - 1] += node.value;
49-
}
50-
}
51-
};
52-
5322
/**
5423
* parse
5524
*/
@@ -70,12 +39,17 @@ const parse = (input, options = {}) => {
7039

7140
const advance = () => input[index++];
7241
const push = node => {
42+
if (node.type === 'text' && prev.type === 'dot') {
43+
prev.type = 'text';
44+
}
45+
7346
if (prev && prev.type === 'text' && node.type === 'text') {
7447
prev.value += node.value;
7548
return;
7649
}
7750

7851
block.nodes.push(node);
52+
node.prev = prev;
7953
prev = node;
8054
return node;
8155
};
@@ -141,17 +115,23 @@ const parse = (input, options = {}) => {
141115
let open = value;
142116
let next;
143117

118+
if (options.keepQuotes !== true) {
119+
value = '';
120+
}
121+
144122
while (index < length && (next = advance())) {
145-
value += next;
146123

147124
if (next === CHAR_BACKSLASH) {
148-
value += advance();
125+
value += next + advance();
149126
continue;
150127
}
151128

152129
if (next === open) {
130+
if (options.keepQuotes === true) value += next;
153131
break;
154132
}
133+
134+
value += next;
155135
}
156136

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

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

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

238218
if (prev.type === 'dot') {
219+
block.range = [];
239220
prev.value += value;
240221
prev.type = 'range';
241222

@@ -279,7 +260,8 @@ const parse = (input, options = {}) => {
279260
block.nodes.forEach(node => {
280261
if (!node.nodes) {
281262
node.invalid = true;
282-
node[node.type] = true;
263+
if (node.type === 'open') node.isOpen = true;
264+
if (node.type === 'close') node.isClose = true;
283265
node.type = 'text';
284266
}
285267
});

lib/stringify.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const utils = require('./utils');
4+
35
module.exports = (ast, options = {}) => {
46
let stringify = (node, parent = {}) => {
57
let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);

lib/utils.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ exports.encloseBrace = node => {
3838

3939
exports.isInvalidBrace = block => {
4040
if (block.type !== 'brace') return false;
41-
if (block.invalid === true) return true;
41+
if (block.invalid === true || block.dollar) return true;
4242
if ((block.commas >> 0 + block.ranges >> 0) === 0) {
4343
block.invalid = true;
4444
return true;
@@ -61,6 +61,15 @@ exports.isOpenOrClose = node => {
6161
return node.open === true || node.close === true;
6262
};
6363

64+
/**
65+
* Reduce an array of text nodes.
66+
*/
67+
68+
exports.reduce = nodes => nodes.reduce((acc, node) => {
69+
if (node.type === 'text') acc.push(node.value);
70+
return acc;
71+
}, []);
72+
6473
/**
6574
* Flatten an array
6675
*/

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
}
8282
},
8383
"dependencies": {
84+
"fill-range": "^6.0.0",
8485
"to-regex": "^3.0.2"
8586
}
8687
}

0 commit comments

Comments
 (0)