Comparing version 0.3.4 to 0.4.0
var ret = require('ret'); | ||
var DRange = require('discontinuous-range'); | ||
var types = ret.types; | ||
@@ -6,14 +7,2 @@ | ||
/** | ||
* Returns random number in the range [a, b]. | ||
* | ||
* @param {Number} a | ||
* @param {Number} b | ||
* @return {Number} | ||
*/ | ||
function randInt(a, b) { | ||
return a + Math.floor(Math.random() * (1 + b - a)); | ||
} | ||
/** | ||
* If code is alphabetic, converts to other case. | ||
@@ -32,132 +21,153 @@ * If not alphabetic, returns back code. | ||
/** | ||
* Returns subset of [a, b] if [from, to] is in it. | ||
* Randomly returns a true or false value. | ||
* | ||
* @param {Number} a | ||
* @param {Number} b | ||
* @param {Number} from | ||
* @param {Number} to | ||
* @return {Object|Boolean} | ||
* {Number} from | ||
* {Number} to | ||
* @return {Boolean} | ||
*/ | ||
function range(a, b, from, to) { | ||
return a <= from && from <= b ? { from: from, to: Math.min(b, to) } : | ||
a <= to && to <= b ? { from: Math.max(a, from), to: to } : | ||
false; | ||
} | ||
function randBool() { | ||
return !this.randInt(0, 1); | ||
}; | ||
/** | ||
* Returns true if all properties of a are equal to b. | ||
* a and b are arrays of objects. | ||
* | ||
* @param {Number} a | ||
* @param {Number} b | ||
* Randomly selects and returns a value from the array. | ||
* | ||
* @param {Array.<Object>} arr | ||
* @return {Object} | ||
*/ | ||
function deepEqual(a, b) { | ||
var i, l, key, obj; | ||
if ((l = a.length) !== b.length) return false; | ||
for (i = 0; i < l; i++) { | ||
obj = a[i]; | ||
for (key in obj) { | ||
if (obj.hasOwnProperty(key) && obj[key] !== b[i][key]) { | ||
return false; | ||
} | ||
} | ||
function randSelect(arr) { | ||
if (arr instanceof DRange) { | ||
return arr.index(this.randInt(0, arr.length - 1)); | ||
} | ||
return arr[this.randInt(0, arr.length - 1)]; | ||
}; | ||
return true; | ||
} | ||
/** | ||
* Determines if a character code is alphabetic and decide | ||
* to switch case randomly. | ||
* | ||
* @param {Number} code | ||
* @param {Boolean} ignoreCase | ||
* @return {String} | ||
*/ | ||
function char(code, ignoreCase) { | ||
code = ignoreCase && randBool.call(this) ? toOtherCase(code) : code; | ||
return String.fromCharCode(code); | ||
}; | ||
/** | ||
* Returns true if negated needle set is inside of sets array. | ||
* Using deepEqual as comparator. | ||
* expands a token to a DiscontinuousRange of characters which has a | ||
* length and an index function (for random selecting) | ||
* | ||
* @param {Array.<Object>} sets | ||
* @param {String} needle | ||
* @param {Boolean} | ||
* @param {Object} token | ||
* @return {DiscontinuousRange} | ||
*/ | ||
function findNotSet(sets, needle) { | ||
for (var i = 0, l = sets.length; i < l; i++) { | ||
var cset = sets[i]; | ||
if (cset.not !== needle.not && deepEqual(cset.set, needle.set)) { | ||
return true; | ||
function expand(token) { | ||
if (token.type === ret.types.CHAR) return new DRange(token.value); | ||
if (token.type === ret.types.RANGE) return new DRange(token.from, token.to); | ||
if (token.type === ret.types.SET) { | ||
var drange = new DRange(); | ||
for (var i = 0; i < token.set.length; i++) { | ||
drange.add(expand.call(this, token.set[i])); | ||
} | ||
if (token.not) { | ||
return this.defaultRange.clone().subtract(drange); | ||
} else { | ||
return drange; | ||
} | ||
} | ||
throw new Error('unexpandable token type: ' + token.type); | ||
}; | ||
return false; | ||
} | ||
/** | ||
* Returns true if character is in class set. | ||
* | ||
* @param {Object} set | ||
* @param {Number} code | ||
* @param {Boolean} ignoreCase | ||
* @return {Boolean} | ||
* @constructor | ||
* @param {RegExp|String} regexp | ||
* @param {String} m | ||
*/ | ||
function inClass(set, code, ignoreCase) { | ||
var token; | ||
var v; | ||
var sets = []; | ||
var infLoop = false; | ||
var RandExp = module.exports = function(regexp, m) { | ||
this.defaultRange = this.defaultRange.clone(); | ||
if (regexp instanceof RegExp) { | ||
this.ignoreCase = regexp.ignoreCase; | ||
this.multiline = regexp.multiline; | ||
if (typeof regexp.max === 'number') { | ||
this.max = regexp.max; | ||
} | ||
regexp = regexp.source; | ||
for (var i = 0, l = set.length; i < l; i++) { | ||
token = set[i]; | ||
} else if (typeof regexp === 'string') { | ||
this.ignoreCase = m && m.indexOf('i') !== -1; | ||
this.multiline = m && m.indexOf('m') !== -1; | ||
} else { | ||
throw new Error('Expected a regexp or string'); | ||
} | ||
switch (token.type) { | ||
case types.CHAR: | ||
v = token.value; | ||
if (v === code || (ignoreCase && toOtherCase(v) === code)) { | ||
return true; | ||
} | ||
break; | ||
this.tokens = ret(regexp); | ||
}; | ||
case types.RANGE: | ||
// If ignoreCase is on, and alphabetic character ranges fall | ||
// inside this range, check against both cases. | ||
if (token.from <= code && code <= token.to || (ignoreCase && | ||
(((v = range(97, 122, token.from, token.to)) !== false && | ||
v.from <= code && code <= v.to) || | ||
((v = range(65, 90, token.from, token.to)) !== false && | ||
v.from <= code && code <= v.to)))) { | ||
return true; | ||
} | ||
break; | ||
case types.SET: | ||
// Use these to detect an infinite loop with 2 sets | ||
// that cancel out each other. | ||
if (sets.length > 0 && findNotSet(sets, token)) { | ||
infLoop = true; | ||
} else { | ||
sets.push(token); | ||
} | ||
// When a repetitional token has its max set to Infinite, | ||
// randexp won't actually generate a random amount between min and Infinite | ||
// instead it will see Infinite as min + 100. | ||
RandExp.prototype.max = 100; | ||
if (!infLoop && | ||
inClass(token.set, code, ignoreCase) !== token.not) { | ||
return true; | ||
} | ||
// Generates the random string. | ||
RandExp.prototype.gen = function() { | ||
return gen.call(this, this.tokens, []); | ||
}; | ||
// Enables use of randexp with a shorter call. | ||
RandExp.randexp = function(regexp, m) { | ||
var randexp; | ||
if (regexp._randexp === undefined) { | ||
randexp = new RandExp(regexp, m); | ||
regexp._randexp = randexp; | ||
} else { | ||
randexp = regexp._randexp; | ||
if (typeof regexp.max === 'number') { | ||
randexp.max = regexp.max; | ||
} | ||
if (regexp.defaultRange instanceof DRange) { | ||
randexp.defaultRange = regexp.defaultRange; | ||
} | ||
if (typeof regexp.randInt === 'function') { | ||
randexp.randInt = regexp.randInt; | ||
} | ||
} | ||
return false; | ||
} | ||
return randexp.gen(); | ||
}; | ||
// This enables sugary /regexp/.gen syntax. | ||
RandExp.sugar = function() { | ||
/* jshint freeze:false */ | ||
RegExp.prototype.gen = function() { | ||
return RandExp.randexp(this); | ||
}; | ||
}; | ||
// This allows expanding to include additional characters | ||
// for instance: RandExp.defaultRange.add(0, 65535); | ||
RandExp.prototype.defaultRange = new DRange(32, 126); | ||
/** | ||
* Determines if a character code is alphabetic and decide | ||
* to switch case randomly. | ||
* Randomly generates and returns a number between a and b (inclusive). | ||
* | ||
* @param {Number} code | ||
* @param {Boolean} ignoreCase | ||
* @return {String} | ||
* @param {Number} a | ||
* @param {Number} b | ||
* @return {Number} | ||
*/ | ||
function char(code, ignoreCase) { | ||
return String.fromCharCode(ignoreCase && Math.random() > 0.5 ? | ||
toOtherCase(code) : code); | ||
} | ||
RandExp.prototype.randInt = function(a, b) { | ||
return a + Math.floor(Math.random() * (1 + b - a)); | ||
}; | ||
@@ -170,7 +180,6 @@ | ||
* @param {Array.<String>} groups | ||
* @param {Boolean} negate | ||
* @return {String} | ||
*/ | ||
function gen(token, groups, negate) { | ||
var stack, str, n, i, l, not; | ||
function gen(token, groups) { | ||
var stack, str, n, i, l; | ||
@@ -189,5 +198,3 @@ switch (token.type) { | ||
stack = token.options ? | ||
token.options[Math.floor(Math.random() * token.options.length)] : | ||
token.stack; | ||
stack = token.options ? randSelect.call(this, token.options) : token.stack; | ||
@@ -212,31 +219,8 @@ str = ''; | ||
// If this class is an except class, i.e. [^abc], | ||
// generate a random character until one that isnt in this class | ||
// is found. | ||
negate = !!negate; | ||
not = negate !== token.not; | ||
if (not) { | ||
while (true) { | ||
var c = this.anyRandChar(); | ||
var code = c.charCodeAt(0); | ||
var expanded_set = expand.call(this, token); | ||
if (!expanded_set.length) return ''; | ||
return char.call(this, randSelect.call(this, expanded_set), this.ignoreCase); | ||
if (inClass(token.set, code, this.ignoreCase)) { continue; } | ||
return c; | ||
} | ||
// Otherwise, pick a random token in the class set. | ||
} else { | ||
if (token.set.length) { | ||
return gen.call(this, | ||
token.set[Math.floor(Math.random() * token.set.length)], | ||
groups, not); | ||
} else { | ||
return ''; | ||
} | ||
} | ||
break; | ||
case types.RANGE: | ||
return char(randInt(token.from, token.to), this.ignoreCase); | ||
return char.call(this, this.randInt(token.from, token.to), this.ignoreCase); | ||
@@ -246,3 +230,3 @@ | ||
// Randomly generate number between min and max. | ||
n = randInt(token.min, | ||
n = this.randInt(token.min, | ||
token.max === Infinity ? token.min + this.max : token.max); | ||
@@ -263,3 +247,3 @@ | ||
case types.CHAR: | ||
return char(token.value, this.ignoreCase); | ||
return char.call(this, token.value, this.ignoreCase); | ||
} | ||
@@ -269,76 +253,1 @@ } | ||
/** | ||
* @constructor | ||
* @param {RegExp|String} regexp | ||
* @param {String} m | ||
*/ | ||
var RandExp = module.exports = function(regexp, m) { | ||
if (regexp instanceof RegExp) { | ||
this.ignoreCase = regexp.ignoreCase; | ||
this.multiline = regexp.multiline; | ||
if (typeof regexp.max === 'number') { | ||
this.max = regexp.max; | ||
} | ||
if (typeof regexp.anyRandChar === 'function') { | ||
this.anyRandChar = regexp.anyRandChar; | ||
} | ||
regexp = regexp.source; | ||
} else if (typeof regexp === 'string') { | ||
this.ignoreCase = m && m.indexOf('i') !== -1; | ||
this.multiline = m && m.indexOf('m') !== -1; | ||
} else { | ||
throw new Error('Expected a regexp or string'); | ||
} | ||
this.tokens = ret(regexp); | ||
}; | ||
// When a repetitional token has its max set to Infinite, | ||
// randexp won't actually generate a random amount between min and Infinite | ||
// instead it will see Infinite as min + 100. | ||
RandExp.prototype.max = 100; | ||
// Returns any random character. | ||
RandExp.prototype.anyRandChar = function() { | ||
return String.fromCharCode(randInt(0, 65535)); | ||
}; | ||
// Generates the random string. | ||
RandExp.prototype.gen = function() { | ||
return gen.call(this, this.tokens, []); | ||
}; | ||
// Enables use of randexp with a shorter call. | ||
// Saves the RandExp object into the regex. | ||
var randexp = RandExp.randexp = function(regexp, m) { | ||
var randexp; | ||
if (regexp._randexp === undefined) { | ||
randexp = new RandExp(regexp, m); | ||
regexp._randexp = randexp; | ||
} else { | ||
randexp = regexp._randexp; | ||
if (typeof regexp.max === 'number') { | ||
randexp.max = regexp.max; | ||
} | ||
if (typeof regexp.anyRandChar === 'function') { | ||
randexp.anyRandChar = regexp.anyRandChar; | ||
} | ||
} | ||
return randexp.gen(); | ||
}; | ||
// This enables sugary /regexp/.gen syntax. | ||
RandExp.sugar = function() { | ||
/* jshint freeze:false */ | ||
RegExp.prototype.gen = function() { | ||
return randexp(this); | ||
}; | ||
}; |
@@ -5,3 +5,3 @@ { | ||
"keywords": ["regex", "regexp", "regular expression", "random", "test"], | ||
"version": "0.3.4", | ||
"version": "0.4.0", | ||
"homepage": "http://fent.github.io/randexp.js/", | ||
@@ -21,3 +21,4 @@ "repository": { | ||
"dependencies": { | ||
"ret": "0.1.x" | ||
"discontinuous-range": "1.0.0", | ||
"ret": "0.1.10" | ||
}, | ||
@@ -24,0 +25,0 @@ "devDependencies": { |
@@ -20,3 +20,3 @@ # randexp.js [![Build Status](https://secure.travis-ci.org/fent/randexp.js.png)](http://travis-ci.org/fent/randexp.js) | ||
new RandExp(/random stuff: .+/).gen(); | ||
// => random stuff: 湐箻ໜ䫴㳸長���邓蕲뤀쑡篷皇硬剈궦佔칗븛뀃匫鴔事좍ﯣ⭼ꝏ䭍詳蒂䥂뽭 | ||
// => random stuff: l3m;Hf9XYbI [YPaxV>U*4-_F!WXQh9>;rH3i l!8.zoh?[utt1OWFQrE ^~8zEQm]~tK | ||
@@ -57,20 +57,18 @@ // ignore case | ||
# Negated Character Sets | ||
Sets like the `.` character will match anything except a new line. In this case, a character with a random char code between 0 and 65535 will be generated. If you want to overwrite this function you can change the `anyRandChar` function in the randexp object. | ||
# Default Range | ||
The default generated character range includes printable ASCII. In order to add or remove characters, | ||
a `defaultRange` attribute is exposed. you can `subtract(from, to)` and `add(from, to)` | ||
```js | ||
var randexp = new RandExp(/./); | ||
randexp.anyRandChar = function() { | ||
return 'c'; | ||
}; | ||
var randexp = new RandExp(/random stuff: .+/); | ||
randexp.defaultRange.subtract(32, 126); | ||
randexp.defaultRange.add(0, 65535); | ||
randexp.gen(); | ||
// => random stuff: 湐箻ໜ䫴㳸長���邓蕲뤀쑡篷皇硬剈궦佔칗븛뀃匫鴔事좍ﯣ⭼ꝏ䭍詳蒂䥂뽭 | ||
``` | ||
If using `RandExp.sugar()` | ||
# Custom PRNG | ||
The default randomness is provided by `Math.random()`. If you need to use a seedable or cryptographic PRNG you | ||
can override `RandExp.prototype.randInt` or `randexp.randInt` (where `randexp` is an instance of `RandExp`. `randInt(from, to)` accepts an inclusive range and returns a randomly selected | ||
number within that range. | ||
```js | ||
var regexp = /./; | ||
regexp.anyRandChar = function() { | ||
return 'c'; | ||
}; | ||
``` | ||
@@ -102,5 +100,3 @@ # Infinite Repetitionals | ||
Other cancelling out character sets like `/[^\D]/` are bad too. These are the same as `/[\d]/`. Except it will be slow in generating a matching string because it will first generate a random character and then check if it's in the set inside. It will take a while to randomly generate an integer out of 65535 characters. | ||
# Install | ||
@@ -107,0 +103,0 @@ ### Node.js |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11529
2
189
119
1
+ Addeddiscontinuous-range@1.0.0
+ Addeddiscontinuous-range@1.0.0(transitive)
+ Addedret@0.1.10(transitive)
- Removedret@0.1.15(transitive)
Updatedret@0.1.10