regexp-coder
Version:
A Javascript/Typescript RegExp Coder
472 lines • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegExpCoder = void 0;
const regExpSpec_1 = require("./regExpSpec");
const Or = '|';
const Not = '^';
/**
* Regular Expression Coder
*
* @see [Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
*/
class RegExpCoder {
constructor() {
// variables
this.variables = new Map();
// the final regular expression
this.result = '';
// if enable match whole
this.isMatchWhole = false;
}
/**
* Create an instance of RegExpCoder
*/
static new() {
return new RegExpCoder();
}
/**
* Get the last options if there is
* @param exp the expression
*/
getParamOptions(...exp) {
if (!this) {
return undefined;
}
if (!exp) {
return undefined;
}
const lastParam = exp[exp.length - 1];
if (!lastParam) {
return undefined;
}
if (typeof lastParam === 'string' || lastParam instanceof RegExpCoder || lastParam instanceof RegExp) {
return undefined;
}
if (lastParam.expression) {
return undefined;
}
return lastParam;
}
/**
* Apply options to a specific expression
* @param exp the expression
* @param options the options
*/
applyOptions(exp, options) {
if (!this) {
return '';
}
let source = exp;
// case: set
if (options && options.negatedSet) {
// case: negatedSet
source = `[${Not}${source}]`;
}
else if (options && options.set) {
// case: set
source = `[${source}]`;
}
// qualifier
if (options && options.qualifier) {
// groupQualifiedItem's defalt value is true, as qualifier operations are in high priority
if (options.groupQualifiedItem === undefined || options.groupQualifiedItem) {
source = `(${options.notRememberQualifiedItem ? '?:' : ''}${source})`;
}
source = `${source}${options.qualifier}`;
}
// case: group
if (options && options.group) {
if (options.notRemember) {
source = `(?:${source})`;
}
else {
const name = options.name || '';
source = `(${name.length > 0 ? `?<${name}>:` : ''}${source})`;
}
}
return source;
}
/**
* Get the source of an expression or a variable
* @param exp the expression
*/
getSource(exp) {
if (!exp) {
return '';
}
// case: string
if (typeof exp === 'string') {
if (this.variables.has(exp)) {
// case: variable
return this.variables.get(exp);
}
return exp;
}
// case: RegExp
if (exp instanceof RegExp) {
return exp.source;
}
// case: RegExpCoder
if (exp instanceof RegExpCoder) {
return exp.source;
}
// case: RegExpOptions
if (exp.expression) {
return this.applyOptions(this.getSource(exp.expression), exp);
}
// case: RegExpOptions without expression,
// return empty, the option will be used by outside
return '';
}
/**
* create an expression
* @param exp the expression
*/
build(...exp) {
let source = '';
if (!exp) {
return source;
}
const totalOptions = this.getParamOptions(...exp);
exp.forEach((item, index) => {
let itemSource = this.getSource(item);
if (itemSource) {
if (totalOptions && totalOptions.groupItem) {
itemSource = `(${itemSource})`;
}
let or = '';
if (index > 0 && totalOptions && totalOptions.or) {
or = Or;
}
source += `${or}${itemSource}`;
}
});
return this.applyOptions(source, totalOptions);
}
/**
* Encode a raw string to a regular expression string.
* Following special characters will be encoded: `^\.()[]?+*|$`
*
* For set case, encoded characters: `^\-]`
* Rules:
* - escape `^` only when it is the first char
* - escape `-` only when it is not the first char and not the last char
* - escaped `\]` always
* @param raw the raw string
* @param forSet if for set `[]` operation
*/
static encodeRegExp(raw, forSet = false) {
if (!raw) {
return raw;
}
let ret = '';
for (let index = 0; index < raw.length; index += 1) {
const char = raw.charAt(index);
if (forSet) {
let newChar = RegExpCoder.escapedSetChars.get(char);
if (char === '^' && index !== 0) {
newChar = undefined;
}
else if (char === '-' && (index === 0 || index === raw.length - 1)) {
newChar = undefined;
}
ret += newChar || char;
}
else {
const newChar = RegExpCoder.escapedChars.get(char);
ret += newChar || char;
}
}
return ret;
}
/**
* `xy`
* Define a variable
* @param name the variable name
* @param exp the variable expression
*/
define(name, ...exp) {
this.variables.set(name, this.build(...exp, this.getParamOptions(...exp)));
return this;
}
/**
* `(xy)`
* Define a variable for group operation
* @param name the variable name
* @param exp the variable expression
*/
defineGroup(name, ...exp) {
this.variables.set(name, this.build(...exp, Object.assign({ group: true }, this.getParamOptions(...exp))));
return this;
}
/**
* `x|y`
* Define a variable for or operation
* @param name the variable name
* @param exp the variable expression
*/
defineOr(name, ...exp) {
this.variables.set(name, this.build(...exp, Object.assign({ or: true }, this.getParamOptions(...exp))));
return this;
}
/**
* `[xy]`
* Define a variable for set
* @param name the variable name
* @param exp the variable expression
*/
defineSet(name, ...exp) {
this.variables.set(name, this.build(...exp, Object.assign({ set: true }, this.getParamOptions(...exp))));
return this;
}
/**
* `[^xy]`
* Define a variable for negated set
* @param name the variable name
* @param exp the variable expression
*/
defineNegatedSet(name, ...exp) {
this.variables.set(name, this.build(...exp, Object.assign({ negatedSet: true }, this.getParamOptions(...exp))));
return this;
}
/**
* `x(?=y)`
* Define a lookahead variable
* @param name the variable name
* @param exp the variable expression
* @param exp2 the variable expression
* @param options the build options
*/
defineLookahead(name, exp, exp2, options) {
this.variables.set(name, this.build(`${this.getSource(exp)}(?=${this.getSource(exp2)})`, options));
return this;
}
/**
* `x(?!y)`
* Define a negated lookahead variable
* @param name the variable name
* @param exp the variable expression
* @param exp2 the variable expression
* @param options the build options
*/
defineNegatedLookahead(name, exp, exp2, options) {
this.variables.set(name, this.build(`${this.getSource(exp)}(?!${this.getSource(exp2)})`, options));
return this;
}
/**
* `(?<=y)x`
* Define a lookbehind variable
* @param name the variable name
* @param exp the variable expression
* @param exp2 the variable expression
* @param options the build options
*/
defineLookbehind(name, exp, exp2, options) {
this.variables.set(name, this.build(`(?<=${this.getSource(exp2)})${this.getSource(exp)}`, options));
return this;
}
/**
* `(?