UNPKG

regexus

Version:

Human Readable Regular Expressions

421 lines (420 loc) 10.8 kB
"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;