Improve code quality and write tests

This commit is contained in:
Shivam Mathur
2019-09-20 08:11:20 +05:30
parent db44db4b97
commit 43178a7254
3597 changed files with 255478 additions and 785554 deletions

16
node_modules/regexp-tree/dist/bin/regexp-tree.js generated vendored Normal file
View File

@ -0,0 +1,16 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
function main() {
console.info('\n =========================================================\n * CLI commands are moved to the \x1B[1mregexp-tree-cli\x1B[0m package *\n =========================================================\n\n \x1B[1mInstallation:\x1B[0m\n\n npm install -g regexp-tree-cli\n\n \x1B[1mUsage:\x1B[0m\n\n regexp-tree-cli --help\n ');
}
module.exports = main;
if (require.main === module) {
main();
}

View File

@ -0,0 +1,50 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var compatTransforms = require('./transforms');
var _transform = require('../transform');
module.exports = {
/**
* Translates a regexp in new syntax to equivalent regexp in old syntax.
*
* @param string|RegExp|AST - regexp
* @param Array transformsWhitelist - names of the transforms to apply
*/
transform: function transform(regexp) {
var transformsWhitelist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var transformToApply = transformsWhitelist.length > 0 ? transformsWhitelist : Object.keys(compatTransforms);
var result = void 0;
// Collect extra data per transform.
var extra = {};
transformToApply.forEach(function (transformName) {
if (!compatTransforms.hasOwnProperty(transformName)) {
throw new Error('Unknown compat-transform: ' + transformName + '. ' + 'Available transforms are: ' + Object.keys(compatTransforms).join(', '));
}
var handler = compatTransforms[transformName];
result = _transform.transform(regexp, handler);
regexp = result.getAST();
// Collect `extra` transform result.
if (typeof handler.getExtra === 'function') {
extra[transformName] = handler.getExtra();
}
});
// Set the final extras for all transforms.
result.setExtra(extra);
return result;
}
};

View File

@ -0,0 +1,123 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* The `RegExpTree` class provides runtime support for `compat-transpiler`
* module from `regexp-tree`.
*
* E.g. it tracks names of the capturing groups, in order to access the
* names on the matched result.
*
* It's a thin-wrapper on top of original regexp.
*/
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var RegExpTree = function () {
/**
* Initializes a `RegExpTree` instance.
*
* @param RegExp - a regular expression
*
* @param Object state:
*
* An extra state which may store any related to transformation
* data, for example, names of the groups.
*
* - flags - original flags
* - groups - names of the groups, and their indices
* - source - original source
*/
function RegExpTree(re, _ref) {
var flags = _ref.flags,
groups = _ref.groups,
source = _ref.source;
_classCallCheck(this, RegExpTree);
this._re = re;
this._groups = groups;
// Original props.
this.flags = flags;
this.source = source || re.source;
this.dotAll = flags.includes('s');
// Inherited directly from `re`.
this.global = re.global;
this.ignoreCase = re.ignoreCase;
this.multiline = re.multiline;
this.sticky = re.sticky;
this.unicode = re.unicode;
}
/**
* Facade wrapper for RegExp `test` method.
*/
_createClass(RegExpTree, [{
key: 'test',
value: function test(string) {
return this._re.test(string);
}
/**
* Facade wrapper for RegExp `compile` method.
*/
}, {
key: 'compile',
value: function compile(string) {
return this._re.compile(string);
}
/**
* Facade wrapper for RegExp `toString` method.
*/
}, {
key: 'toString',
value: function toString() {
if (!this._toStringResult) {
this._toStringResult = '/' + this.source + '/' + this.flags;
}
return this._toStringResult;
}
/**
* Facade wrapper for RegExp `exec` method.
*/
}, {
key: 'exec',
value: function exec(string) {
var result = this._re.exec(string);
if (!this._groups || !result) {
return result;
}
result.groups = {};
for (var group in this._groups) {
var groupNumber = this._groups[group];
result.groups[group] = result[groupNumber];
}
return result;
}
}]);
return RegExpTree;
}();
module.exports = {
RegExpTree: RegExpTree
};

View File

@ -0,0 +1,69 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to translate `/./s` to `/[\0-\uFFFF]/`.
*/
module.exports = {
// Whether `u` flag present. In which case we transform to
// \u{10FFFF} instead of \uFFFF.
_hasUFlag: false,
// Only run this plugin if we have `s` flag.
shouldRun: function shouldRun(ast) {
var shouldRun = ast.flags.includes('s');
if (!shouldRun) {
return false;
}
// Strip the `s` flag.
ast.flags = ast.flags.replace('s', '');
// Whether we have also `u`.
this._hasUFlag = ast.flags.includes('u');
return true;
},
Char: function Char(path) {
var node = path.node;
if (node.kind !== 'meta' || node.value !== '.') {
return;
}
var toValue = '\\uFFFF';
var toSymbol = '\uFFFF';
if (this._hasUFlag) {
toValue = '\\u{10FFFF}';
toSymbol = '\uDBFF\uDFFF';
}
path.replace({
type: 'CharacterClass',
expressions: [{
type: 'ClassRange',
from: {
type: 'Char',
value: '\\0',
kind: 'decimal',
symbol: '\0'
},
to: {
type: 'Char',
value: toValue,
kind: 'unicode',
symbol: toSymbol
}
}]
});
}
};

View File

@ -0,0 +1,59 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to translate `/(?<name>a)\k<name>/` to `/(a)\1/`.
*/
module.exports = {
// To track the names of the groups, and return them
// in the transform result state.
//
// A map from name to number: {foo: 2, bar: 4}
_groupNames: {},
/**
* Initialises the trasnform.
*/
init: function init() {
this._groupNames = {};
},
/**
* Returns extra state, which eventually is returned to
*/
getExtra: function getExtra() {
return this._groupNames;
},
Group: function Group(path) {
var node = path.node;
if (!node.name) {
return;
}
// Record group name.
this._groupNames[node.name] = node.number;
delete node.name;
delete node.nameRaw;
},
Backreference: function Backreference(path) {
var node = path.node;
if (node.kind !== 'name') {
return;
}
node.kind = 'number';
node.reference = node.number;
delete node.referenceRaw;
}
};

View File

@ -0,0 +1,23 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to remove `x` flag `/foo/x` to `/foo/`.
*
* Note: other features of `x` flags (whitespace, comments) are
* already removed at parsing stage.
*/
module.exports = {
RegExp: function RegExp(_ref) {
var node = _ref.node;
if (node.flags.includes('x')) {
node.flags = node.flags.replace('x', '');
}
}
};

View File

@ -0,0 +1,17 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
module.exports = {
// "dotAll" `s` flag
dotAll: require('./compat-dotall-s-transform'),
// Named capturing groups.
namedCapturingGroups: require('./compat-named-capturing-groups-transform'),
// `x` flag
xFlag: require('./compat-x-flag-transform')
};

177
node_modules/regexp-tree/dist/generator/index.js generated vendored Normal file
View File

@ -0,0 +1,177 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* Helper `gen` function calls node type handler.
*/
function gen(node) {
return node ? generator[node.type](node) : '';
}
/**
* AST handler.
*/
var generator = {
RegExp: function RegExp(node) {
return '/' + gen(node.body) + '/' + node.flags;
},
Alternative: function Alternative(node) {
return (node.expressions || []).map(gen).join('');
},
Disjunction: function Disjunction(node) {
return gen(node.left) + '|' + gen(node.right);
},
Group: function Group(node) {
var expression = gen(node.expression);
if (node.capturing) {
// A named group.
if (node.name) {
return '(?<' + (node.nameRaw || node.name) + '>' + expression + ')';
}
return '(' + expression + ')';
}
return '(?:' + expression + ')';
},
Backreference: function Backreference(node) {
switch (node.kind) {
case 'number':
return '\\' + node.reference;
case 'name':
return '\\k<' + (node.referenceRaw || node.reference) + '>';
default:
throw new TypeError('Unknown Backreference kind: ' + node.kind);
}
},
Assertion: function Assertion(node) {
switch (node.kind) {
case '^':
case '$':
case '\\b':
case '\\B':
return node.kind;
case 'Lookahead':
{
var assertion = gen(node.assertion);
if (node.negative) {
return '(?!' + assertion + ')';
}
return '(?=' + assertion + ')';
}
case 'Lookbehind':
{
var _assertion = gen(node.assertion);
if (node.negative) {
return '(?<!' + _assertion + ')';
}
return '(?<=' + _assertion + ')';
}
default:
throw new TypeError('Unknown Assertion kind: ' + node.kind);
}
},
CharacterClass: function CharacterClass(node) {
var expressions = node.expressions.map(gen).join('');
if (node.negative) {
return '[^' + expressions + ']';
}
return '[' + expressions + ']';
},
ClassRange: function ClassRange(node) {
return gen(node.from) + '-' + gen(node.to);
},
Repetition: function Repetition(node) {
return '' + gen(node.expression) + gen(node.quantifier);
},
Quantifier: function Quantifier(node) {
var quantifier = void 0;
var greedy = node.greedy ? '' : '?';
switch (node.kind) {
case '+':
case '?':
case '*':
quantifier = node.kind;
break;
case 'Range':
// Exact: {1}
if (node.from === node.to) {
quantifier = '{' + node.from + '}';
}
// Open: {1,}
else if (!node.to) {
quantifier = '{' + node.from + ',}';
}
// Closed: {1,3}
else {
quantifier = '{' + node.from + ',' + node.to + '}';
}
break;
default:
throw new TypeError('Unknown Quantifier kind: ' + node.kind);
}
return '' + quantifier + greedy;
},
Char: function Char(node) {
var value = node.value;
switch (node.kind) {
case 'simple':
{
if (node.escaped) {
return '\\' + value;
}
return value;
}
case 'hex':
case 'unicode':
case 'oct':
case 'decimal':
case 'control':
case 'meta':
return value;
default:
throw new TypeError('Unknown Char kind: ' + node.kind);
}
},
UnicodeProperty: function UnicodeProperty(node) {
var escapeChar = node.negative ? 'P' : 'p';
var namePart = void 0;
if (!node.shorthand && !node.binary) {
namePart = node.name + '=';
} else {
namePart = '';
}
return '\\' + escapeChar + '{' + namePart + node.value + '}';
}
};
module.exports = {
/**
* Generates a regexp string from an AST.
*
* @param Object ast - an AST node
*/
generate: gen
};

View File

@ -0,0 +1,412 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
// DFA minization.
/**
* Map from state to current set it goes.
*/
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var currentTransitionMap = null;
/**
* Takes a DFA, and returns a minimized version of it
* compressing some states to groups (using standard, 0-, 1-,
* 2-, ... N-equivalence algorithm).
*/
function minimize(dfa) {
var table = dfa.getTransitionTable();
var allStates = Object.keys(table);
var alphabet = dfa.getAlphabet();
var accepting = dfa.getAcceptingStateNumbers();
currentTransitionMap = {};
var nonAccepting = new Set();
allStates.forEach(function (state) {
state = Number(state);
var isAccepting = accepting.has(state);
if (isAccepting) {
currentTransitionMap[state] = accepting;
} else {
nonAccepting.add(state);
currentTransitionMap[state] = nonAccepting;
}
});
// ---------------------------------------------------------------------------
// Step 1: build equivalent sets.
// All [1..N] equivalent sets.
var all = [
// 0-equivalent sets.
[nonAccepting, accepting].filter(function (set) {
return set.size > 0;
})];
var current = void 0;
var previous = void 0;
// Top of the stack is the current list of sets to analyze.
current = all[all.length - 1];
// Previous set (to check whether we need to stop).
previous = all[all.length - 2];
// Until we'll not have the same N and N-1 equivalent rows.
var _loop = function _loop() {
var newTransitionMap = {};
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = current[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _set = _step3.value;
// Handled states for this set.
var handledStates = {};
var _set2 = _toArray(_set),
first = _set2[0],
rest = _set2.slice(1);
handledStates[first] = new Set([first]);
// Have to compare each from the rest states with
// the already handled states, and see if they are equivalent.
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
restSets: for (var _iterator4 = rest[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var state = _step4.value;
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = Object.keys(handledStates)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var handledState = _step5.value;
// This and some previously handled state are equivalent --
// just append this state to the same set.
if (areEquivalent(state, handledState, table, alphabet)) {
handledStates[handledState].add(state);
handledStates[state] = handledStates[handledState];
continue restSets;
}
}
// Else, this state is not equivalent to any of the
// handled states -- allocate a new set for it.
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
handledStates[state] = new Set([state]);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
// Add these handled states to all states map.
Object.assign(newTransitionMap, handledStates);
}
// Update current transition map for the handled row.
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
currentTransitionMap = newTransitionMap;
var newSets = new Set(Object.keys(newTransitionMap).map(function (state) {
return newTransitionMap[state];
}));
all.push([].concat(_toConsumableArray(newSets)));
// Top of the stack is the current.
current = all[all.length - 1];
// Previous set.
previous = all[all.length - 2];
};
while (!sameRow(current, previous)) {
_loop();
}
// ---------------------------------------------------------------------------
// Step 2: build minimized table from the equivalent sets.
// Remap state numbers from sets to index-based.
var remaped = new Map();
var idx = 1;
current.forEach(function (set) {
return remaped.set(set, idx++);
});
// Build the minimized table from the calculated equivalent sets.
var minimizedTable = {};
var minimizedAcceptingStates = new Set();
var updateAcceptingStates = function updateAcceptingStates(set, idx) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = set[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var state = _step.value;
if (accepting.has(state)) {
minimizedAcceptingStates.add(idx);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
};
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = remaped.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _ref = _step2.value;
var _ref2 = _slicedToArray(_ref, 2);
var set = _ref2[0];
var _idx = _ref2[1];
minimizedTable[_idx] = {};
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = alphabet[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var symbol = _step6.value;
updateAcceptingStates(set, _idx);
// Determine original transition for this symbol from the set.
var originalTransition = void 0;
var _iteratorNormalCompletion7 = true;
var _didIteratorError7 = false;
var _iteratorError7 = undefined;
try {
for (var _iterator7 = set[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
var originalState = _step7.value;
originalTransition = table[originalState][symbol];
if (originalTransition) {
break;
}
}
} catch (err) {
_didIteratorError7 = true;
_iteratorError7 = err;
} finally {
try {
if (!_iteratorNormalCompletion7 && _iterator7.return) {
_iterator7.return();
}
} finally {
if (_didIteratorError7) {
throw _iteratorError7;
}
}
}
if (originalTransition) {
minimizedTable[_idx][symbol] = remaped.get(currentTransitionMap[originalTransition]);
}
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
}
// Update the table, and accepting states on the original DFA.
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
dfa.setTransitionTable(minimizedTable);
dfa.setAcceptingStateNumbers(minimizedAcceptingStates);
return dfa;
}
function sameRow(r1, r2) {
if (!r2) {
return false;
}
if (r1.length !== r2.length) {
return false;
}
for (var i = 0; i < r1.length; i++) {
var s1 = r1[i];
var s2 = r2[i];
if (s1.size !== s2.size) {
return false;
}
if ([].concat(_toConsumableArray(s1)).sort().join(',') !== [].concat(_toConsumableArray(s2)).sort().join(',')) {
return false;
}
}
return true;
}
/**
* Checks whether two states are N-equivalent, i.e. whether they go
* to the same set on a symbol.
*/
function areEquivalent(s1, s2, table, alphabet) {
var _iteratorNormalCompletion8 = true;
var _didIteratorError8 = false;
var _iteratorError8 = undefined;
try {
for (var _iterator8 = alphabet[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
var symbol = _step8.value;
if (!goToSameSet(s1, s2, table, symbol)) {
return false;
}
}
} catch (err) {
_didIteratorError8 = true;
_iteratorError8 = err;
} finally {
try {
if (!_iteratorNormalCompletion8 && _iterator8.return) {
_iterator8.return();
}
} finally {
if (_didIteratorError8) {
throw _iteratorError8;
}
}
}
return true;
}
/**
* Checks whether states go to the same set.
*/
function goToSameSet(s1, s2, table, symbol) {
if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) {
return false;
}
var originalTransitionS1 = table[s1][symbol];
var originalTransitionS2 = table[s2][symbol];
// If no actual transition on this symbol, treat it as positive.
if (!originalTransitionS1 && !originalTransitionS2) {
return true;
}
// Otherwise, check if they are in the same sets.
return currentTransitionMap[s1].has(originalTransitionS1) && currentTransitionMap[s2].has(originalTransitionS2);
}
module.exports = {
minimize: minimize
};

View File

@ -0,0 +1,380 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DFAMinimizer = require('./dfa-minimizer');
var _require = require('../special-symbols'),
EPSILON_CLOSURE = _require.EPSILON_CLOSURE;
/**
* DFA is build by converting from NFA (subset construction).
*/
var DFA = function () {
function DFA(nfa) {
_classCallCheck(this, DFA);
this._nfa = nfa;
}
/**
* Minimizes DFA.
*/
_createClass(DFA, [{
key: 'minimize',
value: function minimize() {
this.getTransitionTable();
this._originalAcceptingStateNumbers = this._acceptingStateNumbers;
this._originalTransitionTable = this._transitionTable;
DFAMinimizer.minimize(this);
}
/**
* Returns alphabet for this DFA.
*/
}, {
key: 'getAlphabet',
value: function getAlphabet() {
return this._nfa.getAlphabet();
}
/**
* Returns accepting states.
*/
}, {
key: 'getAcceptingStateNumbers',
value: function getAcceptingStateNumbers() {
if (!this._acceptingStateNumbers) {
// Accepting states are determined during table construction.
this.getTransitionTable();
}
return this._acceptingStateNumbers;
}
/**
* Returns original accepting states.
*/
}, {
key: 'getOriginaAcceptingStateNumbers',
value: function getOriginaAcceptingStateNumbers() {
if (!this._originalAcceptingStateNumbers) {
// Accepting states are determined during table construction.
this.getTransitionTable();
}
return this._originalAcceptingStateNumbers;
}
/**
* Sets transition table.
*/
}, {
key: 'setTransitionTable',
value: function setTransitionTable(table) {
this._transitionTable = table;
}
/**
* Sets accepting states.
*/
}, {
key: 'setAcceptingStateNumbers',
value: function setAcceptingStateNumbers(stateNumbers) {
this._acceptingStateNumbers = stateNumbers;
}
/**
* DFA transition table is built from NFA table.
*/
}, {
key: 'getTransitionTable',
value: function getTransitionTable() {
var _this = this;
if (this._transitionTable) {
return this._transitionTable;
}
// Calculate from NFA transition table.
var nfaTable = this._nfa.getTransitionTable();
var nfaStates = Object.keys(nfaTable);
this._acceptingStateNumbers = new Set();
// Start state of DFA is E(S[nfa])
var startState = nfaTable[nfaStates[0]][EPSILON_CLOSURE];
// Init the worklist (states which should be in the DFA).
var worklist = [startState];
var alphabet = this.getAlphabet();
var nfaAcceptingStates = this._nfa.getAcceptingStateNumbers();
var dfaTable = {};
// Determine whether the combined DFA state is accepting.
var updateAcceptingStates = function updateAcceptingStates(states) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = nfaAcceptingStates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var nfaAcceptingState = _step.value;
// If any of the states from NFA is accepting, DFA's
// state is accepting as well.
if (states.indexOf(nfaAcceptingState) !== -1) {
_this._acceptingStateNumbers.add(states.join(','));
break;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
};
while (worklist.length > 0) {
var states = worklist.shift();
var dfaStateLabel = states.join(',');
dfaTable[dfaStateLabel] = {};
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = alphabet[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var symbol = _step2.value;
var onSymbol = [];
// Determine whether the combined state is accepting.
updateAcceptingStates(states);
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = states[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var state = _step3.value;
var nfaStatesOnSymbol = nfaTable[state][symbol];
if (!nfaStatesOnSymbol) {
continue;
}
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = nfaStatesOnSymbol[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var nfaStateOnSymbol = _step4.value;
if (!nfaTable[nfaStateOnSymbol]) {
continue;
}
onSymbol.push.apply(onSymbol, _toConsumableArray(nfaTable[nfaStateOnSymbol][EPSILON_CLOSURE]));
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
var dfaStatesOnSymbolSet = new Set(onSymbol);
var dfaStatesOnSymbol = [].concat(_toConsumableArray(dfaStatesOnSymbolSet));
if (dfaStatesOnSymbol.length > 0) {
var dfaOnSymbolStr = dfaStatesOnSymbol.join(',');
dfaTable[dfaStateLabel][symbol] = dfaOnSymbolStr;
if (!dfaTable.hasOwnProperty(dfaOnSymbolStr)) {
worklist.unshift(dfaStatesOnSymbol);
}
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
return this._transitionTable = this._remapStateNumbers(dfaTable);
}
/**
* Remaps state numbers in the resulting table:
* combined states '1,2,3' -> 1, '3,4' -> 2, etc.
*/
}, {
key: '_remapStateNumbers',
value: function _remapStateNumbers(calculatedDFATable) {
var newStatesMap = {};
this._originalTransitionTable = calculatedDFATable;
var transitionTable = {};
Object.keys(calculatedDFATable).forEach(function (originalNumber, newNumber) {
newStatesMap[originalNumber] = newNumber + 1;
});
for (var originalNumber in calculatedDFATable) {
var originalRow = calculatedDFATable[originalNumber];
var row = {};
for (var symbol in originalRow) {
row[symbol] = newStatesMap[originalRow[symbol]];
}
transitionTable[newStatesMap[originalNumber]] = row;
}
// Remap accepting states.
this._originalAcceptingStateNumbers = this._acceptingStateNumbers;
this._acceptingStateNumbers = new Set();
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = this._originalAcceptingStateNumbers[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _originalNumber = _step5.value;
this._acceptingStateNumbers.add(newStatesMap[_originalNumber]);
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
return transitionTable;
}
/**
* Returns original DFA table, where state numbers
* are combined numbers from NFA.
*/
}, {
key: 'getOriginalTransitionTable',
value: function getOriginalTransitionTable() {
if (!this._originalTransitionTable) {
// Original table is determined during table construction.
this.getTransitionTable();
}
return this._originalTransitionTable;
}
/**
* Checks whether this DFA accepts a string.
*/
}, {
key: 'matches',
value: function matches(string) {
var state = 1;
var i = 0;
var table = this.getTransitionTable();
while (string[i]) {
state = table[state][string[i++]];
if (!state) {
return false;
}
}
if (!this.getAcceptingStateNumbers().has(state)) {
return false;
}
return true;
}
}]);
return DFA;
}();
module.exports = DFA;

View File

@ -0,0 +1,59 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var NFA = require('./nfa/nfa');
var DFA = require('./dfa/dfa');
var nfaFromRegExp = require('./nfa/nfa-from-regexp');
var builders = require('./nfa/builders');
module.exports = {
/**
* Export NFA and DFA classes.
*/
NFA: NFA,
DFA: DFA,
/**
* Expose builders.
*/
builders: builders,
/**
* Builds an NFA for the passed regexp.
*
* @param string | AST | RegExp:
*
* a regular expression in different representations: a string,
* a RegExp object, or an AST.
*/
toNFA: function toNFA(regexp) {
return nfaFromRegExp.build(regexp);
},
/**
* Builds DFA for the passed regexp.
*
* @param string | AST | RegExp:
*
* a regular expression in different representations: a string,
* a RegExp object, or an AST.
*/
toDFA: function toDFA(regexp) {
return new DFA(this.toNFA(regexp));
},
/**
* Returns true if regexp accepts the string.
*/
test: function test(regexp, string) {
return this.toDFA(regexp).matches(string);
}
};

View File

@ -0,0 +1,227 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var NFA = require('./nfa');
var NFAState = require('./nfa-state');
var _require = require('../special-symbols'),
EPSILON = _require.EPSILON;
// -----------------------------------------------------------------------------
// Char NFA fragment: `c`
/**
* Char factory.
*
* Creates an NFA fragment for a single char.
*
* [in] --c--> [out]
*/
function char(c) {
var inState = new NFAState();
var outState = new NFAState({
accepting: true
});
return new NFA(inState.addTransition(c, outState), outState);
}
// -----------------------------------------------------------------------------
// Epsilon NFA fragment
/**
* Epsilon factory.
*
* Creates an NFA fragment for ε (recognizes an empty string).
*
* [in] --ε--> [out]
*/
function e() {
return char(EPSILON);
}
// -----------------------------------------------------------------------------
// Alteration NFA fragment: `abc`
/**
* Creates a connection between two NFA fragments on epsilon transition.
*
* [in-a] --a--> [out-a] --ε--> [in-b] --b--> [out-b]
*/
function altPair(first, second) {
first.out.accepting = false;
second.out.accepting = true;
first.out.addTransition(EPSILON, second.in);
return new NFA(first.in, second.out);
}
/**
* Alteration factory.
*
* Creates a alteration NFA for (at least) two NFA-fragments.
*/
function alt(first) {
for (var _len = arguments.length, fragments = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
fragments[_key - 1] = arguments[_key];
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = fragments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var fragment = _step.value;
first = altPair(first, fragment);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return first;
}
// -----------------------------------------------------------------------------
// Disjunction NFA fragment: `a|b`
/**
* Creates a disjunction choice between two fragments.
*/
function orPair(first, second) {
var inState = new NFAState();
var outState = new NFAState();
inState.addTransition(EPSILON, first.in);
inState.addTransition(EPSILON, second.in);
outState.accepting = true;
first.out.accepting = false;
second.out.accepting = false;
first.out.addTransition(EPSILON, outState);
second.out.addTransition(EPSILON, outState);
return new NFA(inState, outState);
}
/**
* Disjunction factory.
*
* Creates a disjunction NFA for (at least) two NFA-fragments.
*/
function or(first) {
for (var _len2 = arguments.length, fragments = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
fragments[_key2 - 1] = arguments[_key2];
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = fragments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var fragment = _step2.value;
first = orPair(first, fragment);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return first;
}
// -----------------------------------------------------------------------------
// Kleene-closure
/**
* Kleene star/closure.
*
* a*
*/
function repExplicit(fragment) {
var inState = new NFAState();
var outState = new NFAState({
accepting: true
});
// 0 or more.
inState.addTransition(EPSILON, fragment.in);
inState.addTransition(EPSILON, outState);
fragment.out.accepting = false;
fragment.out.addTransition(EPSILON, outState);
outState.addTransition(EPSILON, fragment.in);
return new NFA(inState, outState);
}
/**
* Optimized Kleene-star: just adds ε-transitions from
* input to the output, and back.
*/
function rep(fragment) {
fragment.in.addTransition(EPSILON, fragment.out);
fragment.out.addTransition(EPSILON, fragment.in);
return fragment;
}
/**
* Optimized Plus: just adds ε-transitions from
* the output to the input.
*/
function plusRep(fragment) {
fragment.out.addTransition(EPSILON, fragment.in);
return fragment;
}
/**
* Optimized ? repetition: just adds ε-transitions from
* the input to the output.
*/
function questionRep(fragment) {
fragment.in.addTransition(EPSILON, fragment.out);
return fragment;
}
module.exports = {
alt: alt,
char: char,
e: e,
or: or,
rep: rep,
repExplicit: repExplicit,
plusRep: plusRep,
questionRep: questionRep
};

View File

@ -0,0 +1,94 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var parser = require('../../../parser');
var _require = require('./builders'),
alt = _require.alt,
char = _require.char,
or = _require.or,
rep = _require.rep,
plusRep = _require.plusRep,
questionRep = _require.questionRep;
/**
* Helper `gen` function calls node type handler.
*/
function gen(node) {
if (node && !generator[node.type]) {
throw new Error(node.type + ' is not supported in NFA/DFA interpreter.');
}
return node ? generator[node.type](node) : '';
}
/**
* AST handler.
*/
var generator = {
RegExp: function RegExp(node) {
if (node.flags !== '') {
throw new Error('NFA/DFA: Flags are not supported yet.');
}
return gen(node.body);
},
Alternative: function Alternative(node) {
var fragments = (node.expressions || []).map(gen);
return alt.apply(undefined, _toConsumableArray(fragments));
},
Disjunction: function Disjunction(node) {
return or(gen(node.left), gen(node.right));
},
Repetition: function Repetition(node) {
switch (node.quantifier.kind) {
case '*':
return rep(gen(node.expression));
case '+':
return plusRep(gen(node.expression));
case '?':
return questionRep(gen(node.expression));
default:
throw new Error('Unknown repeatition: ' + node.quantifier.kind + '.');
}
},
Char: function Char(node) {
if (node.kind !== 'simple') {
throw new Error('NFA/DFA: Only simple chars are supported yet.');
}
return char(node.value);
},
Group: function Group(node) {
return gen(node.expression);
}
};
module.exports = {
/**
* Builds an NFA from the passed regexp.
*/
build: function build(regexp) {
var ast = regexp;
if (regexp instanceof RegExp) {
regexp = '' + regexp;
}
if (typeof regexp === 'string') {
ast = parser.parse(regexp, {
captureLocations: true
});
}
return gen(ast);
}
};

View File

@ -0,0 +1,220 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var State = require('../state');
var _require = require('../special-symbols'),
EPSILON = _require.EPSILON;
/**
* NFA state.
*
* Allows nondeterministic transitions to several states on the
* same symbol, and also epsilon-transitions.
*/
var NFAState = function (_State) {
_inherits(NFAState, _State);
function NFAState() {
_classCallCheck(this, NFAState);
return _possibleConstructorReturn(this, (NFAState.__proto__ || Object.getPrototypeOf(NFAState)).apply(this, arguments));
}
_createClass(NFAState, [{
key: 'matches',
/**
* Whether this state matches a string.
*
* We maintain set of visited epsilon-states to avoid infinite loops
* when an epsilon-transition goes eventually to itself.
*
* NOTE: this function is rather "educational", since we use DFA for strings
* matching. DFA is built on top of NFA, and uses fast transition table.
*/
value: function matches(string) {
var visited = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Set();
// An epsilon-state has been visited, stop to avoid infinite loop.
if (visited.has(this)) {
return false;
}
visited.add(this);
// No symbols left..
if (string.length === 0) {
// .. and we're in the accepting state.
if (this.accepting) {
return true;
}
// Check if we can reach any accepting state from
// on the epsilon transitions.
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.getTransitionsOnSymbol(EPSILON)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var nextState = _step.value;
if (nextState.matches('', visited)) {
return true;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return false;
}
// Else, we get some symbols.
var symbol = string[0];
var rest = string.slice(1);
var symbolTransitions = this.getTransitionsOnSymbol(symbol);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = symbolTransitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _nextState = _step2.value;
if (_nextState.matches(rest)) {
return true;
}
}
// If we couldn't match on symbol, check still epsilon-transitions
// without consuming the symbol (i.e. continue from `string`, not `rest`).
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = this.getTransitionsOnSymbol(EPSILON)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _nextState2 = _step3.value;
if (_nextState2.matches(string, visited)) {
return true;
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
return false;
}
/**
* Returns an ε-closure for this state:
* self + all states following ε-transitions.
*/
}, {
key: 'getEpsilonClosure',
value: function getEpsilonClosure() {
var _this2 = this;
if (!this._epsilonClosure) {
(function () {
var epsilonTransitions = _this2.getTransitionsOnSymbol(EPSILON);
var closure = _this2._epsilonClosure = new Set();
closure.add(_this2);
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = epsilonTransitions[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var nextState = _step4.value;
if (!closure.has(nextState)) {
closure.add(nextState);
var nextClosure = nextState.getEpsilonClosure();
nextClosure.forEach(function (state) {
return closure.add(state);
});
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
})();
}
return this._epsilonClosure;
}
}]);
return NFAState;
}(State);
module.exports = NFAState;

View File

@ -0,0 +1,234 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var _require = require('../special-symbols'),
EPSILON = _require.EPSILON,
EPSILON_CLOSURE = _require.EPSILON_CLOSURE;
/**
* NFA fragment.
*
* NFA sub-fragments can be combined to a larger NFAs building
* the resulting machine. Combining the fragments is done by patching
* edges of the in- and out-states.
*
* 2-states implementation, `in`, and `out`. Eventually all transitions
* go to the same `out`, which can further be connected via ε-transition
* with other fragment.
*/
var NFA = function () {
function NFA(inState, outState) {
_classCallCheck(this, NFA);
this.in = inState;
this.out = outState;
}
/**
* Tries to recognize a string based on this NFA fragment.
*/
_createClass(NFA, [{
key: 'matches',
value: function matches(string) {
return this.in.matches(string);
}
/**
* Returns an alphabet for this NFA.
*/
}, {
key: 'getAlphabet',
value: function getAlphabet() {
if (!this._alphabet) {
this._alphabet = new Set();
var table = this.getTransitionTable();
for (var state in table) {
var transitions = table[state];
for (var symbol in transitions) {
if (symbol !== EPSILON_CLOSURE) {
this._alphabet.add(symbol);
}
}
}
}
return this._alphabet;
}
/**
* Returns set of accepting states.
*/
}, {
key: 'getAcceptingStates',
value: function getAcceptingStates() {
if (!this._acceptingStates) {
// States are determined during table construction.
this.getTransitionTable();
}
return this._acceptingStates;
}
/**
* Returns accepting state numbers.
*/
}, {
key: 'getAcceptingStateNumbers',
value: function getAcceptingStateNumbers() {
if (!this._acceptingStateNumbers) {
this._acceptingStateNumbers = new Set();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.getAcceptingStates()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var acceptingState = _step.value;
this._acceptingStateNumbers.add(acceptingState.number);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
return this._acceptingStateNumbers;
}
/**
* Builds and returns transition table.
*/
}, {
key: 'getTransitionTable',
value: function getTransitionTable() {
var _this = this;
if (!this._transitionTable) {
this._transitionTable = {};
this._acceptingStates = new Set();
var visited = new Set();
var symbols = new Set();
var visitState = function visitState(state) {
if (visited.has(state)) {
return;
}
visited.add(state);
state.number = visited.size;
_this._transitionTable[state.number] = {};
if (state.accepting) {
_this._acceptingStates.add(state);
}
var transitions = state.getTransitions();
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = transitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _ref = _step2.value;
var _ref2 = _slicedToArray(_ref, 2);
var symbol = _ref2[0];
var symbolTransitions = _ref2[1];
var combinedState = [];
symbols.add(symbol);
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = symbolTransitions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var nextState = _step3.value;
visitState(nextState);
combinedState.push(nextState.number);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
_this._transitionTable[state.number][symbol] = combinedState;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
};
// Traverse the graph starting from the `in`.
visitState(this.in);
// Append epsilon-closure column.
visited.forEach(function (state) {
delete _this._transitionTable[state.number][EPSILON];
_this._transitionTable[state.number][EPSILON_CLOSURE] = [].concat(_toConsumableArray(state.getEpsilonClosure())).map(function (s) {
return s.number;
});
});
}
return this._transitionTable;
}
}]);
return NFA;
}();
module.exports = NFA;

View File

@ -0,0 +1,22 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* Epsilon, the empty string.
*/
var EPSILON = 'ε';
/**
* Epsilon-closure.
*/
var EPSILON_CLOSURE = EPSILON + '*';
module.exports = {
EPSILON: EPSILON,
EPSILON_CLOSURE: EPSILON_CLOSURE
};

View File

@ -0,0 +1,81 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A generic FA State class (base for NFA and DFA).
*
* Maintains the transition map, and the flag whether
* the state is accepting.
*/
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var State = function () {
function State() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$accepting = _ref.accepting,
accepting = _ref$accepting === undefined ? false : _ref$accepting;
_classCallCheck(this, State);
/**
* Outgoing transitions to other states.
*/
this._transitions = new Map();
/**
* Whether the state is accepting.
*/
this.accepting = accepting;
}
/**
* Returns transitions for this state.
*/
_createClass(State, [{
key: 'getTransitions',
value: function getTransitions() {
return this._transitions;
}
/**
* Creates a transition on symbol.
*/
}, {
key: 'addTransition',
value: function addTransition(symbol, toState) {
this.getTransitionsOnSymbol(symbol).add(toState);
return this;
}
/**
* Returns transitions set on symbol.
*/
}, {
key: 'getTransitionsOnSymbol',
value: function getTransitionsOnSymbol(symbol) {
var transitions = this._transitions.get(symbol);
if (!transitions) {
transitions = new Set();
this._transitions.set(symbol, transitions);
}
return transitions;
}
}]);
return State;
}();
module.exports = State;

View File

@ -0,0 +1,27 @@
'use strict';
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* The MIT License (MIT)
* Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
var Table = require('cli-table3');
/**
* Wrapper class over `cli-table3` with default options preset.
*/
var TablePrinter = function TablePrinter(options) {
_classCallCheck(this, TablePrinter);
return new Table(Object.assign({}, options, {
style: {
head: ['blue'],
border: ['gray']
}
}));
};
module.exports = TablePrinter;

View File

@ -0,0 +1,35 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to replace `a+` to `aa*`, since NFA/DFA
* handles Kleene-closure `a*`, and `a+` is just a syntactic sugar.
*/
module.exports = {
Repetition: function Repetition(path) {
var node = path.node,
parent = path.parent;
if (node.quantifier.kind !== '+') {
return;
}
if (parent.type === 'Alternative') {
path.getParent().insertChildAt(node.expression, path.index);
} else {
path.replace({
type: 'Alternative',
expressions: [node.expression, node]
});
}
// Change quantifier.
node.quantifier.kind = '*';
}
};

View File

@ -0,0 +1,10 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
module.exports = [
// a+ -> aa*
require('./char-plus-to-star-transform')];

83
node_modules/regexp-tree/dist/optimizer/index.js generated vendored Normal file
View File

@ -0,0 +1,83 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var clone = require('../utils/clone');
var parser = require('../parser');
var transform = require('../transform');
var optimizationTransforms = require('./transforms');
module.exports = {
/**
* Optimizer transforms a regular expression into an optimized version,
* replacing some sub-expressions with their idiomatic patterns.
*
* @param string | RegExp | AST - a regexp to optimize.
*
* @return TransformResult - an optimized regexp.
*
* Example:
*
* /[a-zA-Z_0-9][a-zA-Z_0-9]*\e{1,}/
*
* Optimized to:
*
* /\w+e+/
*/
optimize: function optimize(regexp) {
var transformsWhitelist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var transformToApply = transformsWhitelist.length > 0 ? transformsWhitelist : Object.keys(optimizationTransforms);
var ast = regexp;
if (regexp instanceof RegExp) {
regexp = '' + regexp;
}
if (typeof regexp === 'string') {
ast = parser.parse(regexp);
}
var result = new transform.TransformResult(ast);
var prevResultString = void 0;
do {
// Get a copy of the current state here so
// we can compare it with the state at the
// end of the loop.
prevResultString = result.toString();
ast = clone(result.getAST());
transformToApply.forEach(function (transformName) {
if (!optimizationTransforms.hasOwnProperty(transformName)) {
throw new Error('Unknown optimization-transform: ' + transformName + '. ' + 'Available transforms are: ' + Object.keys(optimizationTransforms).join(', '));
}
var transformer = optimizationTransforms[transformName];
// Don't override result just yet since we
// might want to rollback the transform
var newResult = transform.transform(ast, transformer);
if (newResult.toString() !== result.toString()) {
if (newResult.toString().length <= result.toString().length) {
result = newResult;
} else {
// Result has changed but is not shorter:
// restore ast to its previous state.
ast = clone(result.getAST());
}
}
});
// Keep running the optimizer until it stops
// making any change to the regexp.
} while (result.toString() !== prevResultString);
return result;
}
};

View File

@ -0,0 +1,110 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var UPPER_A_CP = 'A'.codePointAt(0);
var UPPER_Z_CP = 'Z'.codePointAt(0);
/**
* Transforms case-insensitive regexp to lowercase
*
* /AaBbÏ/i -> /aabbï/i
*/
module.exports = {
_AZClassRanges: null,
_hasUFlag: false,
init: function init(ast) {
this._AZClassRanges = new Set();
this._hasUFlag = ast.flags.includes('u');
},
shouldRun: function shouldRun(ast) {
return ast.flags.includes('i');
},
Char: function Char(path) {
var node = path.node,
parent = path.parent;
if (isNaN(node.codePoint)) {
return;
}
// Engine support for case-insensitive matching without the u flag
// for characters above \u1000 does not seem reliable.
if (!this._hasUFlag && node.codePoint >= 0x1000) {
return;
}
if (parent.type === 'ClassRange') {
// The only class ranges we handle must be inside A-Z.
// After the `from` char is processed, the isAZClassRange test
// will be false, so we use a Set to keep track of parents and
// process the `to` char.
if (!this._AZClassRanges.has(parent) && !isAZClassRange(parent)) {
return;
}
this._AZClassRanges.add(parent);
}
var lower = node.symbol.toLowerCase();
if (lower !== node.symbol) {
node.value = displaySymbolAsValue(lower, node);
node.symbol = lower;
node.codePoint = lower.codePointAt(0);
}
}
};
function isAZClassRange(classRange) {
var from = classRange.from,
to = classRange.to;
// A-Z
return from.codePoint >= UPPER_A_CP && from.codePoint <= UPPER_Z_CP && to.codePoint >= UPPER_A_CP && to.codePoint <= UPPER_Z_CP;
}
function displaySymbolAsValue(symbol, node) {
var codePoint = symbol.codePointAt(0);
if (node.kind === 'decimal') {
return '\\' + codePoint;
}
if (node.kind === 'oct') {
return '\\0' + codePoint.toString(8);
}
if (node.kind === 'hex') {
return '\\x' + codePoint.toString(16);
}
if (node.kind === 'unicode') {
if (node.isSurrogatePair) {
var _getSurrogatePairFrom = getSurrogatePairFromCodePoint(codePoint),
lead = _getSurrogatePairFrom.lead,
trail = _getSurrogatePairFrom.trail;
return '\\u' + '0'.repeat(4 - lead.length) + lead + '\\u' + '0'.repeat(4 - trail.length) + trail;
} else if (node.value.includes('{')) {
return '\\u{' + codePoint.toString(16) + '}';
} else {
var code = codePoint.toString(16);
return '\\u' + '0'.repeat(4 - code.length) + code;
}
}
// simple
return symbol;
}
/**
* Converts a code point to a surrogate pair.
* Conversion algorithm is taken from The Unicode Standard 3.0 Section 3.7
* (https://www.unicode.org/versions/Unicode3.0.0/ch03.pdf)
* @param {number} codePoint - Between 0x10000 and 0x10ffff
* @returns {{lead: string, trail: string}}
*/
function getSurrogatePairFromCodePoint(codePoint) {
var lead = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
var trail = (codePoint - 0x10000) % 0x400 + 0xdc00;
return {
lead: lead.toString(16),
trail: trail.toString(16)
};
}

View File

@ -0,0 +1,339 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to merge class ranges.
*
* [a-ec] -> [a-e]
* [a-ec-e] -> [a-e]
* [\w\da-f] -> [\w]
* [abcdef] -> [a-f]
*/
module.exports = {
_hasIUFlags: false,
init: function init(ast) {
this._hasIUFlags = ast.flags.includes('i') && ast.flags.includes('u');
},
CharacterClass: function CharacterClass(path) {
var node = path.node;
var expressions = node.expressions;
var metas = [];
// Extract metas
expressions.forEach(function (expression) {
if (isMeta(expression)) {
metas.push(expression.value);
}
});
expressions.sort(sortCharClass);
for (var i = 0; i < expressions.length; i++) {
var expression = expressions[i];
if (fitsInMetas(expression, metas, this._hasIUFlags) || combinesWithPrecedingClassRange(expression, expressions[i - 1]) || combinesWithFollowingClassRange(expression, expressions[i + 1])) {
expressions.splice(i, 1);
i--;
} else {
var nbMergedChars = charCombinesWithPrecedingChars(expression, i, expressions);
expressions.splice(i - nbMergedChars + 1, nbMergedChars);
i -= nbMergedChars;
}
}
}
};
/**
* Sorts expressions in char class in the following order:
* - meta chars, ordered alphabetically by value
* - chars (except `control` kind) and class ranges, ordered alphabetically (`from` char is used for class ranges)
* - if ambiguous, class range comes before char
* - if ambiguous between two class ranges, orders alphabetically by `to` char
* - control chars, ordered alphabetically by value
* @param {Object} a - Left Char or ClassRange node
* @param {Object} b - Right Char or ClassRange node
* @returns {number}
*/
function sortCharClass(a, b) {
var aValue = getSortValue(a);
var bValue = getSortValue(b);
if (aValue === bValue) {
// We want ClassRange before Char
// [bb-d] -> [b-db]
if (a.type === 'ClassRange' && b.type !== 'ClassRange') {
return -1;
}
if (b.type === 'ClassRange' && a.type !== 'ClassRange') {
return 1;
}
if (a.type === 'ClassRange' && b.type === 'ClassRange') {
return getSortValue(a.to) - getSortValue(b.to);
}
if (isMeta(a) && isMeta(b) || isControl(a) && isControl(b)) {
return a.value < b.value ? -1 : 1;
}
}
return aValue - bValue;
}
/**
* @param {Object} expression - Char or ClassRange node
* @returns {number}
*/
function getSortValue(expression) {
if (expression.type === 'Char') {
if (expression.kind === 'control') {
return Infinity;
}
if (expression.kind === 'meta' && isNaN(expression.codePoint)) {
return -1;
}
return expression.codePoint;
}
// ClassRange
return expression.from.codePoint;
}
/**
* Checks if a node is a meta char from the set \d\w\s\D\W\S
* @param {Object} expression - Char or ClassRange node
* @param {?string} value
* @returns {boolean}
*/
function isMeta(expression) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return expression.type === 'Char' && expression.kind === 'meta' && (value ? expression.value === value : /^\\[dws]$/i.test(expression.value));
}
/**
* @param {Object} expression - Char or ClassRange node
* @returns {boolean}
*/
function isControl(expression) {
return expression.type === 'Char' && expression.kind === 'control';
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {string[]} metas - Array of meta chars, e.g. ["\\w", "\\s"]
* @param {boolean} hasIUFlags
* @returns {boolean}
*/
function fitsInMetas(expression, metas, hasIUFlags) {
for (var i = 0; i < metas.length; i++) {
if (fitsInMeta(expression, metas[i], hasIUFlags)) {
return true;
}
}
return false;
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {string} meta - e.g. "\\w"
* @param {boolean} hasIUFlags
* @returns {boolean}
*/
function fitsInMeta(expression, meta, hasIUFlags) {
if (expression.type === 'ClassRange') {
return fitsInMeta(expression.from, meta, hasIUFlags) && fitsInMeta(expression.to, meta, hasIUFlags);
}
// Special cases:
// \S contains \w and \d
if (meta === '\\S' && (isMeta(expression, '\\w') || isMeta(expression, '\\d'))) {
return true;
}
// \D contains \W and \s
if (meta === '\\D' && (isMeta(expression, '\\W') || isMeta(expression, '\\s'))) {
return true;
}
// \w contains \d
if (meta === '\\w' && isMeta(expression, '\\d')) {
return true;
}
// \W contains \s
if (meta === '\\W' && isMeta(expression, '\\s')) {
return true;
}
if (expression.type !== 'Char' || isNaN(expression.codePoint)) {
return false;
}
if (meta === '\\s') {
return fitsInMetaS(expression);
}
if (meta === '\\S') {
return !fitsInMetaS(expression);
}
if (meta === '\\d') {
return fitsInMetaD(expression);
}
if (meta === '\\D') {
return !fitsInMetaD(expression);
}
if (meta === '\\w') {
return fitsInMetaW(expression, hasIUFlags);
}
if (meta === '\\W') {
return !fitsInMetaW(expression, hasIUFlags);
}
return false;
}
/**
* @param {Object} expression - Char node with codePoint
* @returns {boolean}
*/
function fitsInMetaS(expression) {
return expression.codePoint === 0x0009 || // \t
expression.codePoint === 0x000a || // \n
expression.codePoint === 0x000b || // \v
expression.codePoint === 0x000c || // \f
expression.codePoint === 0x000d || // \r
expression.codePoint === 0x0020 || // space
expression.codePoint === 0x00a0 || // nbsp
expression.codePoint === 0x1680 || // part of Zs
expression.codePoint >= 0x2000 && expression.codePoint <= 0x200a || // part of Zs
expression.codePoint === 0x2028 || // line separator
expression.codePoint === 0x2029 || // paragraph separator
expression.codePoint === 0x202f || // part of Zs
expression.codePoint === 0x205f || // part of Zs
expression.codePoint === 0x3000 || // part of Zs
expression.codePoint === 0xfeff; // zwnbsp
}
/**
* @param {Object} expression - Char node with codePoint
* @returns {boolean}
*/
function fitsInMetaD(expression) {
return expression.codePoint >= 0x30 && expression.codePoint <= 0x39; // 0-9
}
/**
* @param {Object} expression - Char node with codePoint
* @param {boolean} hasIUFlags
* @returns {boolean}
*/
function fitsInMetaW(expression, hasIUFlags) {
return fitsInMetaD(expression) || expression.codePoint >= 0x41 && expression.codePoint <= 0x5a || // A-Z
expression.codePoint >= 0x61 && expression.codePoint <= 0x7a || // a-z
expression.value === '_' || hasIUFlags && (expression.codePoint === 0x017f || expression.codePoint === 0x212a);
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {Object} classRange - Char or ClassRange node
* @returns {boolean}
*/
function combinesWithPrecedingClassRange(expression, classRange) {
if (classRange && classRange.type === 'ClassRange') {
if (fitsInClassRange(expression, classRange)) {
// [a-gc] -> [a-g]
// [a-gc-e] -> [a-g]
return true;
} else if (
// We only want \w chars or char codes to keep readability
isMetaWCharOrCode(expression) && classRange.to.codePoint === expression.codePoint - 1) {
// [a-de] -> [a-e]
classRange.to = expression;
return true;
} else if (expression.type === 'ClassRange' && expression.from.codePoint <= classRange.to.codePoint + 1 && expression.to.codePoint >= classRange.from.codePoint - 1) {
// [a-db-f] -> [a-f]
// [b-fa-d] -> [a-f]
// [a-cd-f] -> [a-f]
if (expression.from.codePoint < classRange.from.codePoint) {
classRange.from = expression.from;
}
if (expression.to.codePoint > classRange.to.codePoint) {
classRange.to = expression.to;
}
return true;
}
}
return false;
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {Object} classRange - Char or ClassRange node
* @returns {boolean}
*/
function combinesWithFollowingClassRange(expression, classRange) {
if (classRange && classRange.type === 'ClassRange') {
// Considering the elements were ordered alphabetically,
// there is only one case to handle
// [ab-e] -> [a-e]
if (
// We only want \w chars or char codes to keep readability
isMetaWCharOrCode(expression) && classRange.from.codePoint === expression.codePoint + 1) {
classRange.from = expression;
return true;
}
}
return false;
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {Object} classRange - ClassRange node
* @returns {boolean}
*/
function fitsInClassRange(expression, classRange) {
if (expression.type === 'Char' && isNaN(expression.codePoint)) {
return false;
}
if (expression.type === 'ClassRange') {
return fitsInClassRange(expression.from, classRange) && fitsInClassRange(expression.to, classRange);
}
return expression.codePoint >= classRange.from.codePoint && expression.codePoint <= classRange.to.codePoint;
}
/**
* @param {Object} expression - Char or ClassRange node
* @param {Number} index
* @param {Object[]} expressions - expressions in CharClass
* @returns {number} - Number of characters combined with expression
*/
function charCombinesWithPrecedingChars(expression, index, expressions) {
// We only want \w chars or char codes to keep readability
if (!isMetaWCharOrCode(expression)) {
return 0;
}
var nbMergedChars = 0;
while (index > 0) {
var currentExpression = expressions[index];
var precedingExpresion = expressions[index - 1];
if (isMetaWCharOrCode(precedingExpresion) && precedingExpresion.codePoint === currentExpression.codePoint - 1) {
nbMergedChars++;
index--;
} else {
break;
}
}
if (nbMergedChars > 1) {
expressions[index] = {
type: 'ClassRange',
from: expressions[index],
to: expression
};
return nbMergedChars;
}
return 0;
}
function isMetaWCharOrCode(expression) {
return expression && expression.type === 'Char' && !isNaN(expression.codePoint) && (fitsInMetaW(expression, false) || expression.kind === 'unicode' || expression.kind === 'hex' || expression.kind === 'oct' || expression.kind === 'decimal');
}

View File

@ -0,0 +1,30 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to simplify character classes
* spanning only one or two chars.
*
* [a-a] -> [a]
* [a-b] -> [ab]
*/
module.exports = {
ClassRange: function ClassRange(path) {
var node = path.node;
if (node.from.codePoint === node.to.codePoint) {
path.replace(node.from);
} else if (node.from.codePoint === node.to.codePoint - 1) {
path.getParent().insertChildAt(node.to, path.index + 1);
path.replace(node.from);
}
}
};

View File

@ -0,0 +1,33 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to remove duplicates from character classes.
*/
module.exports = {
CharacterClass: function CharacterClass(path) {
var node = path.node;
var sources = {};
for (var i = 0; i < node.expressions.length; i++) {
var childPath = path.getChild(i);
var source = childPath.jsonEncode();
if (sources.hasOwnProperty(source)) {
childPath.remove();
// Since we remove the current node.
// TODO: make it simpler for users with a method.
i--;
}
sources[source] = true;
}
}
};

View File

@ -0,0 +1,211 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to replace standard character classes with
* their meta symbols equivalents.
*/
module.exports = {
_hasIFlag: false,
_hasUFlag: false,
init: function init(ast) {
this._hasIFlag = ast.flags.includes('i');
this._hasUFlag = ast.flags.includes('u');
},
CharacterClass: function CharacterClass(path) {
// [0-9] -> \d
rewriteNumberRanges(path);
// [a-zA-Z_0-9] -> \w
rewriteWordRanges(path, this._hasIFlag, this._hasUFlag);
// [ \t\r\n\f] -> \s
rewriteWhitespaceRanges(path);
}
};
/**
* Rewrites number ranges: [0-9] -> \d
*/
function rewriteNumberRanges(path) {
var node = path.node;
node.expressions.forEach(function (expression, i) {
if (isFullNumberRange(expression)) {
path.getChild(i).replace({
type: 'Char',
value: '\\d',
kind: 'meta'
});
}
});
}
/**
* Rewrites word ranges: [a-zA-Z_0-9] -> \w
* Thus, the ranges may go in any order, and other symbols/ranges
* are kept untouched, e.g. [a-z_\dA-Z$] -> [\w$]
*/
function rewriteWordRanges(path, hasIFlag, hasUFlag) {
var node = path.node;
var numberPath = null;
var lowerCasePath = null;
var upperCasePath = null;
var underscorePath = null;
var u017fPath = null;
var u212aPath = null;
node.expressions.forEach(function (expression, i) {
// \d
if (isMetaChar(expression, '\\d')) {
numberPath = path.getChild(i);
}
// a-z
else if (isLowerCaseRange(expression)) {
lowerCasePath = path.getChild(i);
}
// A-Z
else if (isUpperCaseRange(expression)) {
upperCasePath = path.getChild(i);
}
// _
else if (isUnderscore(expression)) {
underscorePath = path.getChild(i);
} else if (hasIFlag && hasUFlag && isU017fPath(expression)) {
u017fPath = path.getChild(i);
} else if (hasIFlag && hasUFlag && isU212aPath(expression)) {
u212aPath = path.getChild(i);
}
});
// If we found the whole pattern, replace it.
if (numberPath && (lowerCasePath && upperCasePath || hasIFlag && (lowerCasePath || upperCasePath)) && underscorePath && (!hasUFlag || !hasIFlag || u017fPath && u212aPath)) {
// Put \w in place of \d.
numberPath.replace({
type: 'Char',
value: '\\w',
kind: 'meta'
});
// Other paths are removed.
if (lowerCasePath) {
lowerCasePath.remove();
}
if (upperCasePath) {
upperCasePath.remove();
}
underscorePath.remove();
if (u017fPath) {
u017fPath.remove();
}
if (u212aPath) {
u212aPath.remove();
}
}
}
/**
* Rewrites whitespace ranges: [ \t\r\n\f] -> \s.
*/
function rewriteWhitespaceRanges(path) {
var node = path.node;
var spacePath = null;
var tPath = null;
var nPath = null;
var rPath = null;
var fPath = null;
node.expressions.forEach(function (expression, i) {
// Space
if (isChar(expression, ' ')) {
spacePath = path.getChild(i);
}
// \t
else if (isMetaChar(expression, '\\t')) {
tPath = path.getChild(i);
}
// \n
else if (isMetaChar(expression, '\\n')) {
nPath = path.getChild(i);
}
// \r
else if (isMetaChar(expression, '\\r')) {
rPath = path.getChild(i);
}
// \f
else if (isMetaChar(expression, '\\f')) {
fPath = path.getChild(i);
}
});
// If we found the whole pattern, replace it.
// Make \f optional.
if (spacePath && tPath && nPath && rPath) {
// Put \s in place of \n.
nPath.node.value = '\\s';
// Other paths are removed.
spacePath.remove();
tPath.remove();
rPath.remove();
if (fPath) {
fPath.remove();
}
}
}
function isFullNumberRange(node) {
return node.type === 'ClassRange' && node.from.value === '0' && node.to.value === '9';
}
function isChar(node, value) {
var kind = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'simple';
return node.type === 'Char' && node.value === value && node.kind === kind;
}
function isMetaChar(node, value) {
return isChar(node, value, 'meta');
}
function isLowerCaseRange(node) {
return node.type === 'ClassRange' && node.from.value === 'a' && node.to.value === 'z';
}
function isUpperCaseRange(node) {
return node.type === 'ClassRange' && node.from.value === 'A' && node.to.value === 'Z';
}
function isUnderscore(node) {
return node.type === 'Char' && node.value === '_' && node.kind === 'simple';
}
function isU017fPath(node) {
return node.type === 'Char' && node.kind === 'unicode' && node.codePoint === 0x017f;
}
function isU212aPath(node) {
return node.type === 'Char' && node.kind === 'unicode' && node.codePoint === 0x212a;
}

View File

@ -0,0 +1,71 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to replace single char character classes with
* just that character.
*
* [\d] -> \d, [^\w] -> \W
*/
module.exports = {
CharacterClass: function CharacterClass(path) {
var node = path.node;
if (node.expressions.length !== 1 || !isAppropriateChar(node.expressions[0])) {
return;
}
var _node$expressions$ = node.expressions[0],
value = _node$expressions$.value,
kind = _node$expressions$.kind,
escaped = _node$expressions$.escaped;
if (node.negative) {
// For negative can extract only meta chars like [^\w] -> \W
// cannot do for [^a] -> a (wrong).
if (!isMeta(value)) {
return;
}
value = getInverseMeta(value);
}
path.replace({
type: 'Char',
value: value,
kind: kind,
escaped: escaped || shouldEscape(value)
});
}
};
function isAppropriateChar(node) {
return node.type === 'Char' &&
// We don't extract [\b] (backspace) since \b has different
// semantics (word boundary).
node.value !== '\\b';
}
function isMeta(value) {
return (/^\\[dwsDWS]$/.test(value)
);
}
function getInverseMeta(value) {
return (/[dws]/.test(value) ? value.toUpperCase() : value.toLowerCase()
);
}
// Note: \{ and \} are always preserved to avoid `a[{]2[}]` turning
// into `a{2}`.
function shouldEscape(value) {
return (/[*[()+?$./{}|]/.test(value)
);
}

View File

@ -0,0 +1,84 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var UPPER_A_CP = 'A'.codePointAt(0);
var UPPER_Z_CP = 'Z'.codePointAt(0);
var LOWER_A_CP = 'a'.codePointAt(0);
var LOWER_Z_CP = 'z'.codePointAt(0);
var DIGIT_0_CP = '0'.codePointAt(0);
var DIGIT_9_CP = '9'.codePointAt(0);
/**
* A regexp-tree plugin to transform coded chars into simple chars.
*
* \u0061 -> a
*/
module.exports = {
Char: function Char(path) {
var node = path.node,
parent = path.parent;
if (isNaN(node.codePoint) || node.kind === 'simple') {
return;
}
if (parent.type === 'ClassRange') {
if (!isSimpleRange(parent)) {
return;
}
}
if (!isPrintableASCIIChar(node.codePoint)) {
return;
}
var symbol = String.fromCodePoint(node.codePoint);
var newChar = {
type: 'Char',
kind: 'simple',
value: symbol,
symbol: symbol,
codePoint: node.codePoint
};
if (needsEscape(symbol, parent.type)) {
newChar.escaped = true;
}
path.replace(newChar);
}
};
/**
* Checks if a range is included either in 0-9, a-z or A-Z
* @param classRange
* @returns {boolean}
*/
function isSimpleRange(classRange) {
var from = classRange.from,
to = classRange.to;
return from.codePoint >= DIGIT_0_CP && from.codePoint <= DIGIT_9_CP && to.codePoint >= DIGIT_0_CP && to.codePoint <= DIGIT_9_CP || from.codePoint >= UPPER_A_CP && from.codePoint <= UPPER_Z_CP && to.codePoint >= UPPER_A_CP && to.codePoint <= UPPER_Z_CP || from.codePoint >= LOWER_A_CP && from.codePoint <= LOWER_Z_CP && to.codePoint >= LOWER_A_CP && to.codePoint <= LOWER_Z_CP;
}
/**
* Checks if a code point in the range of printable ASCII chars
* (DEL char excluded)
* @param codePoint
* @returns {boolean}
*/
function isPrintableASCIIChar(codePoint) {
return codePoint >= 0x20 && codePoint <= 0x7e;
}
function needsEscape(symbol, parentType) {
if (parentType === 'ClassRange' || parentType === 'CharacterClass') {
return (/[\]\\^-]/.test(symbol)
);
}
return (/[*[()+?^$./\\|{}]/.test(symbol)
);
}

View File

@ -0,0 +1,143 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to remove unnecessary escape.
*
* \e -> e
*
* [\(] -> [(]
*/
module.exports = {
_hasXFlag: false,
init: function init(ast) {
this._hasXFlag = ast.flags.includes('x');
},
Char: function Char(path) {
var node = path.node;
if (!node.escaped) {
return;
}
if (shouldUnescape(path, this._hasXFlag)) {
delete node.escaped;
}
}
};
function shouldUnescape(path, hasXFlag) {
var value = path.node.value,
index = path.index,
parent = path.parent;
// In char class (, etc are allowed.
if (parent.type !== 'CharacterClass' && parent.type !== 'ClassRange') {
return !preservesEscape(value, index, parent, hasXFlag);
}
return !preservesInCharClass(value, index, parent);
}
/**
* \], \\, \^, \-
*/
function preservesInCharClass(value, index, parent) {
if (value === '^') {
// Avoid [\^a] turning into [^a]
return index === 0 && !parent.negative;
}
if (value === '-') {
// Avoid [a\-z] turning into [a-z]
return index !== 0 && index !== parent.expressions.length - 1;
}
return (/[\]\\]/.test(value)
);
}
function preservesEscape(value, index, parent, hasXFlag) {
if (value === '{') {
return preservesOpeningCurlyBraceEscape(index, parent);
}
if (value === '}') {
return preservesClosingCurlyBraceEscape(index, parent);
}
if (hasXFlag && /[ #]/.test(value)) {
return true;
}
return (/[*[()+?^$./\\|]/.test(value)
);
}
function consumeNumbers(startIndex, parent, rtl) {
var i = startIndex;
var siblingNode = (rtl ? i >= 0 : i < parent.expressions.length) && parent.expressions[i];
while (siblingNode && siblingNode.type === 'Char' && siblingNode.kind === 'simple' && !siblingNode.escaped && /\d/.test(siblingNode.value)) {
rtl ? i-- : i++;
siblingNode = (rtl ? i >= 0 : i < parent.expressions.length) && parent.expressions[i];
}
return Math.abs(startIndex - i);
}
function isSimpleChar(node, value) {
return node && node.type === 'Char' && node.kind === 'simple' && !node.escaped && node.value === value;
}
function preservesOpeningCurlyBraceEscape(index, parent) {
var nbFollowingNumbers = consumeNumbers(index + 1, parent);
var i = index + nbFollowingNumbers + 1;
var nextSiblingNode = i < parent.expressions.length && parent.expressions[i];
if (nbFollowingNumbers) {
// Avoid \{3} turning into {3}
if (isSimpleChar(nextSiblingNode, '}')) {
return true;
}
if (isSimpleChar(nextSiblingNode, ',')) {
nbFollowingNumbers = consumeNumbers(i + 1, parent);
i = i + nbFollowingNumbers + 1;
nextSiblingNode = i < parent.expressions.length && parent.expressions[i];
// Avoid \{3,} turning into {3,}
return isSimpleChar(nextSiblingNode, '}');
}
}
return false;
}
function preservesClosingCurlyBraceEscape(index, parent) {
var nbPrecedingNumbers = consumeNumbers(index - 1, parent, true);
var i = index - nbPrecedingNumbers - 1;
var previousSiblingNode = i >= 0 && parent.expressions[i];
// Avoid {3\} turning into {3}
if (nbPrecedingNumbers && isSimpleChar(previousSiblingNode, '{')) {
return true;
}
if (isSimpleChar(previousSiblingNode, ',')) {
nbPrecedingNumbers = consumeNumbers(i - 1, parent, true);
i = i - nbPrecedingNumbers - 1;
previousSiblingNode = i < parent.expressions.length && parent.expressions[i];
// Avoid {3,\} turning into {3,}
return nbPrecedingNumbers && isSimpleChar(previousSiblingNode, '{');
}
return false;
}

View File

@ -0,0 +1,27 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to transform surrogate pairs into single unicode code point
*
* \ud83d\ude80 -> \u{1f680}
*/
module.exports = {
shouldRun: function shouldRun(ast) {
return ast.flags.includes('u');
},
Char: function Char(path) {
var node = path.node;
if (node.kind !== 'unicode' || !node.isSurrogatePair || isNaN(node.codePoint)) {
return;
}
node.value = '\\u{' + node.codePoint.toString(16) + '}';
delete node.isSurrogatePair;
}
};

View File

@ -0,0 +1,195 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var NodePath = require('../../traverse/node-path');
var _require = require('../../transform/utils'),
increaseQuantifierByOne = _require.increaseQuantifierByOne;
/**
* A regexp-tree plugin to combine repeating patterns.
*
* /^abcabcabc/ -> /^abc{3}/
* /^(?:abc){2}abc/ -> /^(?:abc){3}/
* /^abc(?:abc){2}/ -> /^(?:abc){3}/
*/
module.exports = {
Alternative: function Alternative(path) {
var node = path.node;
// We can skip the first child
var index = 1;
while (index < node.expressions.length) {
var child = path.getChild(index);
index = Math.max(1, combineRepeatingPatternLeft(path, child, index));
if (index >= node.expressions.length) {
break;
}
child = path.getChild(index);
index = Math.max(1, combineWithPreviousRepetition(path, child, index));
if (index >= node.expressions.length) {
break;
}
child = path.getChild(index);
index = Math.max(1, combineRepetitionWithPrevious(path, child, index));
index++;
}
}
};
// abcabc -> (?:abc){2}
function combineRepeatingPatternLeft(alternative, child, index) {
var node = alternative.node;
var nbPossibleLengths = Math.ceil(index / 2);
var i = 0;
while (i < nbPossibleLengths) {
var startIndex = index - 2 * i - 1;
var right = void 0,
left = void 0;
if (i === 0) {
right = child;
left = alternative.getChild(startIndex);
} else {
right = NodePath.getForNode({
type: 'Alternative',
expressions: [].concat(_toConsumableArray(node.expressions.slice(index - i, index)), [child.node])
});
left = NodePath.getForNode({
type: 'Alternative',
expressions: [].concat(_toConsumableArray(node.expressions.slice(startIndex, index - i)))
});
}
if (right.hasEqualSource(left)) {
for (var j = 0; j < 2 * i + 1; j++) {
alternative.getChild(startIndex).remove();
}
child.replace({
type: 'Repetition',
expression: i === 0 ? right.node : {
type: 'Group',
capturing: false,
expression: right.node
},
quantifier: {
type: 'Quantifier',
kind: 'Range',
from: 2,
to: 2,
greedy: true
}
});
return startIndex;
}
i++;
}
return index;
}
// (?:abc){2}abc -> (?:abc){3}
function combineWithPreviousRepetition(alternative, child, index) {
var node = alternative.node;
var i = 0;
while (i < index) {
var previousChild = alternative.getChild(i);
if (previousChild.node.type === 'Repetition' && previousChild.node.quantifier.greedy) {
var left = previousChild.getChild();
var right = void 0;
if (left.node.type === 'Group' && !left.node.capturing) {
left = left.getChild();
}
if (i + 1 === index) {
right = child;
if (right.node.type === 'Group' && !right.node.capturing) {
right = right.getChild();
}
} else {
right = NodePath.getForNode({
type: 'Alternative',
expressions: [].concat(_toConsumableArray(node.expressions.slice(i + 1, index + 1)))
});
}
if (left.hasEqualSource(right)) {
for (var j = i; j < index; j++) {
alternative.getChild(i + 1).remove();
}
increaseQuantifierByOne(previousChild.node.quantifier);
return i;
}
}
i++;
}
return index;
}
// abc(?:abc){2} -> (?:abc){3}
function combineRepetitionWithPrevious(alternative, child, index) {
var node = alternative.node;
if (child.node.type === 'Repetition' && child.node.quantifier.greedy) {
var right = child.getChild();
var left = void 0;
if (right.node.type === 'Group' && !right.node.capturing) {
right = right.getChild();
}
var rightLength = void 0;
if (right.node.type === 'Alternative') {
rightLength = right.node.expressions.length;
left = NodePath.getForNode({
type: 'Alternative',
expressions: [].concat(_toConsumableArray(node.expressions.slice(index - rightLength, index)))
});
} else {
rightLength = 1;
left = alternative.getChild(index - 1);
if (left.node.type === 'Group' && !left.node.capturing) {
left = left.getChild();
}
}
if (left.hasEqualSource(right)) {
for (var j = index - rightLength; j < index; j++) {
alternative.getChild(index - rightLength).remove();
}
increaseQuantifierByOne(child.node.quantifier);
return index - rightLength;
}
}
return index;
}

View File

@ -0,0 +1,44 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var NodePath = require('../../traverse/node-path');
var _require = require('../../transform/utils'),
disjunctionToList = _require.disjunctionToList,
listToDisjunction = _require.listToDisjunction;
/**
* Removes duplicates from a disjunction sequence:
*
* /(ab|bc|ab)+(xy|xy)+/ -> /(ab|bc)+(xy)+/
*/
module.exports = {
Disjunction: function Disjunction(path) {
var node = path.node;
// Make unique nodes.
var uniqueNodesMap = {};
var parts = disjunctionToList(node).filter(function (part) {
var encoded = part ? NodePath.getForNode(part).jsonEncode() : 'null';
// Already recorded this part, filter out.
if (uniqueNodesMap.hasOwnProperty(encoded)) {
return false;
}
uniqueNodesMap[encoded] = part;
return true;
});
// Replace with the optimized disjunction.
path.replace(listToDisjunction(parts));
}
};

View File

@ -0,0 +1,92 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to replace single char group disjunction to char group
*
* a|b|c -> [abc]
* [12]|3|4 -> [1234]
* (a|b|c) -> ([abc])
* (?:a|b|c) -> [abc]
*/
module.exports = {
Disjunction: function Disjunction(path) {
var node = path.node,
parent = path.parent;
if (!handlers[parent.type]) {
return;
}
var charset = new Map();
if (!shouldProcess(node, charset) || !charset.size) {
return;
}
var characterClass = {
type: 'CharacterClass',
expressions: Array.from(charset.keys()).sort().map(function (key) {
return charset.get(key);
})
};
handlers[parent.type](path.getParent(), characterClass);
}
};
var handlers = {
RegExp: function RegExp(path, characterClass) {
var node = path.node;
node.body = characterClass;
},
Group: function Group(path, characterClass) {
var node = path.node;
if (node.capturing) {
node.expression = characterClass;
} else {
path.replace(characterClass);
}
}
};
function shouldProcess(expression, charset) {
if (!expression) {
// Abort on empty disjunction part
return false;
}
var type = expression.type;
if (type === 'Disjunction') {
var left = expression.left,
right = expression.right;
return shouldProcess(left, charset) && shouldProcess(right, charset);
} else if (type === 'Char') {
var value = expression.value;
charset.set(value, expression);
return true;
} else if (type === 'CharacterClass') {
return expression.expressions.every(function (expression) {
return shouldProcess(expression, charset);
});
}
return false;
}

View File

@ -0,0 +1,56 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
module.exports = {
// \ud83d\ude80 -> \u{1f680}
'charSurrogatePairToSingleUnicode': require('./char-surrogate-pair-to-single-unicode-transform'),
// \u0061 -> a
'charCodeToSimpleChar': require('./char-code-to-simple-char-transform'),
// /Aa/i -> /aa/i
'charCaseInsensitiveLowerCaseTransform': require('./char-case-insensitive-lowercase-transform'),
// [\d\d] -> [\d]
'charClassRemoveDuplicates': require('./char-class-remove-duplicates-transform'),
// a{1,2}a{2,3} -> a{3,5}
'quantifiersMerge': require('./quantifiers-merge-transform'),
// a{1,} -> a+, a{3,3} -> a{3}, a{1} -> a
'quantifierRangeToSymbol': require('./quantifier-range-to-symbol-transform'),
// [a-a] -> [a], [a-b] -> [ab]
'charClassClassrangesToChars': require('./char-class-classranges-to-chars-transform'),
// [a-de-f] -> [a-f]
'charClassClassrangesMerge': require('./char-class-classranges-merge-transform'),
// [0-9] -> [\d]
'charClassToMeta': require('./char-class-to-meta-transform'),
// [\d] -> \d, [^\w] -> \W
'charClassToSingleChar': require('./char-class-to-single-char-transform'),
// \e -> e
'charEscapeUnescape': require('./char-escape-unescape-transform'),
// (ab|ab) -> (ab)
'disjunctionRemoveDuplicates': require('./disjunction-remove-duplicates-transform'),
// (a|b|c) -> [abc]
'groupSingleCharsToCharClass': require('./group-single-chars-to-char-class'),
// (?:)a -> a
'removeEmptyGroup': require('./remove-empty-group-transform'),
// (?:a) -> a
'ungroup': require('./ungroup-transform'),
// abcabcabc -> (?:abc){3}
'combineRepeatingPatterns': require('./combine-repeating-patterns-transform')
};

View File

@ -0,0 +1,74 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to replace different range-based quantifiers
* with their symbol equivalents.
*
* a{0,} -> a*
* a{1,} -> a+
* a{1} -> a
*
* NOTE: the following is automatically handled in the generator:
*
* a{3,3} -> a{3}
*/
module.exports = {
Quantifier: function Quantifier(path) {
var node = path.node;
if (node.kind !== 'Range') {
return;
}
// a{0,} -> a*
rewriteOpenZero(path);
// a{1,} -> a+
rewriteOpenOne(path);
// a{1} -> a
rewriteExactOne(path);
}
};
function rewriteOpenZero(path) {
var node = path.node;
if (node.from !== 0 || node.to) {
return;
}
node.kind = '*';
delete node.from;
}
function rewriteOpenOne(path) {
var node = path.node;
if (node.from !== 1 || node.to) {
return;
}
node.kind = '+';
delete node.from;
}
function rewriteExactOne(path) {
var node = path.node;
if (node.from !== 1 || node.to !== 1) {
return;
}
path.parentPath.replace(path.parentPath.node.expression);
}

View File

@ -0,0 +1,113 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _require = require('../../transform/utils'),
increaseQuantifierByOne = _require.increaseQuantifierByOne;
/**
* A regexp-tree plugin to merge quantifiers
*
* a+a+ -> a{2,}
* a{2}a{3} -> a{5}
* a{1,2}a{2,3} -> a{3,5}
*/
module.exports = {
Repetition: function Repetition(path) {
var node = path.node,
parent = path.parent;
if (parent.type !== 'Alternative' || !path.index) {
return;
}
var previousSibling = path.getPreviousSibling();
if (!previousSibling) {
return;
}
if (previousSibling.node.type === 'Repetition') {
if (!previousSibling.getChild().hasEqualSource(path.getChild())) {
return;
}
var _extractFromTo = extractFromTo(previousSibling.node.quantifier),
previousSiblingFrom = _extractFromTo.from,
previousSiblingTo = _extractFromTo.to;
var _extractFromTo2 = extractFromTo(node.quantifier),
nodeFrom = _extractFromTo2.from,
nodeTo = _extractFromTo2.to;
// It's does not seem reliable to merge quantifiers with different greediness
// when none of both is a greedy open range
if (previousSibling.node.quantifier.greedy !== node.quantifier.greedy && !isGreedyOpenRange(previousSibling.node.quantifier) && !isGreedyOpenRange(node.quantifier)) {
return;
}
// a*a* -> a*
// a*a+ -> a+
// a+a+ -> a{2,}
// a{2}a{4} -> a{6}
// a{1,2}a{2,3} -> a{3,5}
// a{1,}a{2,} -> a{3,}
// a+a{2,} -> a{3,}
// a??a{2,} -> a{2,}
// a*?a{2,} -> a{2,}
// a+?a{2,} -> a{3,}
node.quantifier.kind = 'Range';
node.quantifier.from = previousSiblingFrom + nodeFrom;
if (previousSiblingTo && nodeTo) {
node.quantifier.to = previousSiblingTo + nodeTo;
} else {
delete node.quantifier.to;
}
if (isGreedyOpenRange(previousSibling.node.quantifier) || isGreedyOpenRange(node.quantifier)) {
node.quantifier.greedy = true;
}
previousSibling.remove();
} else {
if (!previousSibling.hasEqualSource(path.getChild())) {
return;
}
increaseQuantifierByOne(node.quantifier);
previousSibling.remove();
}
}
};
function isGreedyOpenRange(quantifier) {
return quantifier.greedy && (quantifier.kind === '+' || quantifier.kind === '*' || quantifier.kind === 'Range' && !quantifier.to);
}
function extractFromTo(quantifier) {
var from = void 0,
to = void 0;
if (quantifier.kind === '*') {
from = 0;
} else if (quantifier.kind === '+') {
from = 1;
} else if (quantifier.kind === '?') {
from = 0;
to = 1;
} else {
from = quantifier.from;
if (quantifier.to) {
to = quantifier.to;
}
}
return { from: from, to: to };
}

View File

@ -0,0 +1,34 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to remove non-capturing empty groups.
*
* /(?:)a/ -> /a/
* /a|(?:)/ -> /a|/
*/
module.exports = {
Group: function Group(path) {
var node = path.node,
parent = path.parent;
var childPath = path.getChild();
if (node.capturing || childPath) {
return;
}
if (parent.type === 'Repetition') {
path.getParent().replace(node);
} else if (parent.type !== 'RegExp') {
path.remove();
}
}
};

View File

@ -0,0 +1,55 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* A regexp-tree plugin to remove unnecessary groups.
*
* /(?:a)/ -> /a/
*/
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
module.exports = {
Group: function Group(path) {
var node = path.node,
parent = path.parent;
var childPath = path.getChild();
if (node.capturing || !childPath) {
return;
}
// Don't optimize /a(?:b|c)/ to /ab|c/
// but /(?:b|c)/ to /b|c/ is ok
if (childPath.node.type === 'Disjunction' && parent.type !== 'RegExp') {
return;
}
// Don't optimize /(?:ab)+/ to /ab+/
// but /(?:a)+/ to /a+/ is ok
// and /(?:[a-d])+/ to /[a-d]+/ is ok too
if (parent.type === 'Repetition' && childPath.node.type !== 'Char' && childPath.node.type !== 'CharacterClass') {
return;
}
if (childPath.node.type === 'Alternative') {
var parentPath = path.getParent();
if (parentPath.node.type === 'Alternative') {
// /abc(?:def)ghi/ When (?:def) is ungrouped its content must be merged with parent alternative
parentPath.replace({
type: 'Alternative',
expressions: [].concat(_toConsumableArray(parent.expressions.slice(0, path.index)), _toConsumableArray(childPath.node.expressions), _toConsumableArray(parent.expressions.slice(path.index + 1)))
});
}
} else {
path.replace(childPath.node);
}
}
};

File diff suppressed because one or more lines are too long

28
node_modules/regexp-tree/dist/parser/index.js generated vendored Normal file
View File

@ -0,0 +1,28 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var regexpTreeParser = require('./generated/regexp-tree');
/**
* Original parse function.
*/
var generatedParseFn = regexpTreeParser.parse.bind(regexpTreeParser);
/**
* Parses a regular expression.
*
* Override original `regexpTreeParser.parse` to convert a value to a string,
* since in regexp-tree we may pass strings, and RegExp instance.
*/
regexpTreeParser.parse = function (regexp, options) {
return generatedParseFn('' + regexp, options);
};
// By default do not capture locations; callers may override.
regexpTreeParser.setOptions({ captureLocations: false });
module.exports = regexpTreeParser;

View File

@ -0,0 +1,379 @@
'use strict';
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
var NON_BINARY_PROP_NAMES_TO_ALIASES = {
General_Category: 'gc',
Script: 'sc',
Script_Extensions: 'scx'
};
var NON_BINARY_ALIASES_TO_PROP_NAMES = inverseMap(NON_BINARY_PROP_NAMES_TO_ALIASES);
var BINARY_PROP_NAMES_TO_ALIASES = {
ASCII: 'ASCII',
ASCII_Hex_Digit: 'AHex',
Alphabetic: 'Alpha',
Any: 'Any',
Assigned: 'Assigned',
Bidi_Control: 'Bidi_C',
Bidi_Mirrored: 'Bidi_M',
Case_Ignorable: 'CI',
Cased: 'Cased',
Changes_When_Casefolded: 'CWCF',
Changes_When_Casemapped: 'CWCM',
Changes_When_Lowercased: 'CWL',
Changes_When_NFKC_Casefolded: 'CWKCF',
Changes_When_Titlecased: 'CWT',
Changes_When_Uppercased: 'CWU',
Dash: 'Dash',
Default_Ignorable_Code_Point: 'DI',
Deprecated: 'Dep',
Diacritic: 'Dia',
Emoji: 'Emoji',
Emoji_Component: 'Emoji_Component',
Emoji_Modifier: 'Emoji_Modifier',
Emoji_Modifier_Base: 'Emoji_Modifier_Base',
Emoji_Presentation: 'Emoji_Presentation',
Extended_Pictographic: 'Extended_Pictographic',
Extender: 'Ext',
Grapheme_Base: 'Gr_Base',
Grapheme_Extend: 'Gr_Ext',
Hex_Digit: 'Hex',
IDS_Binary_Operator: 'IDSB',
IDS_Trinary_Operator: 'IDST',
ID_Continue: 'IDC',
ID_Start: 'IDS',
Ideographic: 'Ideo',
Join_Control: 'Join_C',
Logical_Order_Exception: 'LOE',
Lowercase: 'Lower',
Math: 'Math',
Noncharacter_Code_Point: 'NChar',
Pattern_Syntax: 'Pat_Syn',
Pattern_White_Space: 'Pat_WS',
Quotation_Mark: 'QMark',
Radical: 'Radical',
Regional_Indicator: 'RI',
Sentence_Terminal: 'STerm',
Soft_Dotted: 'SD',
Terminal_Punctuation: 'Term',
Unified_Ideograph: 'UIdeo',
Uppercase: 'Upper',
Variation_Selector: 'VS',
White_Space: 'space',
XID_Continue: 'XIDC',
XID_Start: 'XIDS'
};
var BINARY_ALIASES_TO_PROP_NAMES = inverseMap(BINARY_PROP_NAMES_TO_ALIASES);
var GENERAL_CATEGORY_VALUE_TO_ALIASES = {
Cased_Letter: 'LC',
Close_Punctuation: 'Pe',
Connector_Punctuation: 'Pc',
Control: ['Cc', 'cntrl'],
Currency_Symbol: 'Sc',
Dash_Punctuation: 'Pd',
Decimal_Number: ['Nd', 'digit'],
Enclosing_Mark: 'Me',
Final_Punctuation: 'Pf',
Format: 'Cf',
Initial_Punctuation: 'Pi',
Letter: 'L',
Letter_Number: 'Nl',
Line_Separator: 'Zl',
Lowercase_Letter: 'Ll',
Mark: ['M', 'Combining_Mark'],
Math_Symbol: 'Sm',
Modifier_Letter: 'Lm',
Modifier_Symbol: 'Sk',
Nonspacing_Mark: 'Mn',
Number: 'N',
Open_Punctuation: 'Ps',
Other: 'C',
Other_Letter: 'Lo',
Other_Number: 'No',
Other_Punctuation: 'Po',
Other_Symbol: 'So',
Paragraph_Separator: 'Zp',
Private_Use: 'Co',
Punctuation: ['P', 'punct'],
Separator: 'Z',
Space_Separator: 'Zs',
Spacing_Mark: 'Mc',
Surrogate: 'Cs',
Symbol: 'S',
Titlecase_Letter: 'Lt',
Unassigned: 'Cn',
Uppercase_Letter: 'Lu'
};
var GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES = inverseMap(GENERAL_CATEGORY_VALUE_TO_ALIASES);
var SCRIPT_VALUE_TO_ALIASES = {
Adlam: 'Adlm',
Ahom: 'Ahom',
Anatolian_Hieroglyphs: 'Hluw',
Arabic: 'Arab',
Armenian: 'Armn',
Avestan: 'Avst',
Balinese: 'Bali',
Bamum: 'Bamu',
Bassa_Vah: 'Bass',
Batak: 'Batk',
Bengali: 'Beng',
Bhaiksuki: 'Bhks',
Bopomofo: 'Bopo',
Brahmi: 'Brah',
Braille: 'Brai',
Buginese: 'Bugi',
Buhid: 'Buhd',
Canadian_Aboriginal: 'Cans',
Carian: 'Cari',
Caucasian_Albanian: 'Aghb',
Chakma: 'Cakm',
Cham: 'Cham',
Cherokee: 'Cher',
Common: 'Zyyy',
Coptic: ['Copt', 'Qaac'],
Cuneiform: 'Xsux',
Cypriot: 'Cprt',
Cyrillic: 'Cyrl',
Deseret: 'Dsrt',
Devanagari: 'Deva',
Dogra: 'Dogr',
Duployan: 'Dupl',
Egyptian_Hieroglyphs: 'Egyp',
Elbasan: 'Elba',
Ethiopic: 'Ethi',
Georgian: 'Geor',
Glagolitic: 'Glag',
Gothic: 'Goth',
Grantha: 'Gran',
Greek: 'Grek',
Gujarati: 'Gujr',
Gunjala_Gondi: 'Gong',
Gurmukhi: 'Guru',
Han: 'Hani',
Hangul: 'Hang',
Hanifi_Rohingya: 'Rohg',
Hanunoo: 'Hano',
Hatran: 'Hatr',
Hebrew: 'Hebr',
Hiragana: 'Hira',
Imperial_Aramaic: 'Armi',
Inherited: ['Zinh', 'Qaai'],
Inscriptional_Pahlavi: 'Phli',
Inscriptional_Parthian: 'Prti',
Javanese: 'Java',
Kaithi: 'Kthi',
Kannada: 'Knda',
Katakana: 'Kana',
Kayah_Li: 'Kali',
Kharoshthi: 'Khar',
Khmer: 'Khmr',
Khojki: 'Khoj',
Khudawadi: 'Sind',
Lao: 'Laoo',
Latin: 'Latn',
Lepcha: 'Lepc',
Limbu: 'Limb',
Linear_A: 'Lina',
Linear_B: 'Linb',
Lisu: 'Lisu',
Lycian: 'Lyci',
Lydian: 'Lydi',
Mahajani: 'Mahj',
Makasar: 'Maka',
Malayalam: 'Mlym',
Mandaic: 'Mand',
Manichaean: 'Mani',
Marchen: 'Marc',
Medefaidrin: 'Medf',
Masaram_Gondi: 'Gonm',
Meetei_Mayek: 'Mtei',
Mende_Kikakui: 'Mend',
Meroitic_Cursive: 'Merc',
Meroitic_Hieroglyphs: 'Mero',
Miao: 'Plrd',
Modi: 'Modi',
Mongolian: 'Mong',
Mro: 'Mroo',
Multani: 'Mult',
Myanmar: 'Mymr',
Nabataean: 'Nbat',
New_Tai_Lue: 'Talu',
Newa: 'Newa',
Nko: 'Nkoo',
Nushu: 'Nshu',
Ogham: 'Ogam',
Ol_Chiki: 'Olck',
Old_Hungarian: 'Hung',
Old_Italic: 'Ital',
Old_North_Arabian: 'Narb',
Old_Permic: 'Perm',
Old_Persian: 'Xpeo',
Old_Sogdian: 'Sogo',
Old_South_Arabian: 'Sarb',
Old_Turkic: 'Orkh',
Oriya: 'Orya',
Osage: 'Osge',
Osmanya: 'Osma',
Pahawh_Hmong: 'Hmng',
Palmyrene: 'Palm',
Pau_Cin_Hau: 'Pauc',
Phags_Pa: 'Phag',
Phoenician: 'Phnx',
Psalter_Pahlavi: 'Phlp',
Rejang: 'Rjng',
Runic: 'Runr',
Samaritan: 'Samr',
Saurashtra: 'Saur',
Sharada: 'Shrd',
Shavian: 'Shaw',
Siddham: 'Sidd',
SignWriting: 'Sgnw',
Sinhala: 'Sinh',
Sogdian: 'Sogd',
Sora_Sompeng: 'Sora',
Soyombo: 'Soyo',
Sundanese: 'Sund',
Syloti_Nagri: 'Sylo',
Syriac: 'Syrc',
Tagalog: 'Tglg',
Tagbanwa: 'Tagb',
Tai_Le: 'Tale',
Tai_Tham: 'Lana',
Tai_Viet: 'Tavt',
Takri: 'Takr',
Tamil: 'Taml',
Tangut: 'Tang',
Telugu: 'Telu',
Thaana: 'Thaa',
Thai: 'Thai',
Tibetan: 'Tibt',
Tifinagh: 'Tfng',
Tirhuta: 'Tirh',
Ugaritic: 'Ugar',
Vai: 'Vaii',
Warang_Citi: 'Wara',
Yi: 'Yiii',
Zanabazar_Square: 'Zanb'
};
var SCRIPT_VALUE_ALIASES_TO_VALUE = inverseMap(SCRIPT_VALUE_TO_ALIASES);
function inverseMap(data) {
var inverse = {};
for (var name in data) {
if (!data.hasOwnProperty(name)) {
continue;
}
var value = data[name];
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
inverse[value[i]] = name;
}
} else {
inverse[value] = name;
}
}
return inverse;
}
function isValidName(name) {
return NON_BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) || BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
}
function isValidValue(name, value) {
if (isGeneralCategoryName(name)) {
return isGeneralCategoryValue(value);
}
if (isScriptCategoryName(name)) {
return isScriptCategoryValue(value);
}
return false;
}
function isAlias(name) {
return NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
}
function isGeneralCategoryName(name) {
return name === 'General_Category' || name == 'gc';
}
function isScriptCategoryName(name) {
return name === 'Script' || name === 'Script_Extensions' || name === 'sc' || name === 'scx';
}
function isGeneralCategoryValue(value) {
return GENERAL_CATEGORY_VALUE_TO_ALIASES.hasOwnProperty(value) || GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value);
}
function isScriptCategoryValue(value) {
return SCRIPT_VALUE_TO_ALIASES.hasOwnProperty(value) || SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value);
}
function isBinaryPropertyName(name) {
return BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
}
function getCanonicalName(name) {
if (NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
return NON_BINARY_ALIASES_TO_PROP_NAMES[name];
}
if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
return BINARY_ALIASES_TO_PROP_NAMES[name];
}
return null;
}
function getCanonicalValue(value) {
if (GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)) {
return GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES[value];
}
if (SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)) {
return SCRIPT_VALUE_ALIASES_TO_VALUE[value];
}
if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(value)) {
return BINARY_ALIASES_TO_PROP_NAMES[value];
}
return null;
}
module.exports = {
isAlias: isAlias,
isValidName: isValidName,
isValidValue: isValidValue,
isGeneralCategoryValue: isGeneralCategoryValue,
isScriptCategoryValue: isScriptCategoryValue,
isBinaryPropertyName: isBinaryPropertyName,
getCanonicalName: getCanonicalName,
getCanonicalValue: getCanonicalValue,
NON_BINARY_PROP_NAMES_TO_ALIASES: NON_BINARY_PROP_NAMES_TO_ALIASES,
NON_BINARY_ALIASES_TO_PROP_NAMES: NON_BINARY_ALIASES_TO_PROP_NAMES,
BINARY_PROP_NAMES_TO_ALIASES: BINARY_PROP_NAMES_TO_ALIASES,
BINARY_ALIASES_TO_PROP_NAMES: BINARY_ALIASES_TO_PROP_NAMES,
GENERAL_CATEGORY_VALUE_TO_ALIASES: GENERAL_CATEGORY_VALUE_TO_ALIASES,
GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES: GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES,
SCRIPT_VALUE_TO_ALIASES: SCRIPT_VALUE_TO_ALIASES,
SCRIPT_VALUE_ALIASES_TO_VALUE: SCRIPT_VALUE_ALIASES_TO_VALUE
};

178
node_modules/regexp-tree/dist/regexp-tree.js generated vendored Normal file
View File

@ -0,0 +1,178 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var compatTranspiler = require('./compat-transpiler');
var generator = require('./generator');
var optimizer = require('./optimizer');
var parser = require('./parser');
var _transform = require('./transform');
var _traverse = require('./traverse');
var fa = require('./interpreter/finite-automaton');
var _require = require('./compat-transpiler/runtime'),
RegExpTree = _require.RegExpTree;
/**
* An API object for RegExp processing (parsing/transform/generation).
*/
var regexpTree = {
/**
* Parser module exposed.
*/
parser: parser,
/**
* Expose finite-automaton module.
*/
fa: fa,
/**
* `TransformResult` exposed.
*/
TransformResult: _transform.TransformResult,
/**
* Parses a regexp string, producing an AST.
*
* @param string regexp
*
* a regular expression in different formats: string, AST, RegExp.
*
* @param Object options
*
* parsing options for this parse call. Default are:
*
* - captureLocations: boolean
* - any other custom options
*
* @return Object AST
*/
parse: function parse(regexp, options) {
return parser.parse('' + regexp, options);
},
/**
* Traverses a RegExp AST.
*
* @param Object ast
* @param Object | Array<Object> handlers
*
* Each `handler` is an object containing handler function for needed
* node types. Example:
*
* regexpTree.traverse(ast, {
* onChar(node) {
* ...
* },
* });
*
* The value for a node type may also be an object with functions pre and post.
* This enables more context-aware analyses, e.g. measuring star height.
*/
traverse: function traverse(ast, handlers, options) {
return _traverse.traverse(ast, handlers, options);
},
/**
* Transforms a regular expression.
*
* A regexp can be passed in different formats (string, regexp or AST),
* applying a set of transformations. It is a convenient wrapper
* on top of "parse-traverse-generate" tool chain.
*
* @param string | AST | RegExp regexp - a regular expression;
* @param Object | Array<Object> handlers - a list of handlers.
*
* @return TransformResult - a transformation result.
*/
transform: function transform(regexp, handlers) {
return _transform.transform(regexp, handlers);
},
/**
* Generates a RegExp string from an AST.
*
* @param Object ast
*
* Invariant:
*
* regexpTree.generate(regexpTree.parse('/[a-z]+/i')); // '/[a-z]+/i'
*/
generate: function generate(ast) {
return generator.generate(ast);
},
/**
* Creates a RegExp object from a regexp string.
*
* @param string regexp
*/
toRegExp: function toRegExp(regexp) {
var compat = this.compatTranspile(regexp);
return new RegExp(compat.getSource(), compat.getFlags());
},
/**
* Optimizes a regular expression by replacing some
* sub-expressions with their idiomatic patterns.
*
* @param string regexp
*
* @return TransformResult object
*/
optimize: function optimize(regexp, whitelist) {
return optimizer.optimize(regexp, whitelist);
},
/**
* Translates a regular expression in new syntax or in new format
* into equivalent expressions in old syntax.
*
* @param string regexp
*
* @return TransformResult object
*/
compatTranspile: function compatTranspile(regexp, whitelist) {
return compatTranspiler.transform(regexp, whitelist);
},
/**
* Executes a regular expression on a string.
*
* @param RegExp|string re - a regular expression.
* @param string string - a testing string.
*/
exec: function exec(re, string) {
if (typeof re === 'string') {
var compat = this.compatTranspile(re);
var extra = compat.getExtra();
if (extra.namedCapturingGroups) {
re = new RegExpTree(compat.toRegExp(), {
flags: compat.getFlags(),
source: compat.getSource(),
groups: extra.namedCapturingGroups
});
} else {
re = compat.toRegExp();
}
}
return re.exec(string);
}
};
module.exports = regexpTree;

138
node_modules/regexp-tree/dist/transform/index.js generated vendored Normal file
View File

@ -0,0 +1,138 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var generator = require('../generator');
var parser = require('../parser');
var traverse = require('../traverse');
/**
* Transform result.
*/
var TransformResult = function () {
/**
* Initializes a transform result for an AST.
*
* @param Object ast - an AST node
* @param mixed extra - any extra data a transform may return
*/
function TransformResult(ast) {
var extra = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
_classCallCheck(this, TransformResult);
this._ast = ast;
this._source = null;
this._string = null;
this._regexp = null;
this._extra = extra;
}
_createClass(TransformResult, [{
key: 'getAST',
value: function getAST() {
return this._ast;
}
}, {
key: 'setExtra',
value: function setExtra(extra) {
this._extra = extra;
}
}, {
key: 'getExtra',
value: function getExtra() {
return this._extra;
}
}, {
key: 'toRegExp',
value: function toRegExp() {
if (!this._regexp) {
this._regexp = new RegExp(this.getSource(), this._ast.flags);
}
return this._regexp;
}
}, {
key: 'getSource',
value: function getSource() {
if (!this._source) {
this._source = generator.generate(this._ast.body);
}
return this._source;
}
}, {
key: 'getFlags',
value: function getFlags() {
return this._ast.flags;
}
}, {
key: 'toString',
value: function toString() {
if (!this._string) {
this._string = generator.generate(this._ast);
}
return this._string;
}
}]);
return TransformResult;
}();
module.exports = {
/**
* Expose `TransformResult`.
*/
TransformResult: TransformResult,
/**
* Transforms a regular expression applying a set of
* transformation handlers.
*
* @param string | AST | RegExp:
*
* a regular expression in different representations: a string,
* a RegExp object, or an AST.
*
* @param Object | Array<Object>:
*
* a handler (or a list of handlers) from `traverse` API.
*
* @return TransformResult instance.
*
* Example:
*
* transform(/[a-z]/i, {
* onChar(path) {
* const {node} = path;
*
* if (...) {
* path.remove();
* }
* }
* });
*/
transform: function transform(regexp, handlers) {
var ast = regexp;
if (regexp instanceof RegExp) {
regexp = '' + regexp;
}
if (typeof regexp === 'string') {
ast = parser.parse(regexp, {
captureLocations: true
});
}
traverse.traverse(ast, handlers);
return new TransformResult(ast);
}
};

88
node_modules/regexp-tree/dist/transform/utils.js generated vendored Normal file
View File

@ -0,0 +1,88 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* Flattens a nested disjunction node to a list.
*
* /a|b|c|d/
*
* {{{a, b}, c}, d} -> [a, b, c, d]
*/
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function disjunctionToList(node) {
if (node.type !== 'Disjunction') {
throw new TypeError('Expected "Disjunction" node, got "' + node.type + '"');
}
var list = [];
if (node.left && node.left.type === 'Disjunction') {
list.push.apply(list, _toConsumableArray(disjunctionToList(node.left)).concat([node.right]));
} else {
list.push(node.left, node.right);
}
return list;
}
/**
* Builds a nested disjunction node from a list.
*
* /a|b|c|d/
*
* [a, b, c, d] -> {{{a, b}, c}, d}
*/
function listToDisjunction(list) {
return list.reduce(function (left, right) {
return {
type: 'Disjunction',
left: left,
right: right
};
});
}
/**
* Increases a quantifier by one.
* Does not change greediness.
* * -> +
* + -> {2,}
* ? -> {1,2}
* {2} -> {3}
* {2,} -> {3,}
* {2,3} -> {3,4}
*/
function increaseQuantifierByOne(quantifier) {
if (quantifier.kind === '*') {
quantifier.kind = '+';
} else if (quantifier.kind === '+') {
quantifier.kind = 'Range';
quantifier.from = 2;
delete quantifier.to;
} else if (quantifier.kind === '?') {
quantifier.kind = 'Range';
quantifier.from = 1;
quantifier.to = 2;
} else if (quantifier.kind === 'Range') {
quantifier.from += 1;
if (quantifier.to) {
quantifier.to += 1;
}
}
}
module.exports = {
disjunctionToList: disjunctionToList,
listToDisjunction: listToDisjunction,
increaseQuantifierByOne: increaseQuantifierByOne
};

313
node_modules/regexp-tree/dist/traverse/index.js generated vendored Normal file
View File

@ -0,0 +1,313 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var NodePath = require('./node-path');
/**
* Does an actual AST traversal, using visitor pattern,
* and calling set of callbacks.
*
* Based on https://github.com/olov/ast-traverse
*
* Expects AST in Mozilla Parser API: nodes which are supposed to be
* handled should have `type` property.
*
* @param Object root - a root node to start traversal from.
*
* @param Object options - an object with set of callbacks:
*
* - `pre(node, parent, prop, index)` - a hook called on node enter
* - `post`(node, parent, prop, index) - a hook called on node exit
* - `skipProperty(prop)` - a predicated whether a property should be skipped
*/
function astTraverse(root) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var pre = options.pre;
var post = options.post;
var skipProperty = options.skipProperty;
function visit(node, parent, prop, idx) {
if (!node || typeof node.type !== 'string') {
return;
}
var res = undefined;
if (pre) {
res = pre(node, parent, prop, idx);
}
if (res !== false) {
// A node can be replaced during traversal, so we have to
// recalculate it from the parent, to avoid traversing "dead" nodes.
if (parent && parent[prop]) {
if (!isNaN(idx)) {
node = parent[prop][idx];
} else {
node = parent[prop];
}
}
for (var _prop in node) {
if (node.hasOwnProperty(_prop)) {
if (skipProperty ? skipProperty(_prop, node) : _prop[0] === '$') {
continue;
}
var child = node[_prop];
// Collection node.
//
// NOTE: a node (or several nodes) can be removed or inserted
// during traversal.
//
// Current traversing index is stored on top of the
// `NodePath.traversingIndexStack`. The stack is used to support
// recursive nature of the traversal.
//
// In this case `NodePath.traversingIndex` (which we use here) is
// updated in the NodePath remove/insert methods.
//
if (Array.isArray(child)) {
var index = 0;
NodePath.traversingIndexStack.push(index);
while (index < child.length) {
visit(child[index], node, _prop, index);
index = NodePath.updateTraversingIndex(+1);
}
NodePath.traversingIndexStack.pop();
}
// Simple node.
else {
visit(child, node, _prop);
}
}
}
}
if (post) {
post(node, parent, prop, idx);
}
}
visit(root, null);
}
module.exports = {
/**
* Traverses an AST.
*
* @param Object ast - an AST node
*
* @param Object | Array<Object> handlers:
*
* an object (or an array of objects)
*
* Each such object contains a handler function per node.
* In case of an array of handlers, they are applied in order.
* A handler may return a transformed node (or a different type).
*
* The per-node function may instead be an object with functions pre and post.
* pre is called before visiting the node, post after.
* If a handler is a function, it is treated as the pre function, with an empty post.
*
* @param Object options:
*
* a config object, specifying traversal options:
*
* `asNodes`: boolean - whether handlers should receives raw AST nodes
* (false by default), instead of a `NodePath` wrapper. Note, by default
* `NodePath` wrapper provides a set of convenient method to manipulate
* a traversing AST, and also has access to all parents list. A raw
* nodes traversal should be used in rare cases, when no `NodePath`
* features are needed.
*
* Special hooks:
*
* - `shouldRun(ast)` - a predicate determining whether the handler
* should be applied.
*
* NOTE: Multiple handlers are used as an optimization of applying all of
* them in one AST traversal pass.
*/
traverse: function traverse(ast, handlers) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { asNodes: false };
if (!Array.isArray(handlers)) {
handlers = [handlers];
}
// Filter out handlers by result of `shouldRun`, if the method is present.
handlers = handlers.filter(function (handler) {
if (typeof handler.shouldRun !== 'function') {
return true;
}
return handler.shouldRun(ast);
});
NodePath.initRegistry();
// Allow handlers to initializer themselves.
handlers.forEach(function (handler) {
if (typeof handler.init === 'function') {
handler.init(ast);
}
});
function getPathFor(node, parent, prop, index) {
var parentPath = NodePath.getForNode(parent);
var nodePath = NodePath.getForNode(node, parentPath, prop, index);
return nodePath;
}
// Handle actual nodes.
astTraverse(ast, {
/**
* Handler on node enter.
*/
pre: function pre(node, parent, prop, index) {
var nodePath = void 0;
if (!options.asNodes) {
nodePath = getPathFor(node, parent, prop, index);
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = handlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var handler = _step.value;
// "Catch-all" `*` handler.
if (typeof handler['*'] === 'function') {
if (nodePath) {
// A path/node can be removed by some previous handler.
if (!nodePath.isRemoved()) {
var handlerResult = handler['*'](nodePath);
// Explicitly stop traversal.
if (handlerResult === false) {
return false;
}
}
} else {
handler['*'](node, parent, prop, index);
}
}
// Per-node handler.
var handlerFuncPre = void 0;
if (typeof handler[node.type] === 'function') {
handlerFuncPre = handler[node.type];
} else if (typeof handler[node.type] === 'object' && typeof handler[node.type].pre === 'function') {
handlerFuncPre = handler[node.type].pre;
}
if (handlerFuncPre) {
if (nodePath) {
// A path/node can be removed by some previous handler.
if (!nodePath.isRemoved()) {
var _handlerResult = handlerFuncPre.call(handler, nodePath);
// Explicitly stop traversal.
if (_handlerResult === false) {
return false;
}
}
} else {
handlerFuncPre.call(handler, node, parent, prop, index);
}
}
} // Loop over handlers
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
},
// pre func
/**
* Handler on node exit.
*/
post: function post(node, parent, prop, index) {
if (!node) {
return;
}
var nodePath = void 0;
if (!options.asNodes) {
nodePath = getPathFor(node, parent, prop, index);
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = handlers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var handler = _step2.value;
// Per-node handler.
var handlerFuncPost = void 0;
if (typeof handler[node.type] === 'object' && typeof handler[node.type].post === 'function') {
handlerFuncPost = handler[node.type].post;
}
if (handlerFuncPost) {
if (nodePath) {
// A path/node can be removed by some previous handler.
if (!nodePath.isRemoved()) {
var handlerResult = handlerFuncPost.call(handler, nodePath);
// Explicitly stop traversal.
if (handlerResult === false) {
return false;
}
}
} else {
handlerFuncPost.call(handler, node, parent, prop, index);
}
}
} // Loop over handlers
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
},
// post func
/**
* Skip locations by default.
*/
skipProperty: function skipProperty(prop) {
return prop === 'loc';
}
});
}
};

426
node_modules/regexp-tree/dist/traverse/node-path.js generated vendored Normal file
View File

@ -0,0 +1,426 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DEFAULT_COLLECTION_PROP = 'expressions';
var DEFAULT_SINGLE_PROP = 'expression';
/**
* NodePath class encapsulates a traversing node,
* its parent node, property name in the parent node, and
* an index (in case if a node is part of a collection).
* It also provides set of methods for AST manipulation.
*/
var NodePath = function () {
/**
* NodePath constructor.
*
* @param Object node - an AST node
* @param NodePath parentPath - a nullable parent path
* @param string property - property name of the node in the parent
* @param number index - index of the node in a collection.
*/
function NodePath(node) {
var parentPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
_classCallCheck(this, NodePath);
this.node = node;
this.parentPath = parentPath;
this.parent = parentPath ? parentPath.node : null;
this.property = property;
this.index = index;
}
_createClass(NodePath, [{
key: '_enforceProp',
value: function _enforceProp(property) {
if (!this.node.hasOwnProperty(property)) {
throw new Error('Node of type ' + this.node.type + ' doesn\'t have "' + property + '" collection.');
}
}
/**
* Sets a node into a children collection or the single child.
* By default child nodes are supposed to be under `expressions` property.
* An explicit property can be passed.
*
* @param Object node - a node to set into a collection or as single child
* @param number index - index at which to set
* @param string property - name of the collection or single property
*/
}, {
key: 'setChild',
value: function setChild(node) {
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var childPath = void 0;
if (index != null) {
if (!property) {
property = DEFAULT_COLLECTION_PROP;
}
this._enforceProp(property);
this.node[property][index] = node;
childPath = NodePath.getForNode(node, this, property, index);
} else {
if (!property) {
property = DEFAULT_SINGLE_PROP;
}
this._enforceProp(property);
this.node[property] = node;
childPath = NodePath.getForNode(node, this, property, null);
}
return childPath;
}
/**
* Appends a node to a children collection.
* By default child nodes are supposed to be under `expressions` property.
* An explicit property can be passed.
*
* @param Object node - a node to set into a collection or as single child
* @param string property - name of the collection or single property
*/
}, {
key: 'appendChild',
value: function appendChild(node) {
var property = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
if (!property) {
property = DEFAULT_COLLECTION_PROP;
}
this._enforceProp(property);
var end = this.node[property].length;
return this.setChild(node, end, property);
}
/**
* Inserts a node into a collection.
* By default child nodes are supposed to be under `expressions` property.
* An explicit property can be passed.
*
* @param Object node - a node to insert into a collection
* @param number index - index at which to insert
* @param string property - name of the collection property
*/
}, {
key: 'insertChildAt',
value: function insertChildAt(node, index) {
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_COLLECTION_PROP;
this._enforceProp(property);
this.node[property].splice(index, 0, node);
// If we inserted a node before the traversing index,
// we should increase the later.
if (index <= NodePath.getTraversingIndex()) {
NodePath.updateTraversingIndex(+1);
}
this._rebuildIndex(this.node, property);
}
/**
* Removes a node.
*/
}, {
key: 'remove',
value: function remove() {
if (this.isRemoved()) {
return;
}
NodePath.registry.delete(this.node);
this.node = null;
if (!this.parent) {
return;
}
// A node is in a collection.
if (this.index !== null) {
this.parent[this.property].splice(this.index, 1);
// If we remove a node before the traversing index,
// we should increase the later.
if (this.index <= NodePath.getTraversingIndex()) {
NodePath.updateTraversingIndex(-1);
}
// Rebuild index.
this._rebuildIndex(this.parent, this.property);
this.index = null;
this.property = null;
return;
}
// A simple node.
delete this.parent[this.property];
this.property = null;
}
/**
* Rebuilds child nodes index (used on remove/insert).
*/
}, {
key: '_rebuildIndex',
value: function _rebuildIndex(parent, property) {
var parentPath = NodePath.getForNode(parent);
for (var i = 0; i < parent[property].length; i++) {
var path = NodePath.getForNode(parent[property][i], parentPath, property, i);
path.index = i;
}
}
/**
* Whether the path was removed.
*/
}, {
key: 'isRemoved',
value: function isRemoved() {
return this.node === null;
}
/**
* Replaces a node with the passed one.
*/
}, {
key: 'replace',
value: function replace(newNode) {
NodePath.registry.delete(this.node);
this.node = newNode;
if (!this.parent) {
return null;
}
// A node is in a collection.
if (this.index !== null) {
this.parent[this.property][this.index] = newNode;
}
// A simple node.
else {
this.parent[this.property] = newNode;
}
// Rebuild the node path for the new node.
return NodePath.getForNode(newNode, this.parentPath, this.property, this.index);
}
/**
* Updates a node inline.
*/
}, {
key: 'update',
value: function update(nodeProps) {
Object.assign(this.node, nodeProps);
}
/**
* Returns parent.
*/
}, {
key: 'getParent',
value: function getParent() {
return this.parentPath;
}
/**
* Returns nth child.
*/
}, {
key: 'getChild',
value: function getChild() {
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
if (this.node.expressions) {
return NodePath.getForNode(this.node.expressions[n], this, DEFAULT_COLLECTION_PROP, n);
} else if (this.node.expression && n == 0) {
return NodePath.getForNode(this.node.expression, this, DEFAULT_SINGLE_PROP);
}
return null;
}
/**
* Whether a path node is syntactically equal to the passed one.
*
* NOTE: we don't rely on `source` property from the `loc` data
* (which would be the fastest comparison), since it might be unsync
* after several modifications. We use here simple `JSON.stringify`
* excluding the `loc` data.
*
* @param NodePath other - path to compare to.
* @return boolean
*/
}, {
key: 'hasEqualSource',
value: function hasEqualSource(path) {
return JSON.stringify(this.node, jsonSkipLoc) === JSON.stringify(path.node, jsonSkipLoc);
}
/**
* JSON-encodes a node skipping location.
*/
}, {
key: 'jsonEncode',
value: function jsonEncode() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
format = _ref.format,
useLoc = _ref.useLoc;
return JSON.stringify(this.node, useLoc ? null : jsonSkipLoc, format);
}
/**
* Returns previous sibling.
*/
}, {
key: 'getPreviousSibling',
value: function getPreviousSibling() {
if (!this.parent || this.index == null) {
return null;
}
return NodePath.getForNode(this.parent[this.property][this.index - 1], NodePath.getForNode(this.parent), this.property, this.index - 1);
}
/**
* Returns next sibling.
*/
}, {
key: 'getNextSibling',
value: function getNextSibling() {
if (!this.parent || this.index == null) {
return null;
}
return NodePath.getForNode(this.parent[this.property][this.index + 1], NodePath.getForNode(this.parent), this.property, this.index + 1);
}
/**
* Returns a NodePath instance for a node.
*
* The same NodePath can be reused in several places, e.g.
* a parent node passed for all its children.
*/
}], [{
key: 'getForNode',
value: function getForNode(node) {
var parentPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var prop = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : -1;
if (!node) {
return null;
}
if (!NodePath.registry.has(node)) {
NodePath.registry.set(node, new NodePath(node, parentPath, prop, index == -1 ? null : index));
}
var path = NodePath.registry.get(node);
if (parentPath !== null) {
path.parentPath = parentPath;
path.parent = path.parentPath.node;
}
if (prop !== null) {
path.property = prop;
}
if (index >= 0) {
path.index = index;
}
return path;
}
/**
* Initializes the NodePath registry. The registry is a map from
* a node to its NodePath instance.
*/
}, {
key: 'initRegistry',
value: function initRegistry() {
if (!NodePath.registry) {
NodePath.registry = new Map();
}
NodePath.registry.clear();
}
/**
* Updates index of a currently traversing collection.
*/
}, {
key: 'updateTraversingIndex',
value: function updateTraversingIndex(dx) {
return NodePath.traversingIndexStack[NodePath.traversingIndexStack.length - 1] += dx;
}
/**
* Returns current traversing index.
*/
}, {
key: 'getTraversingIndex',
value: function getTraversingIndex() {
return NodePath.traversingIndexStack[NodePath.traversingIndexStack.length - 1];
}
}]);
return NodePath;
}();
NodePath.initRegistry();
/**
* Index of a currently traversing collection is stored on top of the
* `NodePath.traversingIndexStack`. Remove/insert methods can adjust
* this index.
*/
NodePath.traversingIndexStack = [];
// Helper function used to skip `loc` in JSON operations.
function jsonSkipLoc(prop, value) {
if (prop === 'loc') {
return undefined;
}
return value;
}
module.exports = NodePath;

29
node_modules/regexp-tree/dist/utils/clone.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
'use strict';
/**
* Performs a deep copy of an simple object.
* Only handles scalar values, arrays and objects.
*
* @param obj Object
*/
module.exports = function clone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
var res = void 0;
if (Array.isArray(obj)) {
res = [];
} else {
res = {};
}
for (var i in obj) {
res[i] = clone(obj[i]);
}
return res;
};