source: node_modules/randexp/lib/randexp.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 6.1 KB
RevLine 
[d24f17c]1const ret = require('ret');
2const DRange = require('drange');
3const types = ret.types;
4
5
6module.exports = class RandExp {
7 /**
8 * @constructor
9 * @param {RegExp|String} regexp
10 * @param {String} m
11 */
12 constructor(regexp, m) {
13 this._setDefaults(regexp);
14 if (regexp instanceof RegExp) {
15 this.ignoreCase = regexp.ignoreCase;
16 this.multiline = regexp.multiline;
17 regexp = regexp.source;
18
19 } else if (typeof regexp === 'string') {
20 this.ignoreCase = m && m.indexOf('i') !== -1;
21 this.multiline = m && m.indexOf('m') !== -1;
22 } else {
23 throw new Error('Expected a regexp or string');
24 }
25
26 this.tokens = ret(regexp);
27 }
28
29
30 /**
31 * Checks if some custom properties have been set for this regexp.
32 *
33 * @param {RandExp} randexp
34 * @param {RegExp} regexp
35 */
36 _setDefaults(regexp) {
37 // When a repetitional token has its max set to Infinite,
38 // randexp won't actually generate a random amount between min and Infinite
39 // instead it will see Infinite as min + 100.
40 this.max = regexp.max != null ? regexp.max :
41 RandExp.prototype.max != null ? RandExp.prototype.max : 100;
42
43 // This allows expanding to include additional characters
44 // for instance: RandExp.defaultRange.add(0, 65535);
45 this.defaultRange = regexp.defaultRange ?
46 regexp.defaultRange : this.defaultRange.clone();
47
48 if (regexp.randInt) {
49 this.randInt = regexp.randInt;
50 }
51 }
52
53
54 /**
55 * Generates the random string.
56 *
57 * @return {String}
58 */
59 gen() {
60 return this._gen(this.tokens, []);
61 }
62
63
64 /**
65 * Generate random string modeled after given tokens.
66 *
67 * @param {Object} token
68 * @param {Array.<String>} groups
69 * @return {String}
70 */
71 _gen(token, groups) {
72 var stack, str, n, i, l;
73
74 switch (token.type) {
75 case types.ROOT:
76 case types.GROUP:
77 // Ignore lookaheads for now.
78 if (token.followedBy || token.notFollowedBy) { return ''; }
79
80 // Insert placeholder until group string is generated.
81 if (token.remember && token.groupNumber === undefined) {
82 token.groupNumber = groups.push(null) - 1;
83 }
84
85 stack = token.options ?
86 this._randSelect(token.options) : token.stack;
87
88 str = '';
89 for (i = 0, l = stack.length; i < l; i++) {
90 str += this._gen(stack[i], groups);
91 }
92
93 if (token.remember) {
94 groups[token.groupNumber] = str;
95 }
96 return str;
97
98 case types.POSITION:
99 // Do nothing for now.
100 return '';
101
102 case types.SET:
103 var expandedSet = this._expand(token);
104 if (!expandedSet.length) { return ''; }
105 return String.fromCharCode(this._randSelect(expandedSet));
106
107 case types.REPETITION:
108 // Randomly generate number between min and max.
109 n = this.randInt(token.min,
110 token.max === Infinity ? token.min + this.max : token.max);
111
112 str = '';
113 for (i = 0; i < n; i++) {
114 str += this._gen(token.value, groups);
115 }
116
117 return str;
118
119 case types.REFERENCE:
120 return groups[token.value - 1] || '';
121
122 case types.CHAR:
123 var code = this.ignoreCase && this._randBool() ?
124 this._toOtherCase(token.value) : token.value;
125 return String.fromCharCode(code);
126 }
127 }
128
129
130 /**
131 * If code is alphabetic, converts to other case.
132 * If not alphabetic, returns back code.
133 *
134 * @param {Number} code
135 * @return {Number}
136 */
137 _toOtherCase(code) {
138 return code + (97 <= code && code <= 122 ? -32 :
139 65 <= code && code <= 90 ? 32 : 0);
140 }
141
142
143 /**
144 * Randomly returns a true or false value.
145 *
146 * @return {Boolean}
147 */
148 _randBool() {
149 return !this.randInt(0, 1);
150 }
151
152
153 /**
154 * Randomly selects and returns a value from the array.
155 *
156 * @param {Array.<Object>} arr
157 * @return {Object}
158 */
159 _randSelect(arr) {
160 if (arr instanceof DRange) {
161 return arr.index(this.randInt(0, arr.length - 1));
162 }
163 return arr[this.randInt(0, arr.length - 1)];
164 }
165
166
167 /**
168 * expands a token to a DiscontinuousRange of characters which has a
169 * length and an index function (for random selecting)
170 *
171 * @param {Object} token
172 * @return {DiscontinuousRange}
173 */
174 _expand(token) {
175 if (token.type === ret.types.CHAR) {
176 return new DRange(token.value);
177 } else if (token.type === ret.types.RANGE) {
178 return new DRange(token.from, token.to);
179 } else {
180 let drange = new DRange();
181 for (let i = 0; i < token.set.length; i++) {
182 let subrange = this._expand(token.set[i]);
183 drange.add(subrange);
184 if (this.ignoreCase) {
185 for (let j = 0; j < subrange.length; j++) {
186 let code = subrange.index(j);
187 let otherCaseCode = this._toOtherCase(code);
188 if (code !== otherCaseCode) {
189 drange.add(otherCaseCode);
190 }
191 }
192 }
193 }
194 if (token.not) {
195 return this.defaultRange.clone().subtract(drange);
196 } else {
197 return this.defaultRange.clone().intersect(drange);
198 }
199 }
200 }
201
202
203 /**
204 * Randomly generates and returns a number between a and b (inclusive).
205 *
206 * @param {Number} a
207 * @param {Number} b
208 * @return {Number}
209 */
210 randInt(a, b) {
211 return a + Math.floor(Math.random() * (1 + b - a));
212 }
213
214
215 /**
216 * Default range of characters to generate from.
217 */
218 get defaultRange() {
219 return this._range = this._range || new DRange(32, 126);
220 }
221
222 set defaultRange(range) {
223 this._range = range;
224 }
225
226
227 /**
228 *
229 * Enables use of randexp with a shorter call.
230 *
231 * @param {RegExp|String| regexp}
232 * @param {String} m
233 * @return {String}
234 */
235 static randexp(regexp, m) {
236 var randexp;
237 if(typeof regexp === 'string') {
238 regexp = new RegExp(regexp, m);
239 }
240
241 if (regexp._randexp === undefined) {
242 randexp = new RandExp(regexp, m);
243 regexp._randexp = randexp;
244 } else {
245 randexp = regexp._randexp;
246 randexp._setDefaults(regexp);
247 }
248 return randexp.gen();
249 }
250
251
252 /**
253 * Enables sugary /regexp/.gen syntax.
254 */
255 static sugar() {
256 /* eshint freeze:false */
257 RegExp.prototype.gen = function() {
258 return RandExp.randexp(this);
259 };
260 }
261};
Note: See TracBrowser for help on using the repository browser.