-
Notifications
You must be signed in to change notification settings - Fork 8
/
string.js
149 lines (110 loc) · 4.95 KB
/
string.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import boolean from "./boolean";
import integer from "./integer";
import set from "./set";
import ret from "ret";
import DRange from "discontinuous-range";
import _range from "lodash.range";
const LOWERCASE_RANGE = [97, 122];
const UPPERCASE_RANGE = [65, 90];
const ASCII_RANGE = [32, 126];
const UNICODE_RANGE = [0, 65535];
const AsciiDRange = DRange(...ASCII_RANGE);
const UnicodeDRange = DRange(...UNICODE_RANGE);
const inRange = ([min, max], n) => n >= min && n <= max;
const changeCase = (code) => {
const lowercase = inRange(LOWERCASE_RANGE, code);
const uppercase = inRange(UPPERCASE_RANGE, code);
return lowercase || uppercase ? code + (lowercase ? -32 : 32) : code;
};
const createChar = (code, ignoreCase, prng) =>
code === null ? '' : String.fromCharCode(
ignoreCase && boolean.random(prng) ? changeCase(code) : code);
const expandCharacter = ({value}) => DRange(value);
const expandRange = ({from, to}) => DRange(from, to);
const expandSet = (token, range) => {
let drange = DRange();
let setRanges = token.set.map((code) => expand(code, range));
setRanges.forEach((setRange) => drange.add(setRange));
return token.not ? range.clone().subtract(drange) : drange;
};
const expanders = {
[ret.types.SET]: expandSet,
[ret.types.RANGE]: expandRange,
[ret.types.CHAR]: expandCharacter
};
const expand = (token, ...args) => expanders[token.type](token, ...args);
// These generators accept a token and the options object and return a character
// code.
const generateCharFromSet = (token, {range, prng}) => {
const set = expand(token, range);
return set.index(integer.boundedRandom(0, set.length - 1, prng));
};
const generateCharFromRange = ({from, to}, {prng}) =>
integer.boundedRandom(from, to, prng);
const generateChar = ({value}) => value;
const createCharGenerator = (func) =>
(token, _, {range, ignoreCase, prng}) =>
createChar(func(token, {range, ignoreCase, prng}), ignoreCase, prng);
// These generators accept a token, the groups and the options and return a
// sequence of tokens, which are then in turn passed to generator functions.
const generateFromGroup = ({notFollowedBy, options, stack}, _, {prng}) =>
notFollowedBy ? [] : options ? set.randomMember(options, prng) : stack;
const generateRepeat = (token, _, options) => {
const max = token.max === Infinity ? token.min + options.max : token.max;
return _range(integer.boundedRandom(token.min, max, options.prng))
.map(() => token.value);
};
const createSequenceGenerator = (func) =>
(token, groups, options) =>
func(token, groups, options)
.map((value) => generateFromToken(value, groups, options)).join('');
// Generator dispatch table based upon the token type.
const generators = {
[ret.types.ROOT]: createSequenceGenerator(generateFromGroup),
[ret.types.GROUP]: createSequenceGenerator(generateFromGroup),
[ret.types.POSITION]: () => '',
[ret.types.REPETITION]: createSequenceGenerator(generateRepeat),
[ret.types.REFERENCE]: ({value}, groups) => groups[value - 1],
[ret.types.CHAR]: createCharGenerator(generateChar),
[ret.types.SET]: createCharGenerator(generateCharFromSet),
[ret.types.RANGE]: createCharGenerator(generateCharFromRange)
};
const generateFromToken = (token, groups, options) => {
const result = generators[token.type](token, groups, options);
if (token.type === ret.types.GROUP && token.remember) {
groups.push(result);
}
return result;
};
const generateStringFromRange = (range, expression, options) =>
() => generateFromToken(ret(expression), [], {range, ...options});
// Exported public functions.
const generateCharacterFromRange = ([min, max], {prng}) =>
generateStringFromRange(DRange(min, max), '.', {prng});
const generateString = (unicode, expression, options) =>
generateStringFromRange(
unicode ? UnicodeDRange : AsciiDRange, expression, options);
const randomCharacterFromRange = (range, prng=Math.random) =>
generateCharacterFromRange(range, {prng})();
const randomAsciiString = (expression, ignoreCase, prng=Math.random) =>
generateStringFromRange(AsciiDRange, expression, {ignoreCase, prng});
const randomUnicodeString = (expression, ignoreCase, prng=Math.random) =>
generateStringFromRange(UnicodeDRange, expression, {ignoreCase, prng});
const randomAsciiCharacter = (prng=Math.random) =>
generateCharacterFromRange(ASCII_RANGE, {prng})();
const randomLowercaseCharacter = (prng=Math.random) =>
generateCharacterFromRange(LOWERCASE_RANGE, {prng})();
const randomUnicodeCharacter = (prng=Math.random) =>
generateCharacterFromRange(UNICODE_RANGE, {prng})();
const randomUppercaseCharacter = (prng=Math.random) =>
generateCharacterFromRange(UPPERCASE_RANGE, {prng})();
export default {
generateString,
randomCharacterFromRange,
randomAsciiString,
randomUnicodeString,
randomAsciiCharacter,
randomLowercaseCharacter,
randomUnicodeCharacter,
randomUppercaseCharacter
};