regexus
Version:
Human Readable Regular Expressions
421 lines (420 loc) • 10.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class RegExpBuilder {
constructor() {
this._flags = '';
this._literal = [];
this._groupsUsed = 0;
this._min = -1;
this._max = -1;
this._of = '';
this._ofAny = false;
this._ofGroup = -1;
this._from = '';
this._notFrom = '';
this._like = null;
this._either = null;
this._reluctant = false;
this._capture = false;
this._captureName = null;
this.clear();
}
clear() {
this._min = -1;
this._max = -1;
this._of = '';
this._ofAny = false;
this._ofGroup = -1;
this._from = '';
this._notFrom = '';
this._like = null;
this._either = null;
this._reluctant = false;
this._capture = false;
}
flushState() {
if (this._of !== '' ||
this._ofAny ||
this._ofGroup > 0 ||
this._from !== '' ||
this._notFrom !== '' ||
this._like !== null) {
const captureLiteral = this._capture ? (this._captureName ? `?P<${this._captureName}>` : '') : '?:';
const quantityLiteral = this.getQuantityLiteral();
const characterLiteral = this.getCharacterLiteral();
const reluctantLiteral = this._reluctant ? '?' : '';
this._literal.push(`(${captureLiteral}(?:${characterLiteral})${quantityLiteral}${reluctantLiteral})`);
this.clear();
}
}
getQuantityLiteral() {
if (this._min !== -1) {
if (this._max !== -1) {
return `{${this._min},${this._max}}`;
}
return `{${this._min},}`;
}
return `{0,${this._max}}`;
}
getCharacterLiteral() {
switch (true) {
case this._of !== '':
return this._of;
case this._ofAny:
return '.';
case this._ofGroup > 0:
return `\\${this._ofGroup}`;
case this._from !== '':
return `[${this._from}]`;
case this._notFrom !== '':
return `[^${this._notFrom}]`;
case this._like !== null:
return this._like;
default:
return '';
}
}
/**
* Returns the literal representation of the RegExp
* @returns {string}
*/
getLiteral() {
this.flushState();
return this._literal.join('');
}
combineGroupNumberingAndGetLiteralral(r) {
const literal = this.incrementGroupNumbering(r.getLiteral(), this._groupsUsed);
this._groupsUsed += r._groupsUsed;
return literal;
}
incrementGroupNumbering(literal, increment) {
if (increment > 0) {
literal = literal.replace(/\\(\d+)/g, (groupReference) => {
const groupNumber = parseInt(groupReference.substring(1)) + increment;
return `\\${groupNumber}`;
});
}
return literal;
}
/**
* Returns the usable RegExp
* @returns {RegExp}
*/
getRegExp() {
this.flushState();
return new RegExp(this._literal.join(''), this._flags);
}
addFlag(flag) {
if (this._flags.indexOf(flag) === -1) {
this._flags += flag;
}
return this;
}
ignoreCase() {
return this.addFlag('i');
}
/**
* Makes the RegExp match across multiple lines
* @returns {RegExpBuilder}
*/
multiLine() {
return this.addFlag('m');
}
/**
* Enables global matching of the RegExp
* @returns {RegExpBuilder}
*/
globalMatch() {
return this.addFlag('g');
}
/**
* Starts the RegExp matching at the beginning of the input
* @returns {RegExpBuilder}
*/
startOfInput() {
this._literal.push('(?:^)');
return this;
}
/**
* Starts the RegExp matching at the beginning of the line
* @returns {RegExpBuilder}
*/
startOfLine() {
this.multiLine();
return this.startOfInput();
}
/**
* Ends the RegExp matching at the end of the input
* @returns {RegExpBuilder}
*/
endOfInput() {
this.flushState();
this._literal.push('(?:$)');
return this;
}
/**
* Ends the RegExp matching at the end of the line
* @returns {RegExpBuilder}
*/
endOfLine() {
this.multiLine();
return this.endOfInput();
}
/**
* Matches the input string against the RegExp
* @param {string} input
* @returns {boolean}
*/
eitherFind(r) {
if (typeof r === 'string') {
return this.setEither(this.getNew().exactly(1).of(r));
}
return this.setEither(r);
}
setEither(r) {
this.flushState();
this._either = this.combineGroupNumberingAndGetLiteralral(r);
return this;
}
orFind(r) {
if (typeof r === 'string') {
return this.setOr(this.getNew().exactly(1).of(r));
}
return this.setOr(r);
}
anyOf(r) {
if (r.length < 1) {
return this;
}
const firstToken = r.shift();
this.eitherFind(firstToken);
r.forEach((token) => {
this.orFind(token);
});
return this;
}
setOr(r) {
const either = this._either;
const or = this.combineGroupNumberingAndGetLiteralral(r);
if (either === null) {
let lastOr = this._literal[this._literal.length - 1];
lastOr = lastOr.substring(0, lastOr.length - 1);
this._literal[this._literal.length - 1] = lastOr;
this._literal.push(`|(?:${or}))`);
}
else {
this._literal.push(`(?:(?:${either})|(?:${or}))`);
}
this.clear();
return this;
}
neither(r) {
if (typeof r === 'string') {
return this.notAhead(this.getNew().exactly(1).of(r));
}
return this.notAhead(r);
}
nor(r) {
if (this._min === 0 && this._ofAny) {
this._min = -1;
this._ofAny = false;
}
this.neither(r);
return this.min(0).ofAny();
}
exactly(n) {
this.flushState();
this._min = n;
this._max = n;
return this;
}
min(n) {
this.flushState();
this._min = n;
return this;
}
max(n) {
this.flushState();
this._max = n;
return this;
}
of(s) {
this._of = this.sanitize(s);
return this;
}
ofAny() {
this._ofAny = true;
return this;
}
ofGroup(n) {
this._ofGroup = n;
return this;
}
from(s) {
this._from = this.sanitize(s.join(''));
return this;
}
notFrom(s) {
this._notFrom = this.sanitize(s.join(''));
return this;
}
like(r) {
this._like = this.combineGroupNumberingAndGetLiteralral(r);
return this;
}
reluctantly() {
this._reluctant = true;
return this;
}
ahead(r) {
this.flushState();
this._literal.push(`(?=${this.combineGroupNumberingAndGetLiteralral(r)})`);
return this;
}
notAhead(r) {
this.flushState();
this._literal.push(`(?!${this.combineGroupNumberingAndGetLiteralral(r)})`);
return this;
}
asGroup(name = null) {
this._capture = true;
this._captureName = name;
this._groupsUsed++;
return this;
}
then(s) {
return this.exactly(1).of(s);
}
find(s) {
return this.then(s);
}
some(s) {
return this.min(1).from(s);
}
maybeSome(s) {
return this.min(0).from(s);
}
maybe(s) {
return this.max(1).of(s);
}
anything() {
return this.min(0).ofAny();
}
anythingBut(s) {
if (s.length === 1) {
return this.min(1).notFrom([s]);
}
this.notAhead(this.getNew().exactly(1).of(s));
return this.min(0).ofAny();
}
something() {
return this.min(1).ofAny();
}
any() {
return this.exactly(1).ofAny();
}
lineBreak() {
this.flushState();
this._literal.push('(?:\\r\\n|\\r|\\n)');
return this;
}
lineBreaks() {
return this.like(this.getNew().lineBreak());
}
whitespace() {
if (this._min === -1 && this._max === -1) {
this.flushState();
this._literal.push('(?:\\s)');
return this;
}
this._like = '\\s';
return this;
}
notWhitespace() {
if (this._min === -1 && this._max === -1) {
this.flushState();
this._literal.push('(?:\\S)');
return this;
}
this._like = '\\S';
return this;
}
tab() {
this.flushState();
this._literal.push('(?:\\t)');
return this;
}
tabs() {
return this.like(this.getNew().tab());
}
digit() {
this.flushState();
this._literal.push('(?:\\d)');
return this;
}
notDigit() {
this.flushState();
this._literal.push('(?:\\D)');
return this;
}
digits() {
return this.like(this.getNew().digit());
}
notDigits() {
return this.like(this.getNew().notDigit());
}
letter() {
this.exactly(1);
this._from = 'A-Za-z';
return this;
}
notLetter() {
this.exactly(1);
this._notFrom = 'A-Za-z';
return this;
}
letters() {
this._from = 'A-Za-z';
return this;
}
notLetters() {
this._notFrom = 'A-Za-z';
return this;
}
lowerCaseLetter() {
this.exactly(1);
this._from = 'a-z';
return this;
}
lowerCaseLetters() {
this._from = 'a-z';
return this;
}
upperCaseLetter() {
this.exactly(1);
this._from = 'A-Z';
return this;
}
upperCaseLetters() {
this._from = 'A-Z';
return this;
}
append(r) {
this.exactly(1);
this._like = this.combineGroupNumberingAndGetLiteralral(r);
return this;
}
optional(r) {
this.max(1);
this._like = this.combineGroupNumberingAndGetLiteralral(r);
return this;
}
sanitize(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
getNew() {
const className = this.constructor;
return new className();
}
}
exports.default = RegExpBuilder;