expressive-ts
Version:
A functional programming library designed to simplify building complex regular expressions
358 lines (357 loc) • 10.2 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
/**
* Expressions allow for composition of complex regular expressions in a clear, concise,
* and declarative manner.
*
* Every expression should begin with a call to `compile`, followed by the individual
* methods used to build the regular expression. In addition, regular expression `Flags`
* can be switched on or off at any time.
*
* A simple example would be to perform a case-insensitive search of a string for
* the pattern `foo`, optionally followed by `bar`. With `expressive-ts`, representing
* this logic is as simple as the following
*
* @example
* import { pipe } from 'fp-ts/es6/function'
* import * as E from 'expressive-ts/lib/Expression'
*
* const expression = pipe(
* E.compile,
* E.caseInsensitive,
* E.string('foo'),
* E.maybe('bar')
* )
*
* const regex = pipe(expression, E.toRegex)
* regex.test('foo') // => true
* regex.test('foobar') // => true
* regex.test('bar') // => false
*
* pipe(expression, E.toRegexString) // => /(?:foo)(?:bar)?/i
*
* @since 0.0.1
*/
import * as M from 'fp-ts/es6/Monoid';
import * as T from 'fp-ts/es6/Traced';
import { identity, flow } from 'fp-ts/es6/function';
// -------------------------------------------------------------------------------------
// destructors
// -------------------------------------------------------------------------------------
var toFlags = function (flags) {
var s = '';
s += flags.allowMultiple ? 'g' : '';
s += flags.caseInsensitive ? 'i' : '';
s += flags.lineByLine ? 'm' : '';
s += flags.singleLine ? 's' : '';
s += flags.unicode ? 'u' : '';
s += flags.sticky ? 'y' : '';
return s;
};
/**
* @category destructors
* @since 0.0.1
*/
export var toRegex = function (builder) {
var expression = builder(monoidExpression.empty);
var flags = toFlags(expression.flags);
return new RegExp(expression.pattern, flags);
};
/**
* @category destructors
* @since 0.0.1
*/
export var toRegexString = function (builder) {
return "" + toRegex(builder);
};
// -------------------------------------------------------------------------------------
// pipeables
// -------------------------------------------------------------------------------------
/**
* @category pipeables
* @since 0.0.1
*/
export var withMultiple = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { allowMultiple: flag }) }));
}; };
/**
* @category pipeables
* @since 0.0.1
*/
export var withCaseInsensitive = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { caseInsensitive: flag }) }));
}; };
/**
* @category pipeables
* @since 0.0.1
*/
export var withLineByLine = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { lineByLine: flag }) }));
}; };
/**
* @category pipeables
* @since 0.0.1
*/
export var withSingleLine = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { singleLine: flag }) }));
}; };
/**
* @category pipeables
* @since 0.0.1
*/
export var withSticky = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { sticky: flag }) }));
}; };
/**
* @category pipeables
* @since 0.0.1
*/
export var withUnicode = function (flag) { return function (wa) {
return wa(__assign(__assign({}, monoidExpression.empty), { flags: __assign(__assign({}, monoidFlags.empty), { unicode: flag }) }));
}; };
// -------------------------------------------------------------------------------------
// combinators
// -------------------------------------------------------------------------------------
var add = function (value) {
return T.map(function (e) { return (__assign(__assign({}, e), { source: M.fold(M.monoidString)([e.source, value]), pattern: M.fold(M.monoidString)([e.prefix, e.source, value, e.suffix]) })); });
};
var prefix = function (value) {
return T.map(function (e) { return (__assign(__assign({}, e), { prefix: M.fold(M.monoidString)([e.prefix, value]) })); });
};
var suffix = function (value) {
return T.map(function (e) { return (__assign(__assign({}, e), { suffix: M.fold(M.monoidString)([value, e.suffix]) })); });
};
/**
* @category combinators
* @since 0.0.1
*/
export var allowMultiple = function (wa) { return C.extend(wa, withMultiple(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var caseInsensitive = function (wa) { return C.extend(wa, withCaseInsensitive(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var lineByLine = function (wa) { return C.extend(wa, withLineByLine(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var singleLine = function (wa) { return C.extend(wa, withSingleLine(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var sticky = function (wa) { return C.extend(wa, withSticky(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var unicode = function (wa) { return C.extend(wa, withUnicode(true)); };
/**
* @category combinators
* @since 0.0.1
*/
export var compile = identity;
/**
* @category combinators
* @since 0.0.1
*/
export var startOfInput = add('^');
/**
* @category combinators
* @since 0.0.1
*/
export var endOfInput = add('$');
/**
* @category combinators
* @since 0.0.1
*/
export var string = function (value) { return add("(?:" + sanitize(value) + ")"); };
/**
* @category combinators
* @since 0.0.1
*/
export var maybe = function (value) { return add("(?:" + sanitize(value) + ")?"); };
/**
* @deprecated
* @category combinators
* @since 0.0.1
*/
export var or = function (value) {
return flow(prefix("(?:"), suffix(")"), add(")|(?:"), string(value));
};
/**
* @category combinators
* @since 0.0.2
*/
export var orExpression = add("|");
/**
* @category combinators
* @since 0.0.1
*/
export var anything = add("(?:.*)");
/**
* @category combinators
* @since 0.0.1
*/
export var anythingBut = function (value) {
return add("(?:[^" + sanitize(value) + "]*)");
};
/**
* @category combinators
* @since 0.0.1
*/
export var something = add("(?:.+)");
/**
* @category combinators
* @since 0.0.1
*/
export var somethingBut = function (value) {
return add("(?:[^" + sanitize(value) + "]+)");
};
/**
* @category combinators
* @since 0.0.1
*/
export var anyOf = function (value) { return add("[" + sanitize(value) + "]"); };
/**
* @category combinators
* @since 0.0.1
*/
export var not = function (value) { return add("(?!" + sanitize(value) + ")"); };
/**
* @category combinators
* @since 0.0.1
*/
export var range = function (from, to) {
return add("[" + sanitize(from) + "-" + sanitize(to) + "]");
};
/**
* @category combinators
* @since 0.0.1
*/
export var lineBreak = add("(?:\\r\\n|\\r|\\n)");
/**
* @category combinators
* @since 0.0.1
*/
export var tab = add("\\t");
/**
* @category combinators
* @since 0.0.1
*/
export var word = add("\\w+");
/**
* @category combinators
* @since 0.0.1
*/
export var digit = add("\\d");
/**
* @category combinators
* @since 0.0.1
*/
export var whitespace = add("\\s");
/**
* @category combinators
* @since 0.0.1
*/
export var zeroOrMore = add("*");
/**
* @category combinators
* @since 0.0.1
*/
export var zeroOrMoreLazy = add("*?");
/**
* @category combinators
* @since 0.0.1
*/
export var oneOrMore = add("+");
/**
* @category combinators
* @since 0.0.1
*/
export var oneOrMoreLazy = add("+?");
/**
* @category combinators
* @since 0.0.1
*/
export var exactly = function (amount) { return add("{" + amount + "}"); };
/**
* @category combinators
* @since 0.0.1
*/
export var atLeast = function (min) { return add("{" + min + ",}"); };
/**
* @category combinators
* @since 0.0.1
*/
export var between = function (min, max) {
return add("{" + min + "," + max + "}");
};
/**
* @category combinators
* @since 0.0.1
*/
export var betweenLazy = function (min, max) {
return add("{" + min + "," + max + "}?");
};
/**
* @category combinators
* @since 0.0.1
*/
export var beginCapture = add("(");
/**
* @category combinators
* @since 0.0.1
*/
export var endCapture = add(")");
// -------------------------------------------------------------------------------------
// instances
// -------------------------------------------------------------------------------------
/**
* @category instances
* @since 0.0.1
*/
export var monoidFlags = M.getStructMonoid({
allowMultiple: M.monoidAny,
caseInsensitive: M.monoidAny,
lineByLine: M.monoidAny,
singleLine: M.monoidAny,
sticky: M.monoidAny,
unicode: M.monoidAny
});
/**
* @category instances
* @since 0.0.1
*/
export var monoidExpression = M.getStructMonoid({
prefix: M.monoidString,
pattern: M.monoidString,
suffix: M.monoidString,
source: M.monoidString,
flags: monoidFlags
});
var C = T.getComonad(monoidExpression);
// -------------------------------------------------------------------------------------
// utils
// -------------------------------------------------------------------------------------
// Regular expression to match meta characters
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp
var toEscape = /([\].|*?+(){}^$\\:=[])/g;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastMatch
var lastMatch = '$&';
// Escape meta characters
var sanitize = function (value) { return value.replace(toEscape, "\\" + lastMatch); };