UNPKG

expressive-ts

Version:

A functional programming library designed to simplify building complex regular expressions

358 lines (357 loc) 10.2 kB
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); };