Comparing version 0.1.1 to 0.3.0
@@ -1,648 +0,224 @@ | ||
// | ||
// randexp | ||
// Copyright (C) 2011 by Roly Fentanes (https://github.com/fent) | ||
// MIT License | ||
// | ||
var ret = require('ret') | ||
, types = ret.types | ||
// returns random number in the rane [a, b] | ||
var randInt = function(a, b) { | ||
return a + Math.floor(Math.random() * (1 + b - a)); | ||
}; | ||
(function() { | ||
var randexp = {}; | ||
if (typeof module !== 'undefined' && module.exports) { | ||
module.exports = randexp; | ||
} | ||
// if code is alphabetic, converts to other case | ||
// if not alphabetic, returns back code | ||
var toOtherCase = function(code) { | ||
return code + (97 <= code && code <= 122 ? -32 : | ||
65 <= code && code <= 90 ? 32 : 0) | ||
}; | ||
var types = randexp.types = { | ||
POSITION : 0 | ||
, CLASS : 2 | ||
, RANGE : 3 | ||
, CLAUSE : 6 | ||
, REFERENCE : 7 | ||
// returns subset of [a, b] if [from, to] is in it | ||
var range = function(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; | ||
}; | ||
, INTS: '0123456789' | ||
, WORDS: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' | ||
, WHITESPACE: ' \f\n\r\t\v\u00A0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u2028\u2029\u202f\u205f\u3000' | ||
}; | ||
// returns true if character is in class set | ||
var inClass = function(set, code, ignoreCase) { | ||
for (var i = 0, l = set.length; i < l; i++) { | ||
var token = set[i]; | ||
switch (token.type) { | ||
case types.CHAR: | ||
var v = token.value; | ||
if (v === code || (ignoreCase && toOtherCase(v) === code)) { | ||
return true; | ||
} | ||
break; | ||
// | ||
// All of these are private and only used by randexp | ||
// it's assumed that they will always be called with the correct input | ||
// | ||
case types.RANGE: | ||
var r; | ||
if (token.from <= code && code <= token.to || (ignoreCase && | ||
(((r = range(97, 122, token.from, token.to)) !== false && | ||
r.from <= code && code <= r.to) || | ||
((r = range(65, 90, token.from, token.to)) !== false && | ||
r.from <= code && code <= r.to)))) { | ||
return true; | ||
} | ||
break; | ||
const CTRL = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?' | ||
, SLSH = { '0': 0, 't': 9, 'n': 10, 'v': 11, 'f': 12, 'r': 13 } | ||
var util = randexp.util = { | ||
// returns random character from string | ||
rndChar: function(str) { | ||
return str[Math.floor(Math.random() * str.length)]; | ||
}, | ||
// returns any random character | ||
anyRndChar: function() { | ||
return String.fromCharCode(util.rndInt(0, 65535)); | ||
}, | ||
// finds character representations in str and convert all to | ||
// their respective characters | ||
strToChars: function(str) { | ||
var chars_regex = /\\(?:u([A-F0-9]{4})|x([A-F0-9]{2})|(0?[0-7]{2})|c([@A-Z\[\\\]^?])|([0tnvfr]))/g; | ||
str = str.replace(chars_regex, function(s, a16, b16, c8, dctrl, eslsh) { | ||
var code = a16 ? parseInt(a16, 16) : | ||
b16 ? parseInt(b16, 16) : | ||
c8 ? parseInt(c8, 8) : | ||
dctrl ? CTRL.indexOf(dctrl) : | ||
eslsh ? SLSH[eslsh] : undefined; | ||
var c; | ||
if (code !== undefined) { | ||
c = String.fromCharCode(code); | ||
// escape special regex characters | ||
c = c.replace(/[[\]{}^$.|?*+()]/g, function(n) { | ||
return '\\' + n; | ||
}); | ||
} else { | ||
c = '\\' + eslsh; | ||
case types.CLASS: | ||
if (inClass(token.set, code, ignoreCase)) { | ||
return true; | ||
} | ||
} | ||
} | ||
return c; | ||
}); | ||
return false; | ||
}; | ||
return str; | ||
}, | ||
// determines if a character code is alphabetic and decide | ||
// to switch case randomly | ||
var char = function(code, ignoreCase) { | ||
return String.fromCharCode(ignoreCase && Math.random() > 0.5 ? | ||
toOtherCase(code) : code); | ||
}; | ||
// returns random number in the rane [a, b] | ||
rndInt: function(a, b) { | ||
return a + Math.floor(Math.random() * (1 + b - a)); | ||
}, | ||
// generate random string modeled after given tokens | ||
var gen = function(token, groups) { | ||
switch (token.type) { | ||
// returns index of first occurance of b that is not preceeded by a | ||
findChar: function(str) { | ||
var a = '\\' | ||
, b = ']' | ||
, offset = 0 | ||
; | ||
case types.ROOT: | ||
case types.GROUP: | ||
if (token.notFollowedBy) { return ''; } | ||
while (true) { | ||
var i = str.indexOf(b); | ||
if (str[i - 1] === a) { | ||
str = str.slice(++i); | ||
offset += i; | ||
} else { | ||
return offset + i; | ||
} | ||
// insert placeholder until group string is generated | ||
if (token.remember) { | ||
var groupNumber = groups.push(true) - 1; | ||
} | ||
}, | ||
var stack = token.options ? | ||
token.options[Math.floor(Math.random() * token.options.length)] : | ||
token.stack; | ||
// returns characters represented by range | ||
range: function(match, from, to) { | ||
var chars = types.WORDS; | ||
return chars.slice(chars.indexOf(from), chars.indexOf(to) + 1); | ||
}, | ||
var str = ''; | ||
for (var i = 0, l = stack.length; i < l; i++) { | ||
str += gen.call(this, stack[i], groups); | ||
} | ||
// changes ranges in class to characters | ||
classRange: function(str) { | ||
return str.replace(/(\w)-(\w)/g, util.range); | ||
}, | ||
if (token.remember) { groups[groupNumber] = str; } | ||
return str; | ||
break; | ||
// returns an alphabetic string with both cases of the given char | ||
toBothCases: function(c) { | ||
var chars = c; | ||
case types.POSITION: | ||
// do nothing for now | ||
return ''; | ||
// find out if c is lower or upper case | ||
chars += c['to' + (c === c.toLowerCase() ? 'Upper' : 'Lower') + 'Case'](); | ||
return chars; | ||
}, | ||
case types.CLASS: | ||
// if this class is an except class i.e. [^abc] | ||
// generate a random character until one that isnt in this class | ||
// is found | ||
if (token.not) { | ||
while (true) { | ||
var c = this.anyRandChar() | ||
, code = c.charCodeAt(0) | ||
// adds characters of the other case based on ignoreCase | ||
inCaseClass: function(str, ignoreCase) { | ||
if (ignoreCase) { | ||
str = str.replace(/[a-zA-Z]/g, function(c) { | ||
return util.toBothCases(c); | ||
}); | ||
if (inClass(token.set, code, this.ignoreCase)) { continue; } | ||
return c; | ||
} | ||
return str; | ||
// otherwise, pick a random token in the class set | ||
} else { | ||
return str; | ||
return gen.call(this, | ||
token.set[Math.floor(Math.random() * token.set.length)]); | ||
} | ||
}, | ||
// if ignoreCase is on, change one character into a class | ||
// to allow for the other case to exist | ||
inCaseChar: function(c, ignoreCase) { | ||
if (ignoreCase && /[a-zA-Z]/.test(c)) { | ||
case types.RANGE: | ||
return char(randInt(token.from, token.to), this.ignoreCase); | ||
break; | ||
return { | ||
type: types.CLASS | ||
, chars: util.toBothCases(c) | ||
}; | ||
} else { | ||
return c; | ||
} | ||
}, | ||
case types.REPETITION: | ||
// randomly generate number between min and max | ||
var n = randInt(token.min, | ||
token.max === Infinity ? token.min + this.max : token.max); | ||
// concactenates continous strings in array | ||
// if it contains one element, returns that one element | ||
// otherwise returns array | ||
flatten: function(arr) { | ||
for (var i = 0; i < arr.length - 1; i++) { | ||
if (typeof arr[i] === 'string') { | ||
var k = i + 1; | ||
while (true) { | ||
var el = arr[k]; | ||
if (typeof el === 'string') { | ||
arr[i] += el; | ||
arr.splice(k, 1); | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
var str = ''; | ||
for (var i = 0; i < n; i++) { | ||
str += gen.call(this, token.value, groups); | ||
} | ||
return arr.length === 1 ? arr[0] : arr; | ||
}, | ||
return str; | ||
// reduces a group to a simple form | ||
// if its stack only has one element, make it the stack | ||
// if it doesn't contain a PIPE or is not to be rememebered, | ||
// return just its stack | ||
reduce: function(group) { | ||
if (group.stack) { | ||
group.stack = util.flatten(group.stack) | ||
} else if (group.options) { | ||
for (var i = 0, l = group.options.length; i < l; i++) { | ||
group.options[i] = util.flatten(group.options[i]); | ||
} | ||
} | ||
case types.REFERENCE: | ||
return groups[token.value - 1]; | ||
return group.options || group.group || group.value ? group : group.stack; | ||
}, | ||
case types.CHAR: | ||
return char(token.value, this.ignoreCase); | ||
} | ||
}; | ||
// gets index + 1 of next closing paranthesis in str | ||
// that is not cancelled out by an opening one | ||
nextClose: function(str) { | ||
var n = 0; | ||
for (var i = 0, l = str.length; i < l; i++) { | ||
var c = str[i]; | ||
switch (c) { | ||
case ')': | ||
if (n === 0) { | ||
return i + 1; | ||
} | ||
n--; | ||
break; | ||
case '(': | ||
n++; | ||
break; | ||
// skip escaped characters | ||
case '\\': | ||
i++; | ||
break; | ||
} | ||
} | ||
// constructor | ||
// takes either a regexp or a string with modifiers | ||
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; | ||
} | ||
}; | ||
// predefined objects | ||
const | ||
WORD_BOUNDARY_POSITION = { | ||
type: types.POSITION | ||
, value: 'b' | ||
} | ||
, NONWORD_BOUNDARY_POSITION = { | ||
type: types.POSITION | ||
, value: 'B' | ||
} | ||
, BEGIN_POSITION = { | ||
type: types.POSITION | ||
, value: '^' | ||
if (typeof regexp.anyRandChar === 'function') { | ||
this.anyRandChar = regexp.anyRandChar; | ||
} | ||
, END_POSITION = { | ||
type: types.POSITION | ||
, value: '$' | ||
} | ||
regexp = regexp.source; | ||
, WORDS_CLASS = { | ||
type: types.CLASS | ||
, chars: types.WORDS | ||
} | ||
, ANTI_WORDS_CLASS = { | ||
type: types.CLASS | ||
, chars: types.WORDS | ||
, not: true | ||
} | ||
, INTS_CLASS = { | ||
type: types.CLASS | ||
, chars: types.INTS | ||
} | ||
, ANTI_INTS_CLASS = { | ||
type: types.CLASS | ||
, chars: types.INTS | ||
, not: true | ||
} | ||
, WHITESPACE_CLASS = { | ||
type: types.CLASS | ||
, chars: types.WHITESPACE | ||
} | ||
, ANTI_WHITESPACE_CLASS = { | ||
type: types.CLASS | ||
, chars: types.WHITESPACE | ||
, not: true | ||
} | ||
, ANY_CHAR_CLASS = { | ||
type: types.CLASS | ||
, chars: '\n' | ||
, not: true | ||
} | ||
; | ||
} 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); | ||
}; | ||
// reads regex string and separates everything into tokens | ||
var tokenize = function(str, ignoreCase, multiline) { | ||
var i = 0, c, | ||
start = { type: types.CLAUSE, stack: []}, | ||
// keep track of last clause and stack | ||
lastGroup = start, | ||
last = start.stack, | ||
groupStack = []; | ||
// 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; | ||
// decode a few escaped characters | ||
str = util.strToChars(str); | ||
// returns any random character | ||
RandExp.prototype.anyRandChar = function() { | ||
return String.fromCharCode(randInt(0, 65535)); | ||
}; | ||
// iterate through each character in string | ||
while (i < str.length) { | ||
c = str[i++]; | ||
// generates the random string | ||
RandExp.prototype.gen = function() { | ||
return gen.call(this, this.tokens, []); | ||
}; | ||
switch (c) { | ||
// handle escaped characters, inclues a few classes | ||
case '\\': | ||
c = str[i++] | ||
switch (c) { | ||
case 'b': | ||
last.push(WORD_BOUNDARY_POSITION); | ||
break; | ||
// enables use of randexp with a shorter calls | ||
// saves the RandExp object into the regex | ||
var randexp = RandExp.randexp = function(regexp, m) { | ||
var randexp; | ||
case 'B': | ||
last.push(NONWORD_BOUNDARY_POSITION); | ||
break; | ||
case 'w': | ||
last.push(WORDS_CLASS); | ||
break; | ||
case 'W': | ||
last.push(ANTI_WORDS_CLASS); | ||
break; | ||
case 'd': | ||
last.push(INTS_CLASS); | ||
break; | ||
case 'D': | ||
last.push(ANTI_INTS_CLASS); | ||
break; | ||
case 's': | ||
last.push(WHITESPACE_CLASS); | ||
break; | ||
case 'S': | ||
last.push(ANTI_WHITESPACE_CLASS); | ||
break; | ||
default: | ||
if (/\d/.test(c)) { | ||
last.push({ | ||
type: types.REFERENCE | ||
, value: parseInt(c) | ||
}); | ||
// escaped character | ||
} else { | ||
last.push(c); | ||
} | ||
} | ||
break; | ||
// positionals | ||
case '^': | ||
last.push(BEGIN_POSITION); | ||
break; | ||
case '$': | ||
last.push(END_POSITION); | ||
break; | ||
// handle classes | ||
case '[': | ||
c = str[i]; | ||
if (c === '\\' && str.slice(i + 1).indexOf('b]') === 0) { | ||
i += 3; | ||
last.push('\u0008'); | ||
} else { | ||
var not; | ||
if (c === '^') { | ||
not = true; | ||
i++; | ||
} else { | ||
not = false; | ||
} | ||
// get all the characters in class | ||
var strPart = str.slice(i) | ||
, l = util.findChar(strPart) | ||
, chars = strPart.substring(0, l) | ||
; | ||
// increase index by length of class | ||
i += l + 1; | ||
last.push({ | ||
type: types.CLASS | ||
, chars: util.inCaseClass(util.classRange(chars), ignoreCase) | ||
, not: not | ||
}); | ||
} | ||
break; | ||
// class of any character except \n | ||
case '.': | ||
last.push(ANY_CHAR_CLASS); | ||
break; | ||
// push group onto stack | ||
case '(': | ||
c = str[i]; | ||
var remember; | ||
if (c === '?') { | ||
c = str[i + 1]; | ||
i += 2; | ||
// match only if not followed by this | ||
// skip | ||
if (c === '!') { | ||
i += util.nextClose(str.slice(i)); | ||
break; | ||
} | ||
remember = false; | ||
} else { | ||
remember = true; | ||
} | ||
// create group | ||
var group = { | ||
type: types.CLAUSE | ||
, group: remember | ||
, stack: [] | ||
}; | ||
// insert subgroup into current group stack | ||
last.push(group); | ||
// remember the current group for when the group closes | ||
groupStack.push(lastGroup); | ||
// make this new group the current group | ||
lastGroup = group; | ||
last = group.stack; | ||
break; | ||
// pop group out of stack | ||
case ')': | ||
lastGroup = groupStack.pop(); | ||
// check if this group has a PIPE | ||
// to get back the correct last stack | ||
last = lastGroup.options ? lastGroup.options[lastGroup.options.length - 1] : lastGroup.stack; | ||
// make sure the last group in the last stack is reduced | ||
var lasti = last.length - 1; | ||
last[lasti] = util.reduce(last[lasti]); | ||
break; | ||
// use pipe character to give more choices | ||
case '|': | ||
// create array where options are if this is the first PIPE | ||
// in this clause | ||
if (!lastGroup.options) { | ||
lastGroup.options = [lastGroup.stack]; | ||
delete lastGroup.stack; | ||
} | ||
// create a new stack and add to options for rest of clause | ||
var stack = []; | ||
lastGroup.options.push(stack); | ||
last = stack; | ||
break; | ||
// repetition | ||
// for every repetition, remove last element from last stack | ||
// then insert back a RANGE object | ||
// this design is chosen because there could be more than | ||
// one repetition symbols in a regex i.e. a?+{2,3} | ||
case '{': | ||
var rs = /^(\d+)(,(\d+)?)?\}/.exec(str.slice(i)) | ||
, min = parseInt(rs[1]) | ||
, max = rs[2] ? rs[3] ? parseInt(rs[3]) : Infinity : min | ||
; | ||
i += rs[0].length; | ||
var popped = last.pop(); | ||
// if min matches max, pre-generate the output | ||
if (min === max) { | ||
for (var k = 0; k < min; k++) { | ||
last.push(popped); | ||
} | ||
} else { | ||
last.push({ | ||
type: types.RANGE | ||
, min: min | ||
, max: max | ||
, value: popped | ||
}); | ||
} | ||
break; | ||
case '?': | ||
var popped = last.pop(); | ||
last.push({ | ||
type: types.RANGE | ||
, min: 0 | ||
, max: 1 | ||
, value: popped | ||
}); | ||
break; | ||
case '+': | ||
var popped = last.pop(); | ||
last.push({ | ||
type: types.RANGE | ||
, min: 1 | ||
, max: Infinity | ||
, value: popped | ||
}); | ||
break; | ||
case '*': | ||
var popped = last.pop(); | ||
last.push({ | ||
type: types.RANGE | ||
, min: 0 | ||
, max: Infinity | ||
, value: popped | ||
}); | ||
break; | ||
// default is a character that is not \[](){}?+*^$ | ||
default: | ||
// if ignoreCase is on, turn alpha character into a class | ||
// with both lower and upper case versions of the character | ||
last.push(util.inCaseChar(c, ignoreCase)); | ||
} | ||
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; | ||
} | ||
return util.reduce(start); | ||
}; | ||
// generate random string modeled after given tokens | ||
var gen = function(obj, max, anyRndChar, groups) { | ||
// check if obj is String | ||
if (typeof obj === 'string') { | ||
return obj; | ||
if (typeof regexp.anyRandChar === 'function') { | ||
randexp.anyRandChar = regexp.anyRandChar; | ||
} | ||
// check if obj is Array | ||
if (obj.length) { | ||
var str = ''; | ||
} | ||
for (var i = 0, l = obj.length; i < l; i++) { | ||
str += gen(obj[i], max, anyRndChar, groups); | ||
} | ||
return randexp.gen(); | ||
}; | ||
return str; | ||
} | ||
// otherwise obj must be an Object | ||
switch (obj.type) { | ||
case types.POSITION: | ||
// do nothing for now | ||
return ''; | ||
case types.CLASS: | ||
// if this class is an except class i.e. [^abc] | ||
// generate a random character until one that isnt in this class | ||
// is found | ||
if (obj.not) { | ||
while (true) { | ||
var c = anyRndChar(); | ||
if (obj.chars.indexOf(c) === -1) { | ||
return c; | ||
} | ||
} | ||
// otherwise, generate a random character from the class | ||
} else { | ||
return util.rndChar(obj.chars); | ||
} | ||
case types.RANGE: | ||
var n = util.rndInt(obj.min, | ||
obj.max === Infinity ? obj.min + max : obj.max) | ||
, str = '' | ||
; | ||
for (var i = 0; i < n; i++) { | ||
str += gen(obj.value, max, anyRndChar, groups); | ||
} | ||
return str; | ||
case types.CLAUSE: | ||
// check if this clause is between a pipe | ||
if (obj.options) { | ||
var n = util.rndInt(0, obj.options.length - 1); | ||
return gen(obj.options[n], max, anyRndChar, groups); | ||
// otherwise this must be a group | ||
} else { | ||
var value = gen(obj.stack, max, anyRndChar, groups); | ||
groups.push(value); | ||
return value; | ||
} | ||
case types.REFERENCE: | ||
return groups[obj.value - 1]; | ||
} | ||
// this enables sugary /regexp/.gen syntax | ||
RandExp.sugar = function() { | ||
RegExp.prototype.gen = function() { | ||
return randexp(this); | ||
}; | ||
}; | ||
// add randexp to global RegExp prototype | ||
// this enables sugary //.gen syntax | ||
RegExp.prototype.__defineGetter__('gen', function() { | ||
if (this._randexp === undefined) { | ||
this._randexp = tokenize(this.source, this.ignoreCase, this.multiline); | ||
this._max = this._max || 100; | ||
this._anyRndChar = this._anyRndChar || util.anyRndChar; | ||
} | ||
return gen(this._randexp, this._max, this._anyRndChar, []); | ||
}); | ||
})(); | ||
// export to browser | ||
if (typeof window !== 'undefined') { | ||
window.RandExp = RandExp; | ||
} |
@@ -5,4 +5,4 @@ { | ||
"keywords": ["regex", "regexp", "regular expression", "random"], | ||
"version": "0.1.1", | ||
"homepage": "http://fent.github.com/randexp.js", | ||
"version": "0.3.0", | ||
"homepage": "http://fent.github.com/randexp.js/", | ||
"repository": { | ||
@@ -15,3 +15,3 @@ "type": "git", | ||
"scripts": { | ||
"test": "vows test/*-test.js --spec" | ||
"test": "mocha -R spec test/*-test.js" | ||
}, | ||
@@ -24,4 +24,7 @@ "directories": { | ||
}, | ||
"dependencies": { | ||
"ret": "0.1.x" | ||
}, | ||
"devDependencies": { | ||
"vows": "0.5.x" | ||
"mocha": "0.7.x" | ||
}, | ||
@@ -28,0 +31,0 @@ "licenses": [ { |
@@ -8,22 +8,44 @@ # randexp.js [![Build Status](https://secure.travis-ci.org/fent/randexp.js.png)](http://travis-ci.org/fent/randexp.js) | ||
```js | ||
require('randexp'); // must require if using node | ||
var RandExp = require('randexp'); | ||
// supports grouping and piping | ||
/hello+ (world|to you)/.gen; | ||
new RandExp(/hello+ (world|to you)/).gen(); | ||
// => hellooooooooooooooooooo world | ||
// classes and ranges and references | ||
/<([a-z]\w{0,20})>foo<\1>/.gen; | ||
new RandExp(/<([a-z]\w{0,20})>foo<\1>/).gen(); | ||
// => <m5xhdg>foo<m5xhdg> | ||
// wildcard class | ||
/random stuff: .+/.gen; | ||
new RandExp(/random stuff: .+/).gen(); | ||
// => random stuff: 湐箻ໜ䫴㳸長���邓蕲뤀쑡篷皇硬剈궦佔칗븛뀃匫鴔事좍ﯣ⭼ꝏ䭍詳蒂䥂뽭 | ||
// ignore case | ||
/xxx xtreme dragon warrior xxx/i.gen; | ||
new RandExp(/xxx xtreme dragon warrior xxx/i).gen(); | ||
// => xxx xtReME dRAGON warRiOR xXX | ||
// dynamic regexp shortcut | ||
new RandExp('(sun|mon|tue|wednes|thurs|fri|satur)day', 'i'); | ||
// is the same as | ||
new RandExp(new RegExp('(sun|mon|tue|wednes|thurs|fri|satur)day', 'i')); | ||
``` | ||
If you're only going to use `gen()` once with a regexp and want slightly shorter syntax for it | ||
```js | ||
var randexp = require('randexp').randexp; | ||
randexp(/[1-6]/); // 4 | ||
randexp('great|good( job)?|excellent'); // great | ||
``` | ||
If you miss the old syntax | ||
```js | ||
require('randexp').sugar(); | ||
/yes|no|maybe|i don't know/.gen(); // maybe | ||
``` | ||
# Motivation | ||
@@ -44,19 +66,35 @@ Regular expressions are used in every language, every programmer is familiar with them. Regex can be used to easily express complex strings. What better way to generate a random string than with a tool you can easily use to express the string any way you want? | ||
Classes 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 do | ||
Classes 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. | ||
```js | ||
var r = /./; | ||
r._anyRndChar = function() { | ||
return the char you want here; | ||
var randexp = new RandExp(/./); | ||
randexp.anyRandChar = function() { | ||
return 'c'; | ||
}; | ||
``` | ||
Ranges like `*`, `+`, and `{3,}` have an infinite max range. In this case, randexp looks at its min and adds 100 to it to get a useable max value. If you want to use another int other than 100 you can do | ||
If using `RandExp.sugar()` | ||
```js | ||
var r = /(hi)*/; | ||
r._max = 1000000; | ||
var regexp = /./; | ||
regexp.anyRandChar = function() { | ||
return 'c'; | ||
}; | ||
``` | ||
Repetitionals such as `*`, `+`, and `{3,}` have an infinite max range. In this case, randexp looks at its min and adds 100 to it to get a useable max value. If you want to use another int other than 100 you can change the `max` property in the randexp object. | ||
```js | ||
var randexp = new RandExp(/no{1,}/); | ||
randexp.max = 1000000; | ||
``` | ||
With `RandExp.sugar()` | ||
```js | ||
var regexp = /(hi)*/; | ||
regexp.max = 1000000; | ||
``` | ||
# Install | ||
@@ -73,3 +111,3 @@ ### Node.js | ||
# Tests | ||
Tests are written with [vows](http://vowsjs.org/) | ||
Tests are written with [mocha](http://visionmedia.github.com/mocha/) | ||
@@ -80,6 +118,4 @@ ```bash | ||
I should really write browser tests too, sometime. | ||
# License | ||
MIT |
@@ -1,38 +0,27 @@ | ||
var tests = require('./tests.js') | ||
, vows = require('vows') | ||
, assert = require('assert') | ||
; | ||
var tests = require('./tests.js') | ||
, assert = require('assert') | ||
, RandExp = require('..') | ||
require('../'); | ||
for (var type in tests) { | ||
if (tests.hasOwnProperty(type)) { | ||
describe(type, function() { | ||
// matcher that will be used in all tests | ||
// all regexps passed to rndexp must match their result | ||
var match = function(regexp, desc) { | ||
var obj = { | ||
topic: function() { | ||
return regexp.gen; | ||
} | ||
}; | ||
for (var row in tests[type]) { | ||
if (tests[type].hasOwnProperty(row)) { | ||
(function(test) { | ||
obj[desc] = function(rndexp) { | ||
assert.isTrue(regexp.test(rndexp)); | ||
}; | ||
it(test.desc, function() { | ||
var randexp = new RandExp(test.regexp); | ||
return obj; | ||
}; | ||
assert.equal(true, test.regexp.test(randexp.gen())); | ||
assert.equal(true, test.regexp.test(randexp.gen())); | ||
}); | ||
})(tests[type][row]); | ||
} | ||
} | ||
for (var type in tests) { | ||
var suite = vows.describe(type), | ||
batch = {}; | ||
for (var row in tests[type]) { | ||
var regexp = tests[type][row].regexp, | ||
desc = tests[type][row].desc; | ||
batch[row] = match(regexp, desc); | ||
}); | ||
} | ||
suite.addBatch(batch); | ||
suite.export(module); | ||
} |
@@ -208,5 +208,5 @@ module.exports = { | ||
regexp: /(\w+)\s+\1/, | ||
desc: '"\x" (where x is a number from 1 to 9) when added to the end of a regular expression pattern allows youo to back reference a subpattern within the pattern, so the value of the subpatterns is remembered and used as part of the matching.' | ||
desc: '"\x" (where x is a number from 1 to 9) when added to the end of a regular expression pattern allows you to back reference a subpattern within the pattern, so the value of the subpatterns is remembered and used as part of the matching.' | ||
} | ||
} | ||
}; |
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
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
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
118
18735
1
11
412
1
1
+ Addedret@0.1.x
+ Addedret@0.1.15(transitive)