Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

randexp

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

randexp - npm Package Compare versions

Comparing version 0.1.1 to 0.3.0

build.js

746

lib/randexp.js

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc