pax_global_header00006660000000000000000000000064136150731750014522gustar00rootroot0000000000000052 comment=ff52eb25258b49781305e0845365546bd8ef2fc0 regexp-tree-0.1.18/000077500000000000000000000000001361507317500140405ustar00rootroot00000000000000regexp-tree-0.1.18/.babelrc000066400000000000000000000003311361507317500154300ustar00rootroot00000000000000{ "env": { "production": { "presets": [ [ "env", { "targets": { "node": "0.12" } } ], "flow" ] } } } regexp-tree-0.1.18/.editorconfig000066400000000000000000000001361361507317500165150ustar00rootroot00000000000000root = true [*] end_of_line = lf charset = utf-8 [*.js] indent_style = space indent_size = 2regexp-tree-0.1.18/.eslintignore000066400000000000000000000000511361507317500165370ustar00rootroot00000000000000src/parser/generated/regexp-tree.js dist/regexp-tree-0.1.18/.eslintrc.json000066400000000000000000000011741361507317500166370ustar00rootroot00000000000000{ "env": { "es6": true, "node": true, "jest": true }, "extends": "eslint:recommended", "rules": { "no-control-regex": "off", "indent": ["error", 2, { "SwitchCase": 1 }], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "single", { "allowTemplateLiterals": true } ], "semi": [ "error", "always" ], "no-useless-escape": 0, "no-console": ["error", { "allow": ["warn", "error", "info"] }] } }regexp-tree-0.1.18/.gitignore000066400000000000000000000000601361507317500160240ustar00rootroot00000000000000/dist node_modules/ .npm-debug.log npm-debug.logregexp-tree-0.1.18/.npmignore000066400000000000000000000002501361507317500160340ustar00rootroot00000000000000/scripts/ /src/ .editorconfig .eslintignore .eslintrc.json .prettierignore .prettierrc .watchmanconfig .gitignore .eslintrc .babelrc .travis.yml .module-cache __tests__regexp-tree-0.1.18/.prettierignore000066400000000000000000000000511361507317500170770ustar00rootroot00000000000000src/parser/generated/regexp-tree.js dist/regexp-tree-0.1.18/.prettierrc000066400000000000000000000002031361507317500162170ustar00rootroot00000000000000{ "singleQuote": true, "semi": true, "useTabs": false, "tabWidth": 2, "trailingComma": "es5", "bracketSpacing": false }regexp-tree-0.1.18/.travis.yml000066400000000000000000000000701361507317500161460ustar00rootroot00000000000000sudo: false language: node_js node_js: - "6" - "6.1"regexp-tree-0.1.18/.watchmanconfig000066400000000000000000000000001361507317500170170ustar00rootroot00000000000000regexp-tree-0.1.18/LICENSE000066400000000000000000000020611361507317500150440ustar00rootroot00000000000000MIT License Copyright (c) 2017 Dmitry Soshnikov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. regexp-tree-0.1.18/README.md000066400000000000000000001270331361507317500153250ustar00rootroot00000000000000# regexp-tree [![Build Status](https://travis-ci.org/DmitrySoshnikov/regexp-tree.svg?branch=master)](https://travis-ci.org/DmitrySoshnikov/regexp-tree) [![npm version](https://badge.fury.io/js/regexp-tree.svg)](https://badge.fury.io/js/regexp-tree) [![npm downloads](https://img.shields.io/npm/dt/regexp-tree.svg)](https://www.npmjs.com/package/regexp-tree) Regular expressions processor in JavaScript TL;DR: **RegExp Tree** is a _regular expressions processor_, which includes _parser_, _traversal_, _transformer_, _optimizer_, and _interpreter_ APIs. You can get an overview of the tool in [this article](https://medium.com/@DmitrySoshnikov/regexp-tree-a-regular-expressions-parser-with-a-simple-ast-format-bcd4d5580df6). ### Table of Contents - [Installation](#installation) - [Development](#development) - [Usage as a CLI](#usage-as-a-cli) - [Usage from Node](#usage-from-node) - [Capturing locations](#capturing-locations) - [Using traversal API](#using-traversal-api) - [Using transform API](#using-transform-api) - [Transform plugins](#transform-plugins) - [Using generator API](#using-generator-api) - [Using optimizer API](#using-optimizer-api) - [Optimizer ESLint plugin](#optimizer-eslint-plugin) - [Using compat-transpiler API](#using-compat-transpiler-api) - [Compat-transpiler Babel plugin](#compat-transpiler-babel-plugin) - [RegExp extensions](#regexp-extensions) - [RegExp extensions Babel plugin](#regexp-extensions-babel-plugin) - [Creating RegExp objects](#creating-regexp-objects) - [Executing regexes](#executing-regexes) - [Using interpreter API](#using-interpreter-api) - [Printing NFA/DFA tables](#printing-nfadfa-tables) - [AST nodes specification](#ast-nodes-specification) ### Installation The parser can be installed as an [npm module](https://www.npmjs.com/package/regexp-tree): ``` npm install -g regexp-tree ``` You can also [try it online](https://astexplorer.net/#/gist/4ea2b52f0e546af6fb14f9b2f5671c1c/39b55944da3e5782396ffa1fea3ba68d126cd394) using _AST Explorer_. ### Development 1. Fork https://github.com/DmitrySoshnikov/regexp-tree repo 2. If there is an actual issue from the [issues](https://github.com/DmitrySoshnikov/regexp-tree/issues) list you'd like to work on, feel free to assign it yourself, or comment on it to avoid collisions (open a new issue if needed) 3. Make your changes 4. Make sure `npm test` still passes (add new tests if needed) 5. Submit a PR The _regexp-tree_ parser is implemented as an automatic LR parser using [Syntax](https://www.npmjs.com/package/syntax-cli) tool. The parser module is generated from the [regexp grammar](https://github.com/DmitrySoshnikov/regexp-tree/blob/master/src/parser/regexp.bnf), which is based on the regular expressions grammar used in ECMAScript. For development from the github repository, run `build` command to generate the parser module, and transpile JS code: ``` git clone https://github.com//regexp-tree.git cd regexp-tree npm install npm run build ``` > NOTE: JS code transpilation is used to support older versions of Node. For faster development cycle you can use `npm run watch` command, which continuously transpiles JS code. ### Usage as a CLI **Note:** the CLI is exposed as its own [regexp-tree-cli](https://www.npmjs.com/package/regexp-tree-cli) module. Check the options available from CLI: ``` regexp-tree-cli --help ``` ``` Usage: regexp-tree-cli [options] Options: -e, --expression A regular expression to be parsed -l, --loc Whether to capture AST node locations -o, --optimize Applies optimizer on the passed expression -c, --compat Applies compat-transpiler on the passed expression -t, --table Print NFA/DFA transition tables (nfa/dfa/all) ``` To parse a regular expression, pass `-e` option: ``` regexp-tree-cli -e '/a|b/i' ``` Which produces an AST node corresponding to this regular expression: ```js { type: 'RegExp', body: { type: 'Disjunction', left: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, right: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } }, flags: 'i', } ``` > NOTE: the format of a regexp is `/ Body / OptionalFlags`. ### Usage from Node The parser can also be used as a Node module: ```js const regexpTree = require('regexp-tree'); console.log(regexpTree.parse(/a|b/i)); // RegExp AST ``` Note, _regexp-tree_ supports parsing regexes from strings, and also from actual `RegExp` objects (in general -- from any object which can be coerced to a string). If some feature is not implemented yet in an actual JavaScript RegExp, it should be passed as a string: ```js // Pass an actual JS RegExp object. regexpTree.parse(/a|b/i); // Pass a string, since `s` flag may not be supported in older versions. regexpTree.parse('/./s'); ``` Also note, that in string-mode, escaping is done using two slashes `\\` per JavaScript: ```js // As an actual regexp. regexpTree.parse(/\n/); // As a string. regexpTree.parse('/\\n/'); ``` ### Capturing locations For source code transformation tools it might be useful also to capture _locations_ of the AST nodes. From the command line it's controlled via the `-l` option: ``` regexp-tree-cli -e '/ab/' -l ``` This attaches `loc` object to each AST node: ```js { type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97, loc: { start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 2, offset: 2, }, } }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98, loc: { start: { line: 1, column: 2, offset: 2, }, end: { line: 1, column: 3, offset: 3, }, } } ], loc: { start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 3, offset: 3, }, } }, flags: '', loc: { start: { line: 1, column: 0, offset: 0, }, end: { line: 1, column: 4, offset: 4, }, } } ``` From Node it's controlled via `setOptions` method exposed on the parser: ```js const regexpTree = require('regexp-tree'); const parsed = regexpTree .parser .setOptions({captureLocations: true}) .parse(/a|b/); ``` The `setOptions` method sets global options, which are preserved between calls. It is also possible to provide options per a single `parse` call, which might be more preferred: ```js const regexpTree = require('regexp-tree'); const parsed = regexpTree.parse(/a|b/, { captureLocations: true, }); ``` ### Using traversal API The [traverse](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/traverse) module allows handling needed AST nodes using the _visitor_ pattern. In Node the module is exposed as the `regexpTree.traverse` method. Handlers receive an instance of the [NodePath](https://github.com/DmitrySoshnikov/regexp-tree/blob/master/src/traverse/README.md#nodepath-class) class, which encapsulates `node` itself, its `parent` node, `property`, and `index` (in case the node is part of a collection). Visiting a node follows this algorithm: - call `pre` handler. - recurse into node's children. - call `post` handler. For each node type of interest, you can provide either: - a function (`pre`). - an object with members `pre` and `post`. You can also provide a `*` handler which will be executed on every node. Example: ```js const regexpTree = require('regexp-tree'); // Get AST. const ast = regexpTree.parse('/[a-z]{1,}/'); // Traverse AST nodes. regexpTree.traverse(ast, { // Visit every node before any type-specific handlers. '*': function({node}) { ... }, // Handle "Quantifier" node type. Quantifier({node}) { ... }, // Handle "Char" node type, before and after. Char: { pre({node}) { ... }, post({node}) { ... } } }); // Generate the regexp. const re = regexpTree.generate(ast); console.log(re); // '/[a-z]+/' ``` ### Using transform API > NOTE: you can play with transformation APIs, and write actual transforms for quick tests in AST Explorer. See [this example](http://astexplorer.net/#/gist/d293d22742b42cd1f7ee7b7e5dc6f697/39b0aabc42fb6fb106b9e368341d3300098f08c0). While traverse module provides basic traversal API, which can be used for any purposes of AST handling, _transform_ module focuses mainly on _transformation_ of regular expressions. It accepts a regular expressions in different formats (string, an actual `RegExp` object, or an AST), applies a set of transformations, and retuns an instance of [TransformResult](https://github.com/DmitrySoshnikov/regexp-tree/blob/master/src/transform/README.md#transformresult). Handles receive as a parameter the same [NodePath](https://github.com/DmitrySoshnikov/regexp-tree/blob/master/src/traverse/README.md#nodepath-class) object used in traverse. Example: ```js const regexpTree = require('regexp-tree'); // Handle nodes. const re = regexpTree.transform('/[a-z]{1,}/i', { /** * Handle "Quantifier" node type, * transforming `{1,}` quantifier to `+`. */ Quantifier(path) { const {node} = path; // {1,} -> + if ( node.kind === 'Range' && node.from === 1 && !node.to ) { path.replace({ type: 'Quantifier', kind: '+', greedy: node.greedy, }); } }, }); console.log(re.toString()); // '/[a-z]+/i' console.log(re.toRegExp()); // /[a-z]+/i console.log(re.getAST()); // AST for /[a-z]+/i ``` #### Transform plugins A _transformation plugin_ is a module which exports a _transformation handler_. We have seen [above](#using-transform-api) how we can pass a handler object directly to the `regexpTree.transform` method, here we extract it into a separate module, so it can be implemented and shared independently: Example of a plugin: ```js // file: ./regexp-tree-a-to-b-transform.js /** * This plugin replaces chars 'a' with chars 'b'. */ module.exports = { Char({node}) { if (node.kind === 'simple' && node.value === 'a') { node.value = 'b'; node.symbol = 'b'; node.codePoint = 98; } }, }; ``` Once we have this plugin ready, we can require it, and pass to the `transform` function: ```js const regexpTree = require('regexp-tree'); const plugin = require('./regexp-tree-a-to-b-transform'); const re = regexpTree.transform(/(a|c)a+[a-z]/, plugin); console.log(re.toRegExp()); // /(b|c)b+[b-z]/ ``` > NOTE: we can also pass a _list of plugins_ to the `regexpTree.transform`. In this case the plugins are applied in one pass in order. Another approach is to run several sequential calls to `transform`, setting up a pipeline, when a transformed AST is passed further to another plugin, etc. You can see other examples of transform plugins in the [optimizer/transforms](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/optimizer/transforms) or in the [compat-transpiler/transforms](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/compat-transpiler/transforms) directories. ### Using generator API The [generator](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/generator) module generates regular expressions from corresponding AST nodes. In Node the module is exposed as `regexpTree.generate` method. Example: ```js const regexpTree = require('regexp-tree'); const re = regexpTree.generate({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, flags: 'i', }); console.log(re); // '/a/i' ``` ### Using optimizer API [Optimizer](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/optimizer) transforms your regexp into an _optimized_ version, replacing some sub-expressions with their idiomatic patterns. This might be good for different kinds of minifiers, as well as for regexp machines. > NOTE: the Optimizer is implemented as a set of _regexp-tree_ [plugins](#transform-plugins). Example: ```js const regexpTree = require('regexp-tree'); const originalRe = /[a-zA-Z_0-9][A-Z_\da-z]*\e{1,}/; const optimizedRe = regexpTree .optimize(originalRe) .toRegExp(); console.log(optimizedRe); // /\w+e+/ ``` From CLI the optimizer is available via `--optimize` (`-o`) option: ``` regexp-tree-cli -e '/[a-zA-Z_0-9][A-Z_\da-z]*\e{1,}/' -o ``` Result: ``` Optimized: /\w+e+/ ``` See the [optimizer README](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/optimizer) for more details. #### Optimizer ESLint plugin The [optimizer](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/optimizer) module is also available as an _ESLint plugin_, which can be installed at: [eslint-plugin-optimize-regex](https://www.npmjs.com/package/eslint-plugin-optimize-regex). ### Using compat-transpiler API The [compat-transpiler](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/compat-transpiler) module translates your regexp in new format or in new syntax, into an equivalent regexp in a legacy representation, so it can be used in engines which don't yet implement the new syntax. > NOTE: the compat-transpiler is implemented as a set of _regexp-tree_ [plugins](#transform-plugins). Example, "dotAll" `s` flag: ```js /./s ``` Is translated into: ```js /[\0-\uFFFF]/ ``` Or [named capturing groups](#named-capturing-group): ```js /(?a)\k\1/ ``` Becomes: ```js /(a)\1\1/ ``` To use the API from Node: ```js const regexpTree = require('regexp-tree'); // Using new syntax. const originalRe = '/(?.)\\k/s'; // For legacy engines. const compatTranspiledRe = regexpTree .compatTranspile(originalRe) .toRegExp(); console.log(compatTranspiledRe); // /([\0-\uFFFF])\1/ ``` From CLI the compat-transpiler is available via `--compat` (`-c`) option: ``` regexp-tree-cli -e '/(?.)\k/s' -c ``` Result: ``` Compat: /([\0-\uFFFF])\1/ ``` #### Compat-transpiler Babel plugin The [compat-transpiler](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/compat-transpiler) module is also available as a _Babel plugin_, which can be installed at: [babel-plugin-transform-modern-regexp](https://www.npmjs.com/package/babel-plugin-transform-modern-regexp). Note, the plugin also includes [extended regexp](#regexp-extensions) features. ### RegExp extensions Besides future proposals, like [named capturing group](#named-capturing-group), and other which are being currently standardized, _regexp-tree_ also supports _non-standard_ features. > NOTE: _"non-standard"_ means specifically ECMAScript standard, since in other regexp egnines, e.g. PCRE, Python, etc. these features are standard. One of such featurs is `x` flag, which enables _extended_ mode of regular expressions. In this mode most of whitespaces are ignored, and expressions can use #-comments. Example: ```regex / # A regular expression for date. (?\d{4})- # year part of a date (?\d{2})- # month part of a date (?\d{2}) # day part of a date /x ``` This is normally parsed by the _regexp-tree_ parser, and [compat-transpiler](#using-compat-transpiler-api) has full support for it; it's translated into: ```regex /(\d{4})-(\d{2})-(\d{2})/ ``` #### RegExp extensions Babel plugin The regexp extensions are also available as a _Babel plugin_, which can be installed at: [babel-plugin-transform-modern-regexp](https://www.npmjs.com/package/babel-plugin-transform-modern-regexp). Note, the plugin also includes [compat-transpiler](#using-compat-transpiler-api) features. ### Creating RegExp objects To create an actual `RegExp` JavaScript object, we can use `regexpTree.toRegExp` method: ```js const regexpTree = require('regexp-tree'); const re = regexpTree.toRegExp('/[a-z]/i'); console.log( re.test('a'), // true re.test('Z'), // true ); ``` ### Executing regexes It is also possible to execute regular expressions using `exec` API method, which has support for new syntax, and features, such as [named capturing group](#named-capturing-group), etc: ```js const regexpTree = require('regexp-tree'); const re = `/ # A regular expression for date. (?\\d{4})- # year part of a date (?\\d{2})- # month part of a date (?\\d{2}) # day part of a date /x`; const string = '2017-04-14'; const result = regexpTree.exec(re, string); console.log(result.groups); // {year: '2017', month: '04', day: '14'} ``` ### Using interpreter API > NOTE: you can read more about implementation details of the interpreter in [this series of articles](https://medium.com/@DmitrySoshnikov/building-a-regexp-machine-part-1-regular-grammars-d4986b585d7e). In addition to executing regular expressions using JavaScript built-in RegExp engine, RegExp Tree also implements own [interpreter](https://github.com/DmitrySoshnikov/regexp-tree/tree/master/src/interpreter/finite-automaton) based on classic NFA/DFA finite automaton engine. Currently it aims educational purposes -- to trace the regexp matching process, transitioning in NFA/DFA states. It also allows building state transitioning table, which can be used for custom implementation. In API the module is exposed as `fa` (finite-automaton) object. Example: ```js const {fa} = require('regexp-tree'); const re = /ab|c*/; console.log(fa.test(re, 'ab')); // true console.log(fa.test(re, '')); // true console.log(fa.test(re, 'c')); // true // NFA, and its transition table. const nfa = fa.toNFA(re); console.log(nfa.getTransitionTable()); // DFA, and its transition table. const dfa = fa.toDFA(re); console.log(dfa.getTransitionTable()); ``` For more granular work with NFA and DFA, `fa` module also exposes convenient builders, so you can build NFA fragments directly: ```js const {fa} = require('regexp-tree'); const { alt, char, or, rep, } = fa.builders; // ab|c* const re = or( alt(char('a'), char('b')), rep(char('c')) ); console.log(re.matches('ab')); // true console.log(re.matches('')); // true console.log(re.matches('c')); // true // Build DFA from NFA const {DFA} = fa; const reDFA = new DFA(re); console.log(reDFA.matches('ab')); // true console.log(reDFA.matches('')); // true console.log(reDFA.matches('c')); // true ``` #### Printing NFA/DFA tables The `--table` option allows displaying NFA/DFA transition tables. RegExp Tree also applies _DFA minimization_ (using _N-equivalence_ algorithm), and produces the minimal transition table as its final result. In the example below for the `/a|b|c/` regexp, we first obtain the NFA transition table, which is further converted to the original DFA transition table (down from the 10 non-deterministic states to 4 deterministic states), and eventually minimized to the final DFA table (from 4 to only 2 states). ``` ./bin/regexp-tree-cli -e '/a|b|c/' --table all ``` Result: ``` > - starting ✓ - accepting NFA transition table: ┌─────┬───┬───┬────┬─────────────┐ │ │ a │ b │ c │ ε* │ ├─────┼───┼───┼────┼─────────────┤ │ 1 > │ │ │ │ {1,2,3,7,9} │ ├─────┼───┼───┼────┼─────────────┤ │ 2 │ │ │ │ {2,3,7} │ ├─────┼───┼───┼────┼─────────────┤ │ 3 │ 4 │ │ │ 3 │ ├─────┼───┼───┼────┼─────────────┤ │ 4 │ │ │ │ {4,5,6} │ ├─────┼───┼───┼────┼─────────────┤ │ 5 │ │ │ │ {5,6} │ ├─────┼───┼───┼────┼─────────────┤ │ 6 ✓ │ │ │ │ 6 │ ├─────┼───┼───┼────┼─────────────┤ │ 7 │ │ 8 │ │ 7 │ ├─────┼───┼───┼────┼─────────────┤ │ 8 │ │ │ │ {8,5,6} │ ├─────┼───┼───┼────┼─────────────┤ │ 9 │ │ │ 10 │ 9 │ ├─────┼───┼───┼────┼─────────────┤ │ 10 │ │ │ │ {10,6} │ └─────┴───┴───┴────┴─────────────┘ DFA: Original transition table: ┌─────┬───┬───┬───┐ │ │ a │ b │ c │ ├─────┼───┼───┼───┤ │ 1 > │ 4 │ 3 │ 2 │ ├─────┼───┼───┼───┤ │ 2 ✓ │ │ │ │ ├─────┼───┼───┼───┤ │ 3 ✓ │ │ │ │ ├─────┼───┼───┼───┤ │ 4 ✓ │ │ │ │ └─────┴───┴───┴───┘ DFA: Minimized transition table: ┌─────┬───┬───┬───┐ │ │ a │ b │ c │ ├─────┼───┼───┼───┤ │ 1 > │ 2 │ 2 │ 2 │ ├─────┼───┼───┼───┤ │ 2 ✓ │ │ │ │ └─────┴───┴───┴───┘ ``` ### AST nodes specification Below are the AST node types for different regular expressions patterns: - [Char](#char) - [Simple char](#simple-char) - [Escaped char](#escaped-char) - [Meta char](#meta-char) - [Control char](#control-char) - [Hex char-code](#hex-char-code) - [Decimal char-code](#decimal-char-code) - [Octal char-code](#octal-char-code) - [Unicode](#unicode) - [Character class](#character-class) - [Positive character class](#positive-character-class) - [Negative character class](#negative-character-class) - [Character class ranges](#character-class-ranges) - [Unicode properties](#unicode-properties) - [Alternative](#alternative) - [Disjunction](#disjunction) - [Groups](#groups) - [Capturing group](#capturing-group) - [Named capturing group](#named-capturing-group) - [Non-capturing group](#non-capturing-group) - [Backreferences](#backreferences) - [Quantifiers](#quantifiers) - [? zero-or-one](#-zero-or-one) - [* zero-or-more](#-zero-or-more) - [+ one-or-more](#-one-or-more) - [Range-based quantifiers](#range-based-quantifiers) - [Exact number of matches](#exact-number-of-matches) - [Open range](#open-range) - [Closed range](#closed-range) - [Non-greedy](#non-greedy) - [Assertions](#assertions) - [^ begin marker](#-begin-marker) - [$ end marker](#-end-marker) - [Boundary assertions](#boundary-assertions) - [Lookahead assertions](#lookahead-assertions) - [Positive lookahead assertion](#positive-lookahead-assertion) - [Negative lookahead assertion](#negative-lookahead-assertion) - [Lookbehind assertions](#lookbehind-assertions) - [Positive lookbehind assertion](#positive-lookbehind-assertion) - [Negative lookbehind assertion](#negative-lookbehind-assertion) #### Char A basic building block, single character. Can be _escaped_, and be of different _kinds_. ##### Simple char Basic _non-escaped_ char in a regexp: ``` z ``` Node: ```js { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 122 } ``` > NOTE: to test this from CLI, the char should be in an actual regexp -- `/z/`. ##### Escaped char ``` \z ``` The same value, `escaped` flag is added: ```js { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 122, escaped: true } ``` Escaping is mostly used with meta symbols: ``` // Syntax error * ``` ``` \* ``` OK, node: ```js { type: 'Char', value: '*', symbol: '*', kind: 'simple', codePoint: 42, escaped: true } ``` ##### Meta char A _meta character_ should not be confused with an [escaped char](#escaped-char). Example: ``` \n ``` Node: ```js { type: 'Char', value: '\\n', symbol: '\n', kind: 'meta', codePoint: 10 } ``` Among other meta character are: `.`, `\f`, `\r`, `\n`, `\t`, `\v`, `\0`, `[\b]` (backspace char), `\s`, `\S`, `\w`, `\W`, `\d`, `\D`. > NOTE: Meta characters representing ranges (like `.`, `\s`, etc.) have `undefined` value for `symbol` and `NaN` for `codePoint`. > NOTE: `\b` and `\B` are parsed as `Assertion` node type, not `Char`. ##### Control char A char preceded with `\c`, e.g. `\cx`, which stands for `CTRL+x`: ``` \cx ``` Node: ```js { type: 'Char', value: '\\cx', symbol: undefined, kind: 'control', codePoint: NaN } ``` ##### HEX char-code A char preceded with `\x`, followed by a HEX-code, e.g. `\x3B` (symbol `;`): ``` \x3B ``` Node: ```js { type: 'Char', value: '\\x3B', symbol: ';', kind: 'hex', codePoint: 59 } ``` ##### Decimal char-code Char-code: ``` \42 ``` Node: ```js { type: 'Char', value: '\\42', symbol: '*', kind: 'decimal', codePoint: 42 } ``` ##### Octal char-code Char-code started with `\0`, followed by an octal number: ``` \073 ``` Node: ```js { type: 'Char', value: '\\073', symbol: ';', kind: 'oct', codePoint: 59 } ``` ##### Unicode Unicode char started with `\u`, followed by a hex number: ``` \u003B ``` Node: ```js { type: 'Char', value: '\\u003B', symbol: ';', kind: 'unicode', codePoint: 59 } ``` When using the `u` flag, unicode chars can also be represented using `\u` followed by a hex number between curly braces: ``` \u{1F680} ``` Node: ```js { type: 'Char', value: '\\u{1F680}', symbol: '🚀', kind: 'unicode', codePoint: 128640 } ``` When using the `u` flag, unicode chars can also be represented using a surrogate pair: ``` \ud83d\ude80 ``` Node: ```js { type: 'Char', value: '\\ud83d\\ude80', symbol: '🚀', kind: 'unicode', codePoint: 128640, isSurrogatePair: true } ``` #### Character class Character classes define a _set_ of characters. A set may include as simple characters, as well as _character ranges_. A class can be _positive_ (any from the characters in the class match), or _negative_ (any _but_ the characters from the class match). ##### Positive character class A positive character class is defined between `[` and `]` brackets: ``` [a*] ``` A node: ```js { type: 'CharacterClass', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: '*', symbol: '*', kind: 'simple', codePoint: 42 } ] } ``` > NOTE: some meta symbols are treated as normal characters in a character class. E.g. `*` is not a repetition quantifier, but a simple char. ##### Negative character class A negative character class is defined between `[^` and `]` brackets: ``` [^ab] ``` An AST node is the same, just `negative` property is added: ```js { type: 'CharacterClass', negative: true, expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } ] } ``` ##### Character class ranges As mentioned, a character class may also contain _ranges_ of symbols: ``` [a-z] ``` A node: ```js { type: 'CharacterClass', expressions: [ { type: 'ClassRange', from: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, to: { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 122 } } ] } ``` > NOTE: it is a _syntax error_ if `to` value is less than `from` value: `/[z-a]/`. The range value can be the same for `from` and `to`, and the special range `-` character is treated as a simple character when it stands in a char position: ``` // from: 'a', to: 'a' [a-a] // from: '-', to: '-' [---] // simple '-' char: [-] // 3 ranges: [a-zA-Z0-9]+ ``` #### Unicode properties Unicode property escapes are a new type of escape sequence available in regular expressions that have the `u` flag set. With this feature it is possible to write Unicode expressions as: ```js const greekSymbolRe = /\p{Script=Greek}/u; greekSymbolRe.test('π'); // true ``` The AST node for this expression is: ```js { type: 'UnicodeProperty', name: 'Script', value: 'Greek', negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: 'Greek' } ``` All possible property names, values, and their aliases can be found at the [specification](https://tc39.github.io/ecma262/#sec-runtime-semantics-unicodematchproperty-p). For `General_Category` it is possible to use a shorthand: ```js /\p{Letter}/u; // Shorthand /\p{General_Category=Letter}/u; // Full notation ``` Binary names use the single value as well: ```js /\p{ASCII_Hex_Digit}/u; // Same as: /[0-9A-Fa-f]/ ``` The capitalized `P` defines the negation of the expression: ```js /\P{ASCII_Hex_Digit}/u; // NOT a ASCII Hex digit ``` #### Alternative An _alternative_ (or _concatenation_) defines a chain of patterns followed one after another: ``` abc ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 }, { type: 'Char', value: 'c', symbol: 'c', kind: 'simple', codePoint: 99 } ] } ``` Another examples: ``` // 'a' with a quantifier, followed by 'b' a?b // A group followed by a class: (ab)[a-z] ``` #### Disjunction The _disjunction_ defines "OR" operation for regexp patterns. It's a _binary_ operation, having `left`, and `right` nodes. Matches `a` or `b`: ``` a|b ``` A node: ```js { type: 'Disjunction', left: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, right: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } } ``` #### Groups The groups play two roles: they define _grouping precedence_, and allow to _capture_ needed sub-expressions in case of a capturing group. ##### Capturing group _"Capturing"_ means the matched string can be referred later by a user, including in the pattern itself -- by using [backreferences](#backreferences). Char `a`, and `b` are grouped, followed by the `c` char: ``` (ab)c ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 1, expression: { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } ] } }, { type: 'Char', value: 'c', symbol: 'c', kind: 'simple', codePoint: 99 } ] } ``` As we can see, it also tracks the number of the group. Another example: ``` // A grouped disjunction of a symbol, and a character class: (5|[a-z]) ``` ##### Named capturing group > NOTE: _Named capturing groups_ are not yet supported by JavaScript RegExp. It is an ECMAScript [proposal](https://tc39.github.io/proposal-regexp-named-groups/) which is at stage 3 at the moment. A capturing group can be given a name using the `(?...)` syntax, for any identifier `name`. For example, a regular expressions for a date: ```js /(?\d{4})-(?\d{2})-(?\d{2})/u ``` For the group: ```js (?x) ``` We have the following node (the `name` property with value `foo` is added): ```js { type: 'Group', capturing: true, name: 'foo', nameRaw: 'foo', number: 1, expression: { type: 'Char', value: 'x', symbol: 'x', kind: 'simple', codePoint: 120 } } ``` Note: The `nameRaw` property represents the name *as parsed from the original source*, including escape sequences. The `name` property represents the canonical decoded form of the name. For example, given the `/u` flag and the following group: ```regexp (?<\u{03C0}>x) ``` We would have the following node: ```js { type: 'Group', capturing: true, name: 'π', nameRaw: '\\u{03C0}', number: 1, expression: { type: 'Char', value: 'x', symbol: 'x', kind: 'simple', codePoint: 120 } } ``` ##### Non-capturing group Sometimes we don't need to actually capture the matched string from a group. In this case we can use a _non-capturing_ group: Char `a`, and `b` are grouped, _but not captured_, followed by the `c` char: ``` (?:ab)c ``` The same node, the `capturing` flag is `false`: ```js { type: 'Alternative', expressions: [ { type: 'Group', capturing: false, expression: { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } ] } }, { type: 'Char', value: 'c', symbol: 'c', kind: 'simple', codePoint: 99 } ] } ``` ##### Backreferences A [capturing group](#capturing-group) can be referenced in the pattern using notation of an escaped group number. Matches `abab` string: ``` (ab)\1 ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 1, expression: { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } ] } }, { type: 'Backreference', kind: 'number', number: 1, reference: 1, } ] } ``` A [named capturing group](#named-capturing-group) can be accessed using `\k` pattern, and also using a numbered reference. Matches `www`: ```js (?w)\k\1 ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, name: 'foo', nameRaw: 'foo', number: 1, expression: { type: 'Char', value: 'w', symbol: 'w', kind: 'simple', codePoint: 119 } }, { type: 'Backreference', kind: 'name', number: 1, reference: 'foo', referenceRaw: 'foo' }, { type: 'Backreference', kind: 'number', number: 1, reference: 1 } ] } ``` Note: The `referenceRaw` property represents the reference *as parsed from the original source*, including escape sequences. The `reference` property represents the canonical decoded form of the reference. For example, given the `/u` flag and the following pattern (matches `www`): ```regexp (?<π>w)\k<\u{03C0}>\1 ``` We would have the following node: ```js { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, name: 'π', nameRaw: 'π', number: 1, expression: { type: 'Char', value: 'w', symbol: 'w', kind: 'simple', codePoint: 119 } }, { type: 'Backreference', kind: 'name', number: 1, reference: 'π', referenceRaw: '\\u{03C0}' }, { type: 'Backreference', kind: 'number', number: 1, reference: 1 } ] } ``` #### Quantifiers Quantifiers specify _repetition_ of a regular expression (or of its part). Below are the quantifiers which _wrap_ a parsed expression into a `Repetition` node. The quantifier itself can be of different _kinds_, and has `Quantifier` node type. ##### ? zero-or-one The `?` quantifier is short for `{0,1}`. ``` a? ``` Node: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: '?', greedy: true } } ``` ##### * zero-or-more The `*` quantifier is short for `{0,}`. ``` a* ``` Node: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: '*', greedy: true } } ``` ##### + one-or-more The `+` quantifier is short for `{1,}`. ``` // Same as `aa*`, or `a{1,}` a+ ``` Node: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: '+', greedy: true } } ``` ##### Range-based quantifiers Explicit _range-based_ quantifiers are parsed as follows: ###### Exact number of matches ``` a{3} ``` The type of the quantifier is `Range`, and `from`, and `to` properties have the same value: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: 'Range', from: 3, to: 3, greedy: true } } ``` ###### Open range An open range doesn't have max value (assuming semantic "more", or Infinity value): ``` a{3,} ``` An AST node for such range doesn't contain `to` property: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: 'Range', from: 3, greedy: true } } ``` ###### Closed range A closed range has explicit max value: (which syntactically can be the same as min value): ``` a{3,5} // Same as a{3} a{3,3} ``` An AST node for a closed range: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: 'Range', from: 3, to: 5, greedy: true } } ``` > NOTE: it is a _syntax error_ if the max value is less than min value: `/a{3,2}/` ##### Non-greedy If any quantifier is followed by the `?`, the quantifier becomes _non-greedy_. Example: ``` a+? ``` Node: ```js { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, quantifier: { type: 'Quantifier', kind: '+', greedy: false } } ``` Other examples: ``` a?? a*? a{1}? a{1,}? a{1,3}? ``` #### Assertions Assertions appear as separate AST nodes, however instread of manipulating on the characters themselves, they _assert_ certain conditions of a matching string. Examples: `^` -- beginning of a string (or a line in multiline mode), `$` -- end of a string, etc. ##### ^ begin marker The `^` assertion checks whether a scanner is at the beginning of a string (or a line in multiline mode). In the example below `^` is not a property of the `a` symbol, but a separate AST node for the assertion. The parsed node is actually an `Alternative` with two nodes: ``` ^a ``` The node: ```js { type: 'Alternative', expressions: [ { type: 'Assertion', kind: '^' }, { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 } ] } ``` Since assertion is a separate node, it may appear anywhere in the matching string. The following regexp is completely valid, and asserts beginning of the string; it'll match an empty string: ``` ^^^^^ ``` ##### $ end marker The `$` assertion is similar to `^`, but asserts the end of a string (or a line in a multiline mode): ``` a$ ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Assertion', kind: '$' } ] } ``` And again, this is a completely valid regexp, and matches an empty string: ``` ^^^^$$$$$ // valid too: $^ ``` ##### Boundary assertions The `\b` assertion check for _word boundary_, i.e. the position between a word and a space. Matches `x` in `x y`, but not in `xy`: ``` x\b ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'x', symbol: 'x', kind: 'simple', codePoint: 120 }, { type: 'Assertion', kind: '\\b' } ] } ``` The `\B` is vice-versa checks for _non-word_ boundary. The following example matches `x` in `xy`, but not in `x y`: ``` x\B ``` A node is the same: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'x', symbol: 'x', kind: 'simple', codePoint: 120 }, { type: 'Assertion', kind: '\\B' } ] } ``` ##### Lookahead assertions These assertions check whether a pattern is _followed_ (or not followed for the negative assertion) by another pattern. ###### Positive lookahead assertion Matches `a` only if it's followed by `b`: ``` a(?=b) ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Assertion', kind: 'Lookahead', assertion: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } } ] } ``` ###### Negative lookahead assertion Matches `a` only if it's _not_ followed by `b`: ``` a(?!b) ``` A node is similar, just `negative` flag is added: ```js { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, { type: 'Assertion', kind: 'Lookahead', negative: true, assertion: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 } } ] } ``` ##### Lookbehind assertions > NOTE: _Lookbehind assertions_ are not yet supported by JavaScript RegExp. It is an ECMAScript [proposal](https://tc39.github.io/proposal-regexp-lookbehind/) which is at stage 3 at the moment. These assertions check whether a pattern is _preceded_ (or not preceded for the negative assertion) by another pattern. ###### Positive lookbehind assertion Matches `b` only if it's preceded by `a`: ``` (?<=a)b ``` A node: ```js { type: 'Alternative', expressions: [ { type: 'Assertion', kind: 'Lookbehind', assertion: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 } }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 98 }, ] } ``` ###### Negative lookbehind assertion Matches `b` only if it's _not_ preceded by `a`: ``` (? */ const regexpTree = require('..'); describe('regexp-tree', () => { it('API', () => { // Parser. expect(typeof regexpTree.parser).toBe('object'); expect(typeof regexpTree.parse).toBe('function'); // Traverse. expect(typeof regexpTree.traverse).toBe('function'); // Transform. expect(typeof regexpTree.transform).toBe('function'); // Generator. expect(typeof regexpTree.generate).toBe('function'); // Create RegExp objects. expect(typeof regexpTree.toRegExp).toBe('function'); // Optimizer. expect(typeof regexpTree.optimize).toBe('function'); // Compatibility transpiler. expect(typeof regexpTree.compatTranspile).toBe('function'); // Exec. expect(typeof regexpTree.exec).toBe('function'); // Finite-automaton module. expect(typeof regexpTree.fa).toBe('object'); }); it('operations', () => { const re = '/a/i'; const ast = regexpTree.parse(re); // 1. Parse. expect(ast).toEqual({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0) }, flags: 'i', }); // 2. Traverse. // 2a. With functions. const traverseFunctionsVisited = []; regexpTree.traverse(ast, { RegExp({node}) { traverseFunctionsVisited.push(node.type); expect(node.type).toBe('RegExp'); }, Char({node}) { traverseFunctionsVisited.push(node.type); expect(node.type).toBe('Char'); expect(node.value).toBe('a'); } }); expect(traverseFunctionsVisited).toEqual([ 'RegExp', 'Char', ]); // 2b. With objects. const traverseObjectsVisited = []; regexpTree.traverse(ast, { RegExp: { pre({node}) { traverseObjectsVisited.push(`${node.type}_pre`); expect(node.type).toBe('RegExp'); }, post({node}) { traverseObjectsVisited.push(`${node.type}_post`); expect(node.type).toBe('RegExp'); } }, Char: { pre({node}) { traverseObjectsVisited.push(`${node.type}_pre`); expect(node.type).toBe('Char'); expect(node.value).toBe('a'); }, post({node}) { traverseObjectsVisited.push(`${node.type}_post`); expect(node.type).toBe('Char'); expect(node.value).toBe('a'); } } }); expect(traverseObjectsVisited).toEqual([ 'RegExp_pre', 'Char_pre', 'Char_post', 'RegExp_post', ]); // 3. Generate. expect(regexpTree.generate(ast)).toBe(re); }); it('creates RegExp', () => { const reStr = '/[a-z]/i'; const re = regexpTree.toRegExp(reStr); expect(re).toBeInstanceOf(RegExp); expect(re.toString()).toBe(reStr); expect(re.test('a')).toBe(true); expect(re.test('Z')).toBe(true); }); it('creates complex RegExp', () => { const reStr = `/ # A regular expression for date. (?\\d{4})- # year part of a date (?\\d{2})- # month part of a date (?\\d{2}) # day part of a date /x`; const re = regexpTree.toRegExp(reStr); expect(re).toBeInstanceOf(RegExp); expect(re.test('2017-04-14')).toBe(true); }); it('calls `ToString` in `parse`', () => { const reStr = '/m/m'; const ast = regexpTree.parse({toString: () => reStr}); expect(regexpTree.generate(ast)).toBe(reStr); }); it('optimizer', () => { expect(regexpTree.optimize('/aa*/').toString()).toBe('/a+/'); }); it('compat-transpiler', () => { expect(regexpTree.compatTranspile('/.(?x)/s').toString()) .toBe('/[\\0-\\uFFFF](x)/'); }); it('exec', () => { const re = '/(?\\d{4})-(?\\d{2})-(?\\d{2})/'; const string = '2017-04-14'; const result = regexpTree.exec(re, string); expect(result.groups).toEqual({ year: '2017', month: '04', day: '14', }); }); it('fa', () => { const re = /ab|c*/; const {fa} = regexpTree; expect(fa.test(re, 'ab')).toBe(true); expect(fa.test(re, '')).toBe(true); expect(fa.test(re, 'c')).toBe(true); expect(fa.test(re, 'ccc')).toBe(true); }); }); regexp-tree-0.1.18/bin/000077500000000000000000000000001361507317500146105ustar00rootroot00000000000000regexp-tree-0.1.18/bin/regexp-tree000077500000000000000000000001111361507317500167560ustar00rootroot00000000000000#!/usr/bin/env node 'use strict'; require('../dist/bin/regexp-tree')();regexp-tree-0.1.18/index.d.ts000066400000000000000000000210311361507317500157360ustar00rootroot00000000000000declare module 'regexp-tree/ast' { export interface AstClassMap { 'RegExp': AstRegExp; 'Disjunction': Disjunction; 'Alternative': Alternative; 'Assertion': Assertion; 'Char': Char; 'CharacterClass': CharacterClass; 'ClassRange': ClassRange; 'Backreference': Backreference; 'Group': Group; 'Repetition': Repetition; 'Quantifier': Quantifier; } export type AstClass = keyof AstClassMap; export type AstNode = AstClassMap[AstClass]; export interface Base { type: T; loc?: { source: string; start: number; end: number; }; } export interface SimpleChar extends Base<'Char'> { value: string; kind: 'simple'; escaped?: true; } export interface SpecialChar extends Base<'Char'> { value: string; kind: 'meta' | 'control' | 'hex' | 'decimal' | 'oct' | 'unicode'; } export type Char = | SimpleChar | SpecialChar; export interface ClassRange extends Base<'ClassRange'> { from: Char; to: Char; } export interface CharacterClass extends Base<'CharacterClass'> { negative?: true; expressions: (Char | ClassRange)[]; } export interface Alternative extends Base<'Alternative'> { expressions: Expression[]; } export interface Disjunction extends Base<'Disjunction'> { expressions: (Expression | null)[]; } export interface CapturingGroup extends Base<'Group'> { capturing: true; number: number; name?: string; nameRaw?: string; expression: Expression | null; } export interface NoncapturingGroup extends Base<'Group'> { capturing: false; expression: Expression | null; } export type Group = | CapturingGroup | NoncapturingGroup; export interface NumericBackreference extends Base<'Backreference'> { kind: 'number'; number: number; reference: number; } export interface NamedBackreference extends Base<'Backreference'> { kind: 'name'; number: number; reference: string; referenceRaw: string; } export type Backreference = | NumericBackreference | NamedBackreference; export interface Repetition extends Base<'Repetition'> { expression: Expression; quantifier: Quantifier; } export interface SimpleQuantifier extends Base<'Quantifier'> { kind: '+' | '*' | '?'; greedy: boolean; } export interface RangeQuantifier extends Base<'Quantifier'> { kind: 'Range'; from: number; to?: number; greedy: boolean; } export type Quantifier = | SimpleQuantifier | RangeQuantifier; export interface SimpleAssertion extends Base<'Assertion'> { kind: '^' | '$' | '\\b' | '\\B'; } export interface LookaroundAssertion extends Base<'Assertion'> { kind: 'Lookahead' | 'Lookbehind'; negative?: true; assertion: Expression | null; } export type Assertion = | SimpleAssertion | LookaroundAssertion; export type Expression = | Char | CharacterClass | Alternative | Disjunction | Group | Backreference | Repetition | Assertion; export interface AstRegExp extends Base<'RegExp'> { body: Expression | null; flags: string; } } declare module 'regexp-tree' { import { AstRegExp, AstNode, AstClass, AstClassMap } from 'regexp-tree/ast' export interface ParserOptions { captureLocations?: boolean; } /** * Parses a regexp string, producing an AST. * * @param regexp a regular expression in different formats: string, AST, RegExp. * @param options parsing options for this parse call. */ export function parse(regexp: string | RegExp, options?: ParserOptions): AstRegExp; /** * Generates a RegExp string from an AST. */ export function generate(ast: AstNode | null | undefined): string; /** * Creates a RegExp object from a regexp string. */ export function toRegExp(regexp: string): RegExp; export interface NodePath { node: T; parent: AstNode | null; parentPath: NodePath | null; property: string | null; index: number | null; getParent(): NodePath | null; getChild(n?: number): NodePath | null; getPreviousSibling(): NodePath | null; getNextSibling(): NodePath | null; setChild(node: T | null, index?: number | null, property?: string | null): NodePath | null; appendChild(node: T | null, property?: string | null): NodePath | null; insertChildAt(node: T | null, index: number, property?: string | null): void; replace(node: T): NodePath | null; update(nodeProps: Partial): void; remove(): void; isRemoved(): boolean; hasEqualSource(path: NodePath): boolean; jsonEncode(options?: { format?: string | number, useLoc?: boolean }): string; } export type NodeTraversalCallback = (node: T, parent: NodePath | null, property?: string, index?: number) => void | boolean; export interface NodeTraversalCallbacks { pre?: NodeTraversalCallback; post?: NodeTraversalCallback; } export type SpecificNodeTraversalHandlers = { [P in AstClass]?: NodeTraversalCallback | NodeTraversalCallbacks; }; export interface NodeTraversalHandlers extends SpecificNodeTraversalHandlers { '*'?: NodeTraversalCallback; shouldRun?(ast: T): boolean; init?(ast: T): void; } export type TraversalCallback = (path: NodePath) => void | boolean; export interface TraversalCallbacks { pre?: TraversalCallback; post?: TraversalCallback; } export type SpecificTraversalHandlers = { [P in AstClass]?: TraversalCallback | TraversalCallbacks; }; export interface TraversalHandlers extends SpecificTraversalHandlers { '*'?: TraversalCallback; shouldRun?(ast: T): boolean; init?(ast: T): void; } /** * Traverses a RegExp AST. * * @param handlers Each `handler` is an object containing handler function for needed * node types. 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. * * @example * regexpTree.traverse(ast, { * onChar(node) { * ... * }, * }); */ export function traverse(ast: T, handlers: NodeTraversalHandlers | ReadonlyArray>, options: { asNodes: true }): void; export function traverse(ast: T, handlers: TraversalHandlers | ReadonlyArray>, options?: { asNodes?: false }): void; export type TransformHandlers = TraversalHandlers; export class TransformResult { private _ast; private _source; private _string; private _regexp; private _extra; constructor(ast: T, extra?: E); getAST(): T; setExtra(extra: E): void; getExtra(): E; toRegExp(): RegExp; getSource(): string; getFlags(): string; toString(): string; } /** * 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. */ export function transform(ast: T, handlers: TraversalHandlers | ReadonlyArray>): TransformResult; export function transform(regexp: string | RegExp, handlers: TransformHandlers | ReadonlyArray>): TransformResult; /** * Optimizes a regular expression by replacing some * sub-expressions with their idiomatic patterns. */ export function optimize(ast: T, whitelist?: string[]): TransformResult; export function optimize(regexp: string | RegExp, whitelist?: string[]): TransformResult; /** * Translates a regular expression in new syntax or in new format * into equivalent expressions in old syntax. */ export function compatTranspile(ast: T, whitelist?: string[]): TransformResult; export function compatTranspile(regexp: string | RegExp, whitelist?: string[]): TransformResult; /** * Executes a regular expression on a string. */ export function exec(re: string | RegExp, string: string): RegExpExecArray; } regexp-tree-0.1.18/index.js000066400000000000000000000002541361507317500155060ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; module.exports = require('./dist/regexp-tree');regexp-tree-0.1.18/package-lock.json000066400000000000000000005611111361507317500172610ustar00rootroot00000000000000{ "name": "regexp-tree", "version": "0.1.18", "lockfileVersion": 1, "requires": true, "dependencies": { "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", "dev": true }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true }, "acorn-globals": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "dev": true, "requires": { "acorn": "^4.0.4" } }, "acorn-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { "acorn": "^3.0.4" }, "dependencies": { "acorn": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } } }, "ajv": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=", "dev": true, "requires": { "co": "^4.6.0", "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0" } }, "ajv-keywords": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", "dev": true }, "ansi-escapes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", "dev": true }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", "dev": true }, "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { "micromatch": "^2.1.5", "normalize-path": "^2.0.0" } }, "append-transform": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { "default-require-extensions": "^1.0.0" } }, "argparse": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { "arr-flatten": "^1.0.1" } }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, "array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", "dev": true }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "async": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", "dev": true, "requires": { "lodash": "^4.14.0" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true, "optional": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", "dev": true }, "babel-cli": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", "dev": true, "requires": { "babel-core": "^6.26.0", "babel-polyfill": "^6.26.0", "babel-register": "^6.26.0", "babel-runtime": "^6.26.0", "chokidar": "^1.6.1", "commander": "^2.11.0", "convert-source-map": "^1.5.0", "fs-readdir-recursive": "^1.0.0", "glob": "^7.1.2", "lodash": "^4.17.4", "output-file-sync": "^1.1.2", "path-is-absolute": "^1.0.1", "slash": "^1.0.0", "source-map": "^0.5.6", "v8flags": "^2.1.1" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { "chalk": "^1.1.3", "esutils": "^2.0.2", "js-tokens": "^3.0.2" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "babel-core": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { "babel-code-frame": "^6.26.0", "babel-generator": "^6.26.0", "babel-helpers": "^6.24.1", "babel-messages": "^6.23.0", "babel-register": "^6.26.0", "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.18.0", "convert-source-map": "^1.5.0", "debug": "^2.6.8", "json5": "^0.5.1", "lodash": "^4.17.4", "minimatch": "^3.0.4", "path-is-absolute": "^1.0.1", "private": "^0.1.7", "slash": "^1.0.0", "source-map": "^0.5.6" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-generator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", "dev": true, "requires": { "babel-messages": "^6.23.0", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "detect-indent": "^4.0.0", "jsesc": "^1.3.0", "lodash": "^4.17.4", "source-map": "^0.5.6", "trim-right": "^1.0.1" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-helper-builder-binary-assignment-operator-visitor": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, "requires": { "babel-helper-explode-assignable-expression": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-helper-call-delegate": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-helper-define-map": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-helper-explode-assignable-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-helper-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-helper-get-function-arity": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-helper-hoist-variables": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-helper-optimise-call-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-helper-regex": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-helper-remap-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, "requires": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-helper-replace-supers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { "babel-helper-optimise-call-expression": "^6.24.1", "babel-messages": "^6.23.0", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, "babel-jest": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-19.0.0.tgz", "integrity": "sha1-WTI87ZmjqE01naIZyogQdP/Gzj8=", "dev": true, "requires": { "babel-core": "^6.0.0", "babel-plugin-istanbul": "^4.0.0", "babel-preset-jest": "^19.0.0" } }, "babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-check-es2015-constants": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-istanbul": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz", "integrity": "sha1-Z2DN2Xf0EdPhdbsGTyvDJ9mbK24=", "dev": true, "requires": { "find-up": "^2.1.0", "istanbul-lib-instrument": "^1.7.5", "test-exclude": "^4.1.1" } }, "babel-plugin-jest-hoist": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-19.0.0.tgz", "integrity": "sha1-SuKgTqYSpuc2UfP95SwXiZEwS+o=", "dev": true }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", "dev": true }, "babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "dev": true }, "babel-plugin-syntax-flow": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", "dev": true }, "babel-plugin-syntax-trailing-function-commas": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", "dev": true }, "babel-plugin-transform-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, "requires": { "babel-helper-remap-async-to-generator": "^6.24.1", "babel-plugin-syntax-async-functions": "^6.8.0", "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-arrow-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoping": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-plugin-transform-es2015-classes": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { "babel-helper-define-map": "^6.24.1", "babel-helper-function-name": "^6.24.1", "babel-helper-optimise-call-expression": "^6.24.1", "babel-helper-replace-supers": "^6.24.1", "babel-messages": "^6.23.0", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-computed-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-destructuring": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-for-of": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-modules-amd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-commonjs": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", "dev": true, "requires": { "babel-plugin-transform-strict-mode": "^6.24.1", "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-types": "^6.26.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-umd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { "babel-plugin-transform-es2015-modules-amd": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-object-super": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { "babel-helper-replace-supers": "^6.24.1", "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-parameters": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { "babel-helper-call-delegate": "^6.24.1", "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-shorthand-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-spread": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-sticky-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-template-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-unicode-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "regexpu-core": "^2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, "requires": { "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", "babel-plugin-syntax-exponentiation-operator": "^6.8.0", "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-flow-strip-types": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", "dev": true, "requires": { "babel-plugin-syntax-flow": "^6.18.0", "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-regenerator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { "regenerator-transform": "^0.10.0" } }, "babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { "babel-runtime": "^6.26.0", "core-js": "^2.5.0", "regenerator-runtime": "^0.10.5" }, "dependencies": { "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", "dev": true } } }, "babel-preset-env": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "^6.22.0", "babel-plugin-syntax-trailing-function-commas": "^6.22.0", "babel-plugin-transform-async-to-generator": "^6.22.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", "babel-plugin-transform-es2015-block-scoping": "^6.23.0", "babel-plugin-transform-es2015-classes": "^6.23.0", "babel-plugin-transform-es2015-computed-properties": "^6.22.0", "babel-plugin-transform-es2015-destructuring": "^6.23.0", "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", "babel-plugin-transform-es2015-for-of": "^6.23.0", "babel-plugin-transform-es2015-function-name": "^6.22.0", "babel-plugin-transform-es2015-literals": "^6.22.0", "babel-plugin-transform-es2015-modules-amd": "^6.22.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", "babel-plugin-transform-es2015-modules-umd": "^6.23.0", "babel-plugin-transform-es2015-object-super": "^6.22.0", "babel-plugin-transform-es2015-parameters": "^6.23.0", "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", "babel-plugin-transform-es2015-spread": "^6.22.0", "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", "babel-plugin-transform-es2015-template-literals": "^6.22.0", "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", "babel-plugin-transform-exponentiation-operator": "^6.22.0", "babel-plugin-transform-regenerator": "^6.22.0", "browserslist": "^2.1.2", "invariant": "^2.2.2", "semver": "^5.3.0" } }, "babel-preset-flow": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", "dev": true, "requires": { "babel-plugin-transform-flow-strip-types": "^6.22.0" } }, "babel-preset-jest": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-19.0.0.tgz", "integrity": "sha1-ItZyAdAjJKGVgRKI6zgpS7PKw5Y=", "dev": true, "requires": { "babel-plugin-jest-hoist": "^19.0.0" } }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { "babel-core": "^6.26.0", "babel-runtime": "^6.26.0", "core-js": "^2.5.0", "home-or-tmp": "^2.0.0", "lodash": "^4.17.4", "mkdirp": "^0.5.1", "source-map-support": "^0.4.15" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" } }, "babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { "babel-runtime": "^6.26.0", "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.18.0", "lodash": "^4.17.4" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { "babel-code-frame": "^6.26.0", "babel-messages": "^6.23.0", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.18.0", "debug": "^2.6.8", "globals": "^9.18.0", "invariant": "^2.2.2", "lodash": "^4.17.4" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" }, "dependencies": { "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true } } }, "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, "requires": { "tweetnacl": "^0.14.3" } }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true, "optional": true }, "boom": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { "hoek": "4.x.x" } }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", "repeat-element": "^1.1.2" } }, "browser-resolve": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", "dev": true, "requires": { "resolve": "1.1.7" }, "dependencies": { "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } } }, "browserslist": { "version": "2.10.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.10.2.tgz", "integrity": "sha512-laXHudMWp4ZLgmObmAHZEzkP7Te4lzUyueSZabSuWTImaQznFhFm42SDRmwutfpAOHqDB4fJKGWSZdyrf5TsXg==", "dev": true, "requires": { "caniuse-lite": "^1.0.30000784", "electron-to-chromium": "^1.3.30" } }, "bser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", "dev": true, "requires": { "node-int64": "^0.4.0" } }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { "callsites": "^0.2.0" }, "dependencies": { "callsites": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true } } }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true }, "caniuse-lite": { "version": "1.0.30000784", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000784.tgz", "integrity": "sha1-EpztdOmhKApEGIC2zSvOMO9Z5sA=", "dev": true }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, "chalk": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", "dev": true, "requires": { "ansi-styles": "~1.0.0", "has-color": "~0.1.0", "strip-ansi": "~0.1.0" }, "dependencies": { "strip-ansi": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", "dev": true } } }, "chardet": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "optional": true, "requires": { "anymatch": "^1.3.0", "async-each": "^1.0.0", "fsevents": "^1.0.0", "glob-parent": "^2.0.0", "inherits": "^2.0.1", "is-binary-path": "^1.0.0", "is-glob": "^2.0.0", "path-is-absolute": "^1.0.0", "readdirp": "^2.0.0" } }, "ci-info": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.1.tgz", "integrity": "sha512-vHDDF/bP9RYpTWtUhpJRhCFdvvp3iDWvEbuDbWgvjUrNGV1MXJrE0MPcwGtEled04m61iwdBLUIHZtDgzWS4ZQ==", "dev": true }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { "restore-cursor": "^2.0.0" } }, "cli-table3": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.0.tgz", "integrity": "sha512-c7YHpUyO1SaKaO7kYtxd5NZ8FjAmSK3LpKkuzdwn+2CwpFxBpdoQLm+OAnnCfoEl7onKhN9PKQi1lsHuAIUqGQ==", "dev": true, "requires": { "colors": "^1.1.2", "object-assign": "^4.1.0", "string-width": "^2.1.1" }, "dependencies": { "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" } } } }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { "color-name": "^1.1.1" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, "combined-stream": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, "content-type-parser": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", "dev": true }, "convert-source-map": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", "dev": true }, "core-js": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", "dev": true }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "cryptiles": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { "boom": "5.x.x" }, "dependencies": { "boom": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "dev": true, "requires": { "hoek": "4.x.x" } } } }, "cssom": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", "dev": true }, "cssstyle": { "version": "0.2.37", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", "dev": true, "requires": { "cssom": "0.3.x" } }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" } }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, "default-require-extensions": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { "strip-bom": "^2.0.0" } }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { "repeating": "^2.0.0" } }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { "esutils": "^2.0.2" } }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, "requires": { "jsbn": "~0.1.0" } }, "electron-releases": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/electron-releases/-/electron-releases-2.1.0.tgz", "integrity": "sha512-cyKFD1bTE/UgULXfaueIN1k5EPFzs+FRc/rvCY5tIynefAPqopQEgjr0EzY+U3Dqrk/G4m9tXSPuZ77v6dL/Rw==", "dev": true }, "electron-to-chromium": { "version": "1.3.30", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz", "integrity": "sha512-zx1Prv7kYLfc4OA60FhxGbSo4qrEjgSzpo1/37i7l9ltXPYOoQBtjQxY9KmsgfHnBxHlBGXwLlsbt/gub1w5lw==", "dev": true, "requires": { "electron-releases": "^2.1.0" } }, "errno": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", "dev": true, "requires": { "prr": "~0.0.0" } }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { "is-arrayish": "^0.2.1" } }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "escodegen": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz", "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", "dev": true, "requires": { "esprima": "^3.1.3", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.5.6" }, "dependencies": { "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true } } }, "eslint": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz", "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==", "dev": true, "requires": { "ajv": "^5.3.0", "babel-code-frame": "^6.22.0", "chalk": "^2.1.0", "concat-stream": "^1.6.0", "cross-spawn": "^5.1.0", "debug": "^3.1.0", "doctrine": "^2.1.0", "eslint-scope": "^3.7.1", "eslint-visitor-keys": "^1.0.0", "espree": "^3.5.2", "esquery": "^1.0.0", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^11.0.1", "ignore": "^3.3.3", "imurmurhash": "^0.1.4", "inquirer": "^3.0.6", "is-resolvable": "^1.0.0", "js-yaml": "^3.9.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.4", "minimatch": "^3.0.2", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", "pluralize": "^7.0.0", "progress": "^2.0.0", "require-uncached": "^1.0.3", "semver": "^5.3.0", "strip-ansi": "^4.0.0", "strip-json-comments": "~2.0.1", "table": "4.0.2", "text-table": "~0.2.0" }, "dependencies": { "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { "ms": "^2.1.1" } }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" } }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "eslint-scope": { "version": "3.7.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", "dev": true, "requires": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" } }, "eslint-visitor-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "dev": true }, "espree": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { "acorn": "^5.5.0", "acorn-jsx": "^3.0.0" }, "dependencies": { "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true } } }, "esprima": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", "dev": true }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { "estraverse": "^4.0.0" } }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { "estraverse": "^4.1.0" } }, "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, "exec-sh": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.1.tgz", "integrity": "sha512-aLt95pexaugVtQerpmE51+4QfWrNc304uez7jvj6fWnN8GeEHpttB8F36n8N7uVhUMbH/1enbxQ9HImZ4w/9qg==", "dev": true, "requires": { "merge": "^1.1.3" } }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { "is-posix-bracket": "^0.1.0" } }, "expand-range": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { "fill-range": "^2.1.0" } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "external-editor": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { "chardet": "^0.4.0", "iconv-lite": "^0.4.17", "tmp": "^0.0.33" } }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { "is-extglob": "^1.0.0" } }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "fb-watchman": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", "dev": true, "requires": { "bser": "^2.0.0" } }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { "flat-cache": "^1.2.1", "object-assign": "^4.0.1" } }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", "dev": true }, "fileset": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { "glob": "^7.0.3", "minimatch": "^3.0.3" } }, "fill-range": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { "is-number": "^2.1.0", "isobject": "^2.0.0", "randomatic": "^1.1.3", "repeat-element": "^1.1.2", "repeat-string": "^1.5.2" } }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { "locate-path": "^2.0.0" } }, "flat-cache": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { "circular-json": "^0.3.1", "graceful-fs": "^4.1.2", "rimraf": "~2.6.2", "write": "^0.2.1" } }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, "for-own": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { "for-in": "^1.0.1" } }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, "form-data": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.5", "mime-types": "^2.1.12" } }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "fsevents": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", "dev": true, "optional": true, "requires": { "nan": "^2.3.0", "node-pre-gyp": "^0.6.39" }, "dependencies": { "abbrev": { "version": "1.1.0", "bundled": true, "dev": true, "optional": true }, "ajv": { "version": "4.11.8", "bundled": true, "dev": true, "optional": true, "requires": { "co": "^4.6.0", "json-stable-stringify": "^1.0.1" } }, "ansi-regex": { "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "aproba": { "version": "1.1.1", "bundled": true, "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", "bundled": true, "dev": true, "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" } }, "asn1": { "version": "0.2.3", "bundled": true }, "assert-plus": { "version": "0.2.0", "bundled": true, "dev": true, "optional": true }, "asynckit": { "version": "0.4.0", "bundled": true, "dev": true, "optional": true }, "aws-sign2": { "version": "0.6.0", "bundled": true, "dev": true, "optional": true }, "aws4": { "version": "1.6.0", "bundled": true, "dev": true, "optional": true }, "balanced-match": { "version": "0.4.2", "bundled": true, "dev": true, "optional": true }, "bcrypt-pbkdf": { "version": "1.0.1", "bundled": true, "requires": { "tweetnacl": "^0.14.3" } }, "block-stream": { "version": "0.0.9", "bundled": true, "dev": true, "optional": true, "requires": { "inherits": "~2.0.0" } }, "boom": { "version": "2.10.1", "bundled": true, "dev": true, "optional": true, "requires": { "hoek": "2.x.x" } }, "brace-expansion": { "version": "1.1.7", "bundled": true, "dev": true, "optional": true, "requires": { "balanced-match": "^0.4.1", "concat-map": "0.0.1" } }, "buffer-shims": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "caseless": { "version": "0.12.0", "bundled": true, "dev": true, "optional": true }, "co": { "version": "4.6.0", "bundled": true, "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", "bundled": true, "dev": true, "optional": true }, "combined-stream": { "version": "1.0.5", "bundled": true, "dev": true, "optional": true, "requires": { "delayed-stream": "~1.0.0" } }, "concat-map": { "version": "0.0.1", "bundled": true, "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "cryptiles": { "version": "2.0.5", "bundled": true, "dev": true, "optional": true, "requires": { "boom": "2.x.x" } }, "dashdash": { "version": "1.14.1", "bundled": true, "requires": { "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { "version": "1.0.0", "bundled": true } } }, "debug": { "version": "2.6.8", "bundled": true, "dev": true, "optional": true, "requires": { "ms": "2.0.0" } }, "deep-extend": { "version": "0.4.2", "bundled": true, "dev": true, "optional": true }, "delayed-stream": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "delegates": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "detect-libc": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "ecc-jsbn": { "version": "0.1.1", "bundled": true, "requires": { "jsbn": "~0.1.0" } }, "extsprintf": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "forever-agent": { "version": "0.6.1", "bundled": true, "dev": true, "optional": true }, "form-data": { "version": "2.1.4", "bundled": true, "dev": true, "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.5", "mime-types": "^2.1.12" } }, "fs.realpath": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "fstream": { "version": "1.0.11", "bundled": true, "dev": true, "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" } }, "fstream-ignore": { "version": "1.0.5", "bundled": true, "dev": true, "optional": true, "requires": { "fstream": "^1.0.0", "inherits": "2", "minimatch": "^3.0.0" } }, "gauge": { "version": "2.7.4", "bundled": true, "dev": true, "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", "has-unicode": "^2.0.0", "object-assign": "^4.1.0", "signal-exit": "^3.0.0", "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" } }, "getpass": { "version": "0.1.7", "bundled": true, "requires": { "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { "version": "1.0.0", "bundled": true } } }, "glob": { "version": "7.1.2", "bundled": true, "dev": true, "optional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "graceful-fs": { "version": "4.1.11", "bundled": true, "dev": true, "optional": true }, "har-schema": { "version": "1.0.5", "bundled": true, "dev": true, "optional": true }, "har-validator": { "version": "4.2.1", "bundled": true, "dev": true, "optional": true, "requires": { "ajv": "^4.9.1", "har-schema": "^1.0.5" } }, "has-unicode": { "version": "2.0.1", "bundled": true, "dev": true, "optional": true }, "hawk": { "version": "3.1.3", "bundled": true, "dev": true, "optional": true, "requires": { "boom": "2.x.x", "cryptiles": "2.x.x", "hoek": "2.x.x", "sntp": "1.x.x" } }, "hoek": { "version": "2.16.3", "bundled": true, "dev": true, "optional": true }, "http-signature": { "version": "1.1.1", "bundled": true, "dev": true, "optional": true, "requires": { "assert-plus": "^0.2.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, "inflight": { "version": "1.0.6", "bundled": true, "dev": true, "optional": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, "dev": true, "optional": true }, "ini": { "version": "1.3.4", "bundled": true, "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "is-typedarray": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "isarray": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true }, "isstream": { "version": "0.1.2", "bundled": true, "dev": true, "optional": true }, "jodid25519": { "version": "1.0.2", "bundled": true, "requires": { "jsbn": "~0.1.0" } }, "jsbn": { "version": "0.1.1", "bundled": true }, "json-schema": { "version": "0.2.3", "bundled": true, "dev": true, "optional": true }, "json-stable-stringify": { "version": "1.0.1", "bundled": true, "dev": true, "optional": true, "requires": { "jsonify": "~0.0.0" } }, "json-stringify-safe": { "version": "5.0.1", "bundled": true, "dev": true, "optional": true }, "jsonify": { "version": "0.0.0", "bundled": true, "dev": true, "optional": true }, "jsprim": { "version": "1.4.0", "bundled": true, "dev": true, "optional": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.0.2", "json-schema": "0.2.3", "verror": "1.3.6" }, "dependencies": { "assert-plus": { "version": "1.0.0", "bundled": true, "dev": true, "optional": true } } }, "mime-db": { "version": "1.27.0", "bundled": true, "dev": true, "optional": true }, "mime-types": { "version": "2.1.15", "bundled": true, "dev": true, "optional": true, "requires": { "mime-db": "~1.27.0" } }, "minimatch": { "version": "3.0.4", "bundled": true, "dev": true, "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, "dev": true, "optional": true }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", "bundled": true, "dev": true, "optional": true }, "node-pre-gyp": { "version": "0.6.39", "bundled": true, "dev": true, "optional": true, "requires": { "detect-libc": "^1.0.2", "hawk": "3.1.3", "mkdirp": "^0.5.1", "nopt": "^4.0.1", "npmlog": "^4.0.2", "rc": "^1.1.7", "request": "2.81.0", "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^2.2.1", "tar-pack": "^3.4.0" } }, "nopt": { "version": "4.0.1", "bundled": true, "dev": true, "optional": true, "requires": { "abbrev": "1", "osenv": "^0.1.4" } }, "npmlog": { "version": "4.1.0", "bundled": true, "dev": true, "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", "gauge": "~2.7.3", "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, "dev": true, "optional": true }, "oauth-sign": { "version": "0.8.2", "bundled": true, "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", "bundled": true, "dev": true, "optional": true }, "once": { "version": "1.4.0", "bundled": true, "dev": true, "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "osenv": { "version": "0.1.4", "bundled": true, "dev": true, "optional": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { "version": "1.0.1", "bundled": true, "dev": true, "optional": true }, "performance-now": { "version": "0.2.0", "bundled": true, "dev": true, "optional": true }, "process-nextick-args": { "version": "1.0.7", "bundled": true, "dev": true, "optional": true }, "punycode": { "version": "1.4.1", "bundled": true, "dev": true, "optional": true }, "qs": { "version": "6.4.0", "bundled": true, "dev": true, "optional": true }, "rc": { "version": "1.2.1", "bundled": true, "dev": true, "optional": true, "requires": { "deep-extend": "~0.4.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { "version": "1.2.0", "bundled": true, "dev": true, "optional": true } } }, "readable-stream": { "version": "2.2.9", "bundled": true, "dev": true, "optional": true, "requires": { "buffer-shims": "~1.0.0", "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "~1.0.0", "process-nextick-args": "~1.0.6", "string_decoder": "~1.0.0", "util-deprecate": "~1.0.1" } }, "request": { "version": "2.81.0", "bundled": true, "dev": true, "optional": true, "requires": { "aws-sign2": "~0.6.0", "aws4": "^1.2.1", "caseless": "~0.12.0", "combined-stream": "~1.0.5", "extend": "~3.0.0", "forever-agent": "~0.6.1", "form-data": "~2.1.1", "har-validator": "~4.2.1", "hawk": "~3.1.3", "http-signature": "~1.1.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.7", "oauth-sign": "~0.8.1", "performance-now": "^0.2.0", "qs": "~6.4.0", "safe-buffer": "^5.0.1", "stringstream": "~0.0.4", "tough-cookie": "~2.3.0", "tunnel-agent": "^0.6.0", "uuid": "^3.0.0" } }, "rimraf": { "version": "2.6.1", "bundled": true, "dev": true, "optional": true, "requires": { "glob": "^7.0.5" } }, "safe-buffer": { "version": "5.0.1", "bundled": true, "dev": true, "optional": true }, "semver": { "version": "5.3.0", "bundled": true, "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", "bundled": true, "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", "bundled": true, "dev": true, "optional": true }, "sntp": { "version": "1.0.9", "bundled": true, "dev": true, "optional": true, "requires": { "hoek": "2.x.x" } }, "string-width": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "string_decoder": { "version": "1.0.1", "bundled": true, "dev": true, "optional": true, "requires": { "safe-buffer": "^5.0.1" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", "bundled": true, "dev": true, "optional": true }, "tar": { "version": "2.2.1", "bundled": true, "dev": true, "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.2", "inherits": "2" } }, "tar-pack": { "version": "3.4.0", "bundled": true, "dev": true, "optional": true, "requires": { "debug": "^2.2.0", "fstream": "^1.0.10", "fstream-ignore": "^1.0.5", "once": "^1.3.3", "readable-stream": "^2.1.4", "rimraf": "^2.5.1", "tar": "^2.2.1", "uid-number": "^0.0.6" } }, "tough-cookie": { "version": "2.3.2", "bundled": true, "dev": true, "optional": true, "requires": { "punycode": "^1.4.1" } }, "tunnel-agent": { "version": "0.6.0", "bundled": true, "dev": true, "optional": true, "requires": { "safe-buffer": "^5.0.1" } }, "tweetnacl": { "version": "0.14.5", "bundled": true }, "uid-number": { "version": "0.0.6", "bundled": true, "dev": true, "optional": true }, "util-deprecate": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true }, "uuid": { "version": "3.0.1", "bundled": true, "dev": true, "optional": true }, "verror": { "version": "1.3.6", "bundled": true, "dev": true, "optional": true, "requires": { "extsprintf": "1.0.2" } }, "wide-align": { "version": "1.1.2", "bundled": true, "dev": true, "optional": true, "requires": { "string-width": "^1.0.2" } }, "wrappy": { "version": "1.0.2", "bundled": true, "dev": true, "optional": true } } }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-base": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { "glob-parent": "^2.0.0", "is-glob": "^2.0.0" } }, "glob-parent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { "is-glob": "^2.0.0" } }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, "handlebars": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", "dev": true, "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" }, "dependencies": { "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "optional": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "uglify-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.5.tgz", "integrity": "sha512-7L3W+Npia1OCr5Blp4/Vw83tK1mu5gnoIURtT1fUVfQ3Kf8WStWV6NJz0fdoBJZls0KlweruRTLVe6XLafmy5g==", "dev": true, "optional": true, "requires": { "commander": "~2.20.3", "source-map": "~0.6.1" } } } }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { "ajv": "^5.1.0", "har-schema": "^2.0.0" } }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { "ansi-regex": "^2.0.0" } }, "has-color": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", "dev": true }, "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "hawk": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { "boom": "4.x.x", "cryptiles": "3.x.x", "hoek": "4.x.x", "sntp": "2.x.x" } }, "hoek": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", "dev": true }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.1" } }, "hosted-git-info": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "dev": true, "requires": { "whatwg-encoding": "^1.0.1" } }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", "dev": true }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "inquirer": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", "external-editor": "^2.0.4", "figures": "^2.0.0", "lodash": "^4.3.0", "mute-stream": "0.0.7", "run-async": "^2.2.0", "rx-lite": "^4.0.8", "rx-lite-aggregates": "^4.0.8", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" }, "dependencies": { "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" } }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, "invariant": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { "loose-envify": "^1.0.0" } }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "optional": true, "requires": { "binary-extensions": "^1.0.0" } }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { "builtin-modules": "^1.0.0" } }, "is-ci": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", "dev": true, "requires": { "ci-info": "^1.0.0" } }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", "dev": true }, "is-equal-shallow": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { "is-primitive": "^2.0.0" } }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, "is-finite": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" } }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { "is-extglob": "^1.0.0" } }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { "kind-of": "^3.0.2" } }, "is-posix-bracket": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", "dev": true }, "is-primitive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { "isarray": "1.0.0" } }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, "istanbul-api": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.2.1.tgz", "integrity": "sha512-oFCwXvd65amgaPCzqrR+a2XjanS1MvpXN6l/MlMUTv6uiA1NOgGX+I0uyq8Lg3GDxsxPsaP1049krz3hIJ5+KA==", "dev": true, "requires": { "async": "^2.1.4", "fileset": "^2.0.2", "istanbul-lib-coverage": "^1.1.1", "istanbul-lib-hook": "^1.1.0", "istanbul-lib-instrument": "^1.9.1", "istanbul-lib-report": "^1.1.2", "istanbul-lib-source-maps": "^1.2.2", "istanbul-reports": "^1.1.3", "js-yaml": "^3.7.0", "mkdirp": "^0.5.1", "once": "^1.4.0" } }, "istanbul-lib-coverage": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==", "dev": true }, "istanbul-lib-hook": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { "append-transform": "^0.4.0" } }, "istanbul-lib-instrument": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz", "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", "dev": true, "requires": { "babel-generator": "^6.18.0", "babel-template": "^6.16.0", "babel-traverse": "^6.18.0", "babel-types": "^6.18.0", "babylon": "^6.18.0", "istanbul-lib-coverage": "^1.1.1", "semver": "^5.3.0" } }, "istanbul-lib-report": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz", "integrity": "sha512-UTv4VGx+HZivJQwAo1wnRwe1KTvFpfi/NYwN7DcsrdzMXwpRT/Yb6r4SBPoHWj4VuQPakR32g4PUUeyKkdDkBA==", "dev": true, "requires": { "istanbul-lib-coverage": "^1.1.1", "mkdirp": "^0.5.1", "path-parse": "^1.0.5", "supports-color": "^3.1.2" }, "dependencies": { "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { "has-flag": "^1.0.0" } } } }, "istanbul-lib-source-maps": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.2.tgz", "integrity": "sha512-8BfdqSfEdtip7/wo1RnrvLpHVEd8zMZEDmOFEnpC6dg0vXflHt9nvoAyQUzig2uMSXfF2OBEYBV3CVjIL9JvaQ==", "dev": true, "requires": { "debug": "^3.1.0", "istanbul-lib-coverage": "^1.1.1", "mkdirp": "^0.5.1", "rimraf": "^2.6.1", "source-map": "^0.5.3" }, "dependencies": { "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" } } } }, "istanbul-reports": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.3.tgz", "integrity": "sha512-ZEelkHh8hrZNI5xDaKwPMFwDsUf5wIEI2bXAFGp1e6deR2mnEKBPhLJEgr4ZBt8Gi6Mj38E/C8kcy9XLggVO2Q==", "dev": true, "requires": { "handlebars": "^4.0.3" } }, "jest-changed-files": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-19.0.2.tgz", "integrity": "sha1-FsVMhMMnC+QI4G0uivPz43qIWCQ=", "dev": true }, "jest-cli": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-19.0.2.tgz", "integrity": "sha1-zDYgtirKxfLZOlSMtu9pfU7IVEM=", "dev": true, "requires": { "ansi-escapes": "^1.4.0", "callsites": "^2.0.0", "chalk": "^1.1.1", "graceful-fs": "^4.1.6", "is-ci": "^1.0.9", "istanbul-api": "^1.1.0-alpha.1", "istanbul-lib-coverage": "^1.0.0", "istanbul-lib-instrument": "^1.1.1", "jest-changed-files": "^19.0.2", "jest-config": "^19.0.2", "jest-environment-jsdom": "^19.0.2", "jest-haste-map": "^19.0.0", "jest-jasmine2": "^19.0.2", "jest-message-util": "^19.0.0", "jest-regex-util": "^19.0.0", "jest-resolve-dependencies": "^19.0.0", "jest-runtime": "^19.0.2", "jest-snapshot": "^19.0.2", "jest-util": "^19.0.2", "micromatch": "^2.3.11", "node-notifier": "^5.0.1", "slash": "^1.0.0", "string-length": "^1.0.1", "throat": "^3.0.0", "which": "^1.1.1", "worker-farm": "^1.3.1", "yargs": "^6.3.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wrap-ansi": "^2.0.0" } }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { "lcid": "^1.0.0" } }, "which-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, "yargs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true, "requires": { "camelcase": "^3.0.0", "cliui": "^3.2.0", "decamelize": "^1.1.1", "get-caller-file": "^1.0.1", "os-locale": "^1.4.0", "read-pkg-up": "^1.0.1", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^1.0.2", "which-module": "^1.0.0", "y18n": "^3.2.1", "yargs-parser": "^4.2.0" } }, "yargs-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "requires": { "camelcase": "^3.0.0" } } } }, "jest-config": { "version": "19.0.4", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-19.0.4.tgz", "integrity": "sha1-QpgCEdRkF+kcp6v/0IbCcCNPc/0=", "dev": true, "requires": { "chalk": "^1.1.1", "jest-environment-jsdom": "^19.0.2", "jest-environment-node": "^19.0.2", "jest-jasmine2": "^19.0.2", "jest-regex-util": "^19.0.0", "jest-resolve": "^19.0.2", "jest-validate": "^19.0.2", "pretty-format": "^19.0.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-diff": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-19.0.0.tgz", "integrity": "sha1-0VY8/FbItgIymI+8BdTRbtkPBjw=", "dev": true, "requires": { "chalk": "^1.1.3", "diff": "^3.0.0", "jest-matcher-utils": "^19.0.0", "pretty-format": "^19.0.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-environment-jsdom": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-19.0.2.tgz", "integrity": "sha1-ztqFnEpLlKs15N59q1S5JvKT5KM=", "dev": true, "requires": { "jest-mock": "^19.0.0", "jest-util": "^19.0.2", "jsdom": "^9.11.0" } }, "jest-environment-node": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-19.0.2.tgz", "integrity": "sha1-boQHnbh+0h0MBeH5Zp8gexFv6Zs=", "dev": true, "requires": { "jest-mock": "^19.0.0", "jest-util": "^19.0.2" } }, "jest-file-exists": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-file-exists/-/jest-file-exists-19.0.0.tgz", "integrity": "sha1-zKLlh6EeyS4kz+qz+KlNZX8/zrg=", "dev": true }, "jest-haste-map": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-19.0.2.tgz", "integrity": "sha1-KGSEw6Fuhtp4crCHfDXc4ww9bwc=", "dev": true, "requires": { "fb-watchman": "^2.0.0", "graceful-fs": "^4.1.6", "micromatch": "^2.3.11", "sane": "~1.5.0", "worker-farm": "^1.3.1" } }, "jest-jasmine2": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-19.0.2.tgz", "integrity": "sha1-FnmRrIJZgfsagArxJug6/MqDLHM=", "dev": true, "requires": { "graceful-fs": "^4.1.6", "jest-matcher-utils": "^19.0.0", "jest-matchers": "^19.0.0", "jest-message-util": "^19.0.0", "jest-snapshot": "^19.0.2" } }, "jest-matcher-utils": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-19.0.0.tgz", "integrity": "sha1-Xs2bY1ZdKwAfYfv37Ex/U3lkVk0=", "dev": true, "requires": { "chalk": "^1.1.3", "pretty-format": "^19.0.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-matchers": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-matchers/-/jest-matchers-19.0.0.tgz", "integrity": "sha1-x07Mbr/sBvOEdnuk1vpKQtZ1V1Q=", "dev": true, "requires": { "jest-diff": "^19.0.0", "jest-matcher-utils": "^19.0.0", "jest-message-util": "^19.0.0", "jest-regex-util": "^19.0.0" } }, "jest-message-util": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-19.0.0.tgz", "integrity": "sha1-cheWuJwOTXYWBvm6jLgoo7YkZBY=", "dev": true, "requires": { "chalk": "^1.1.1", "micromatch": "^2.3.11" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-mock": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-19.0.0.tgz", "integrity": "sha1-ZwOGQelgerLOCOxKjLg6q7yJnQE=", "dev": true }, "jest-regex-util": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-19.0.0.tgz", "integrity": "sha1-t3VFhxEq7eFFZRC7H2r+dO9ZhpE=", "dev": true }, "jest-resolve": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-19.0.2.tgz", "integrity": "sha1-V5NXXeTweuwy99f/DGwYGWPu+zw=", "dev": true, "requires": { "browser-resolve": "^1.11.2", "jest-haste-map": "^19.0.0", "resolve": "^1.2.0" } }, "jest-resolve-dependencies": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-19.0.0.tgz", "integrity": "sha1-p0GtH6CUFA5k7PJkKlBPg07OIu4=", "dev": true, "requires": { "jest-file-exists": "^19.0.0" } }, "jest-runtime": { "version": "19.0.4", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-19.0.4.tgz", "integrity": "sha1-8WfZ8TR3UvICc2EGeSZIU0n8wkU=", "dev": true, "requires": { "babel-core": "^6.0.0", "babel-jest": "^19.0.0", "babel-plugin-istanbul": "^4.0.0", "chalk": "^1.1.3", "graceful-fs": "^4.1.6", "jest-config": "^19.0.2", "jest-file-exists": "^19.0.0", "jest-haste-map": "^19.0.0", "jest-regex-util": "^19.0.0", "jest-resolve": "^19.0.2", "jest-util": "^19.0.2", "json-stable-stringify": "^1.0.1", "micromatch": "^2.3.11", "strip-bom": "3.0.0", "yargs": "^6.3.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wrap-ansi": "^2.0.0" } }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { "lcid": "^1.0.0" } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "which-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, "yargs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true, "requires": { "camelcase": "^3.0.0", "cliui": "^3.2.0", "decamelize": "^1.1.1", "get-caller-file": "^1.0.1", "os-locale": "^1.4.0", "read-pkg-up": "^1.0.1", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^1.0.2", "which-module": "^1.0.0", "y18n": "^3.2.1", "yargs-parser": "^4.2.0" } }, "yargs-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "requires": { "camelcase": "^3.0.0" } } } }, "jest-snapshot": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-19.0.2.tgz", "integrity": "sha1-nBshYhT3GHw4v9XHCx76sWsP9Qs=", "dev": true, "requires": { "chalk": "^1.1.3", "jest-diff": "^19.0.0", "jest-file-exists": "^19.0.0", "jest-matcher-utils": "^19.0.0", "jest-util": "^19.0.2", "natural-compare": "^1.4.0", "pretty-format": "^19.0.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-util": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-19.0.2.tgz", "integrity": "sha1-4KAjKiq55rK1Nmi9s1NMK1l37UE=", "dev": true, "requires": { "chalk": "^1.1.1", "graceful-fs": "^4.1.6", "jest-file-exists": "^19.0.0", "jest-message-util": "^19.0.0", "jest-mock": "^19.0.0", "jest-validate": "^19.0.2", "leven": "^2.0.0", "mkdirp": "^0.5.1" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "jest-validate": { "version": "19.0.2", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-19.0.2.tgz", "integrity": "sha1-3FNN9fEnjVtj3zKxQkHU2/ckTAw=", "dev": true, "requires": { "chalk": "^1.1.1", "jest-matcher-utils": "^19.0.0", "leven": "^2.0.0", "pretty-format": "^19.0.0" }, "dependencies": { "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } } } }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, "jsdom": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", "integrity": "sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=", "dev": true, "requires": { "abab": "^1.0.3", "acorn": "^4.0.4", "acorn-globals": "^3.1.0", "array-equal": "^1.0.0", "content-type-parser": "^1.0.1", "cssom": ">= 0.3.2 < 0.4.0", "cssstyle": ">= 0.2.37 < 0.3.0", "escodegen": "^1.6.1", "html-encoding-sniffer": "^1.0.1", "nwmatcher": ">= 1.3.9 < 2.0.0", "parse5": "^1.5.1", "request": "^2.79.0", "sax": "^1.2.1", "symbol-tree": "^3.2.1", "tough-cookie": "^2.3.2", "webidl-conversions": "^4.0.0", "whatwg-encoding": "^1.0.1", "whatwg-url": "^4.3.0", "xml-name-validator": "^2.0.1" } }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, "json-stable-stringify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { "jsonify": "~0.0.0" } }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" } }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" } }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { "invert-kv": "^1.0.0" } }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", "dev": true }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "strip-bom": "^2.0.0" } }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { "js-tokens": "^3.0.0" } }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", "dev": true, "requires": { "tmpl": "1.0.x" } }, "merge": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", "braces": "^1.8.2", "expand-brackets": "^0.1.4", "extglob": "^0.3.1", "filename-regex": "^2.0.0", "is-extglob": "^1.0.0", "is-glob": "^2.0.1", "kind-of": "^3.0.2", "normalize-path": "^2.0.1", "object.omit": "^2.0.0", "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } }, "mime-db": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", "dev": true }, "mime-types": { "version": "2.1.17", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "dev": true, "requires": { "mime-db": "~1.30.0" } }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "nan": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", "dev": true, "optional": true }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", "dev": true }, "node-notifier": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.1.2.tgz", "integrity": "sha1-L6nhJgX6EACdRFSdb82KY93g5P8=", "dev": true, "requires": { "growly": "^1.3.0", "semver": "^5.3.0", "shellwords": "^0.1.0", "which": "^1.2.12" } }, "nomnom": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", "dev": true, "requires": { "chalk": "~0.4.0", "underscore": "~1.6.0" } }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", "is-builtin-module": "^1.0.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { "remove-trailing-separator": "^1.0.1" } }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "nwmatcher": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", "dev": true }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { "for-own": "^0.1.4", "is-extendable": "^0.1.1" } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" } }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { "mimic-fn": "^1.0.0" } }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" } }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", "wordwrap": "~1.0.0" }, "dependencies": { "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true } } }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "output-file-sync": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "dev": true, "requires": { "graceful-fs": "^4.1.4", "mkdirp": "^0.5.1", "object-assign": "^4.1.0" } }, "p-limit": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", "dev": true }, "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { "p-limit": "^1.1.0" } }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { "glob-base": "^0.3.0", "is-dotfile": "^1.0.0", "is-extglob": "^1.0.0", "is-glob": "^2.0.0" } }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { "error-ex": "^1.2.0" } }, "parse5": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", "dev": true }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, "path-parse": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", "pinkie-promise": "^2.0.0" } }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { "pinkie": "^2.0.0" } }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, "prettier": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz", "integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==", "dev": true }, "pretty-format": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-19.0.0.tgz", "integrity": "sha1-VlMNMqy5ij+khRxOK503tCBoTIQ=", "dev": true, "requires": { "ansi-styles": "^3.0.0" }, "dependencies": { "ansi-styles": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { "color-convert": "^1.9.0" } } } }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "prr": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", "dev": true }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", "dev": true }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" }, "dependencies": { "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" } } } }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { "is-buffer": "^1.1.5" } } } }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", "path-type": "^1.0.0" } }, "read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" }, "dependencies": { "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" } }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { "pinkie-promise": "^2.0.0" } } } }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~1.0.6", "safe-buffer": "~5.1.1", "string_decoder": "~1.0.3", "util-deprecate": "~1.0.1" } }, "readdirp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "optional": true, "requires": { "graceful-fs": "^4.1.2", "minimatch": "^3.0.2", "readable-stream": "^2.0.2", "set-immediate-shim": "^1.0.1" } }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { "resolve": "^1.1.6" } }, "regenerate": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", "dev": true }, "regenerator-runtime": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", "dev": true }, "regenerator-transform": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { "babel-runtime": "^6.18.0", "babel-types": "^6.19.0", "private": "^0.1.6" } }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" } }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { "regenerate": "^1.2.1", "regjsgen": "^0.2.0", "regjsparser": "^0.1.4" } }, "regjsgen": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, "regjsparser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { "jsesc": "~0.5.0" }, "dependencies": { "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } } }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { "is-finite": "^1.0.0" } }, "request": { "version": "2.83.0", "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.6.0", "caseless": "~0.12.0", "combined-stream": "~1.0.5", "extend": "~3.0.1", "forever-agent": "~0.6.1", "form-data": "~2.3.1", "har-validator": "~5.0.3", "hawk": "~6.0.2", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.17", "oauth-sign": "~0.8.2", "performance-now": "^2.1.0", "qs": "~6.5.1", "safe-buffer": "^5.1.1", "stringstream": "~0.0.5", "tough-cookie": "~2.3.3", "tunnel-agent": "^0.6.0", "uuid": "^3.1.0" } }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" } }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "dev": true, "requires": { "path-parse": "^1.0.5" } }, "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "^7.0.5" } }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { "is-promise": "^2.1.0" } }, "rx-lite": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", "dev": true }, "rx-lite-aggregates": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", "dev": true, "requires": { "rx-lite": "*" } }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "sane": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sane/-/sane-1.5.0.tgz", "integrity": "sha1-pK3q52TQSGIeyyfV+ez1ExAZOfM=", "dev": true, "requires": { "anymatch": "^1.3.0", "exec-sh": "^0.2.0", "fb-watchman": "^1.8.0", "minimatch": "^3.0.2", "minimist": "^1.1.1", "walker": "~1.0.5", "watch": "~0.10.0" }, "dependencies": { "bser": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", "integrity": "sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=", "dev": true, "requires": { "node-int64": "^0.4.0" } }, "fb-watchman": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.2.tgz", "integrity": "sha1-okz0eCf4LTj7Waaa1wt247auc4M=", "dev": true, "requires": { "bser": "1.0.2" } }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } } }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", "dev": true }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", "dev": true, "optional": true }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" } }, "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true, "requires": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" } }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true } } }, "sntp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "dev": true, "requires": { "hoek": "4.x.x" } }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { "source-map": "^0.5.6" } }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "dev": true, "requires": { "spdx-license-ids": "^1.0.2" } }, "spdx-expression-parse": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", "dev": true }, "spdx-license-ids": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", "dev": true }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" } }, "string-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", "dev": true, "requires": { "strip-ansi": "^3.0.0" } }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { "safe-buffer": "~5.1.0" } }, "stringstream": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", "dev": true }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { "is-utf8": "^0.2.0" } }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, "syntax-cli": { "version": "0.1.11", "resolved": "https://registry.npmjs.org/syntax-cli/-/syntax-cli-0.1.11.tgz", "integrity": "sha512-aupxOzWPBxZAsT7q8muVHjYRerl5fNXet+3Jrvy8PkCngMzU02Wh160E75O35nhMqIan2quR9+9n81MD9RBggA==", "dev": true, "requires": { "cli-table3": "^0.5.0", "colors": "^1.1.2", "nomnom": "^1.8.1" } }, "table": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { "ajv": "^5.2.3", "ajv-keywords": "^2.1.0", "chalk": "^2.1.0", "lodash": "^4.17.4", "slice-ansi": "1.0.0", "string-width": "^2.1.1" }, "dependencies": { "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" } }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "test-exclude": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", "integrity": "sha512-35+Asrsk3XHJDBgf/VRFexPgh3UyETv8IAn/LRTiZjVy6rjPVqdEk8dJcJYBzl1w0XCJM48lvTy8SfEsCWS4nA==", "dev": true, "requires": { "arrify": "^1.0.1", "micromatch": "^2.3.11", "object-assign": "^4.1.0", "read-pkg-up": "^1.0.1", "require-main-filename": "^1.0.1" } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "throat": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", "integrity": "sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { "os-tmpdir": "~1.0.2" } }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", "dev": true }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, "tough-cookie": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "dev": true, "requires": { "punycode": "^1.4.1" } }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { "safe-buffer": "^5.0.1" } }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { "prelude-ls": "~1.1.2" } }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true }, "user-home": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "dev": true }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", "dev": true }, "v8flags": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { "user-home": "^1.1.1" } }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true, "requires": { "spdx-correct": "~1.0.0", "spdx-expression-parse": "~1.0.0" } }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", "dev": true, "requires": { "makeerror": "1.0.x" } }, "watch": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=", "dev": true }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true }, "whatwg-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", "dev": true, "requires": { "iconv-lite": "0.4.19" } }, "whatwg-url": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", "integrity": "sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=", "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" }, "dependencies": { "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", "dev": true } } }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "^2.0.0" } }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, "worker-farm": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.1.tgz", "integrity": "sha512-T5NH6Wqsd8MwGD4AK8BBllUy6LmHaqjEOyo/YIUEegZui6/v5Bqde//3jwyE3PGiGYMmWi06exFBi5LNhhPFNw==", "dev": true, "requires": { "errno": "^0.1.4", "xtend": "^4.0.1" } }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { "mkdirp": "^0.5.1" } }, "xml-name-validator": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "dev": true }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true } } } regexp-tree-0.1.18/package.json000066400000000000000000000017311361507317500163300ustar00rootroot00000000000000{ "name": "regexp-tree", "version": "0.1.18", "license": "MIT", "description": "Regular Expressions parser in JavaScript", "repository": "DmitrySoshnikov/regexp-tree", "homepage": "https://github.com/DmitrySoshnikov/regexp-tree", "bugs": "https://github.com/DmitrySoshnikov/regexp-tree/issues", "scripts": { "build": "node scripts/build.js", "watch": "node scripts/build.js --watch", "test": "jest", "prepublish": "npm run build && npm test", "eslint": "eslint src/ && eslint bin/regexp-tree" }, "bin": { "regexp-tree": "./bin/regexp-tree" }, "keywords": [ "regexp", "parser", "AST", "tree", "JavaScript", "ECMAScript" ], "author": "Dmitry Soshnikov", "devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "1.6.1", "babel-preset-flow": "6.23.0", "eslint": "^4.11.0", "jest-cli": "^19.0.2", "prettier": "^1.17.1", "shelljs": "^0.7.8", "syntax-cli": "^0.1.11" } } regexp-tree-0.1.18/scripts/000077500000000000000000000000001361507317500155275ustar00rootroot00000000000000regexp-tree-0.1.18/scripts/build.js000066400000000000000000000032101361507317500171600ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const colors = require('colors'); const shell = require('shelljs'); // Whether we're in the watch mode (continuous JS code transpiling). const watchMode = process.argv[2] || ''; let watchMsg = ''; if (watchMode) { watchMsg = ` (watch mode)`; } console.info(colors.bold(`Building${watchMsg}...\n`)); // ---------------------------------------------------------- // Rebuild parser. console.info(colors.bold('[1/3] Generating parser module...\n')); shell.exec( `node node_modules/syntax-cli/bin/syntax -g src/parser/regexp.bnf -o src/parser/generated/regexp-tree.js -m lalr1 --loc` ); // ---------------------------------------------------------- // Git hooks. console.info(colors.bold('[2/3] Installing Git hooks...\n')); // Setup pre-commit hook. console.info(' - pre-commit: .git/hooks/pre-commit'); shell.exec('unlink .git/hooks/pre-commit'); shell.chmod('+x', './scripts/git-pre-commit'); shell.ln('-s', '../../scripts/git-pre-commit', '.git/hooks/pre-commit'); // Setup pre-push hook. console.info(' - pre-push: .git/hooks/pre-push\n'); shell.exec('unlink .git/hooks/pre-push'); shell.chmod('+x', './scripts/git-pre-push'); shell.ln('-s', '../../scripts/git-pre-push', '.git/hooks/pre-push'); // ---------------------------------------------------------- // Transform code for older Node versions. console.info(colors.bold('[3/3] Transpiling JS code...\n')); shell.exec( `NODE_ENV=production "node_modules/.bin/babel" ${process.argv[2] || ''} src/ --out-dir dist/ --ignore __tests__` ); console.info(colors.bold('\nAll done.\n')); regexp-tree-0.1.18/scripts/generate-unicode-id-parts.js000066400000000000000000000107161361507317500230310ustar00rootroot00000000000000// based on https://github.com/microsoft/TypeScript/tree/master/scripts/regenerate-unicode-identifier-parts.js /** @param {number} i */ function toHex4Digits(i) { let s = i.toString(16); while (s.length < 4) { s = '0' + s; } if (s.length > 4) throw new Error('Invalid Hex4Digits value'); return s; } class NonSurrogateRange { /** @param {number} codePoint */ constructor(codePoint) { this.firstCodePoint = codePoint; this.lastCodePoint = codePoint; } toString() { let text = '\\u' + toHex4Digits(this.firstCodePoint); if (this.lastCodePoint !== this.firstCodePoint) { text += '-\\u' + toHex4Digits(this.lastCodePoint); } return text; } } class LeadSurrogateRange { /** @param {number} leadSurrogate */ constructor(leadSurrogate) { this.leadSurrogate = leadSurrogate; /** @type {TrailSurrogateRange[]} */ this.ranges = []; } toString() { return ( '\\u' + toHex4Digits(this.leadSurrogate) + '[' + this.ranges.join('') + ']' ); } } class TrailSurrogateRange { /** @param {number} trailSurrogate */ constructor(trailSurrogate) { this.firstTrailSurrogate = trailSurrogate; this.lastTrailSurrogate = trailSurrogate; } toString() { let text = '\\u' + toHex4Digits(this.firstTrailSurrogate); if (this.lastTrailSurrogate !== this.firstTrailSurrogate) { text += '-\\u' + toHex4Digits(this.lastTrailSurrogate); } return text; } } class Writer { constructor() { /** @type {number} */ this.lastCodePoint = -1; /** @type {NonSurrogateRange[]} */ this.nonSurrogateRanges = []; /** @type {LeadSurrogateRange[]} */ this.surrogateRanges = []; /** @type {NonSurrogateRange} */ this.nonSurrogateRange; /** @type {LeadSurrogateRange} */ this.leadSurrogateRange; /** @type {TrailSurrogateRange} */ this.trailSurrogateRange; } /** @param {number} codePoint */ push(codePoint) { if (codePoint <= this.lastCodePoint) throw new Error('Code points must be added in order.'); this.lastCodePoint = codePoint; if (codePoint < MAX_UNICODE_NON_SURROGATE) { if ( this.nonSurrogateRange && this.nonSurrogateRange.lastCodePoint === codePoint - 1 ) { this.nonSurrogateRange.lastCodePoint = codePoint; return; } this.nonSurrogateRange = new NonSurrogateRange(codePoint); this.nonSurrogateRanges.push(this.nonSurrogateRange); } else { const leadSurrogate = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800; const trailSurrogate = ((codePoint - 0x10000) % 0x400) + 0xdc00; if ( !this.leadSurrogateRange || this.leadSurrogateRange.leadSurrogate !== leadSurrogate ) { this.trailSurrogateRange = undefined; this.leadSurrogateRange = new LeadSurrogateRange(leadSurrogate); this.surrogateRanges.push(this.leadSurrogateRange); } if ( this.trailSurrogateRange && this.trailSurrogateRange.lastTrailSurrogate === trailSurrogate - 1 ) { this.trailSurrogateRange.lastTrailSurrogate = trailSurrogate; return; } this.trailSurrogateRange = new TrailSurrogateRange(trailSurrogate); this.leadSurrogateRange.ranges.push(this.trailSurrogateRange); } } toString() { let first = this.nonSurrogateRanges.join(''); let second = this.surrogateRanges.join('|'); return first && second ? `([${first}]|${second})` : first ? `[${first}]` : second ? `(${second})` : ''; } } const MAX_UNICODE_NON_SURROGATE = 0xffff; const MAX_UNICODE_CODEPOINT = 0x10ffff; const isStart = c => /\p{ID_Start}/u.test(c); const isContinue = c => /\p{ID_Continue}/u.test(c); let idStartWriter = new Writer(); let idContinueWriter = new Writer(); for (let cp = 0; cp <= MAX_UNICODE_CODEPOINT; cp++) { const ch = String.fromCodePoint(cp); if (isStart(ch)) { idStartWriter.push(cp); } if (isContinue(ch)) { idContinueWriter.push(cp); } } console.log(`/** * Generated by scripts/generate-unicode-id-parts.js on node ${ process.version } with unicode ${process.versions.unicode} * based on http://www.unicode.org/reports/tr31/ and https://tc39.es/ecma262/#sec-names-and-keywords * U_ID_START corresponds to the ID_Start property, and U_ID_CONTINUE corresponds to ID_Continue property. */`); console.log('U_ID_START ' + idStartWriter.toString()); console.log('U_ID_CONTINUE ' + idContinueWriter.toString()); regexp-tree-0.1.18/scripts/git-pre-commit000077500000000000000000000005611361507317500203140ustar00rootroot00000000000000#!/bin/sh # Prettier jsfiles=$(git diff HEAD --name-only --diff-filter=ACM "*.js" | tr '\n' ' ') if [ ! -z "$jsfiles" ] then # Prettify all staged .js files echo "$jsfiles" | xargs ./node_modules/.bin/prettier --write # Add back the modified/prettified files to staging echo "$jsfiles" | xargs git add fi # Pre-commit validaitons: npm test npm run eslintregexp-tree-0.1.18/scripts/git-pre-push000077500000000000000000000000761361507317500200040ustar00rootroot00000000000000#!/bin/sh # Pre-commit validaitons: npm test npm run eslintregexp-tree-0.1.18/src/000077500000000000000000000000001361507317500146275ustar00rootroot00000000000000regexp-tree-0.1.18/src/bin/000077500000000000000000000000001361507317500153775ustar00rootroot00000000000000regexp-tree-0.1.18/src/bin/regexp-tree.js000066400000000000000000000010451361507317500201640ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; function main() { console.info(` ========================================================= * CLI commands are moved to the \x1b[1mregexp-tree-cli\x1b[0m package * ========================================================= \x1b[1mInstallation:\x1b[0m npm install -g regexp-tree-cli \x1b[1mUsage:\x1b[0m regexp-tree-cli --help `); } module.exports = main; if (require.main === module) { main(); } regexp-tree-0.1.18/src/compat-transpiler/000077500000000000000000000000001361507317500202735ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/README.md000066400000000000000000000025241361507317500215550ustar00rootroot00000000000000# regexp-tree: Compatibility transpiler The _compat-transpiler_ module translates your regexp in new format or in new syntax, into an equivalent regexp in a legacy representation, so it can be used in engines which don't yet implement the new syntax. Example, "dotAll" `s` flag: ```js /./s ``` Is translated into: ```js /[\0-\uFFFF]/ ``` Or named capturing groups: ```js /(?a)\k\1/ ``` Becomes: ```js /(a)\1\1/ ``` Thus, the information about processed group names is stored on the transform result, and can be further analyzed in other modules. ## API * `compatTranspiler.transform(regexp [, whitelist])` - transforms a regular expressions applying compatibility transforms (can be passed as `whitelist` parameter). ```js compatTranspiler.transform('/./s', ['dotAll']); ``` If whitelist is not passed, all transforms are applied. Available transforms are: * `dotAll` - translates `/./s` into `/[\0-\uFFFF]/` * `namedCapturingGroups` - transforms `/(?a)\k/` into `/(a)\1/` ## Babel plugin The _compat-transpiler_ module is also available as a _Babel plugin_, which can be installed at: [babel-plugin-transform-modern-regexp](https://www.npmjs.com/package/babel-plugin-transform-modern-regexp). Note, the plugin also includes [extended regexp](https://github.com/dmitrysoshnikov/regexp-tree#regexp-extensions) features.regexp-tree-0.1.18/src/compat-transpiler/__tests__/000077500000000000000000000000001361507317500222315ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/__tests__/compat-transpiler-integration-test.js000066400000000000000000000015521361507317500315340ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const compatTranspiler = require('..'); describe('compat-transpiler-integration-test', () => { it('applies all transforms', () => { const original = '/a.b(?x)/sx'; const compat = /a[\0-\uFFFF]b(x)/; const result = compatTranspiler.transform(original); expect(result.toString()) .toBe(compat.toString()); expect(result.getExtra()).toEqual({ // This transform collected group names, and their indices. namedCapturingGroups: { name: 1, }, }); }); it('applies whitelist only', () => { const original = '/a.b(?x)/s'; const compat = '/a[\\0-\\uFFFF]b(?x)/'; expect(compatTranspiler.transform(original, ['dotAll']).toString()) .toBe(compat); }); });regexp-tree-0.1.18/src/compat-transpiler/index.js000066400000000000000000000026061361507317500217440ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const compatTransforms = require('./transforms'); const 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(regexp, transformsWhitelist = []) { const transformToApply = transformsWhitelist.length > 0 ? transformsWhitelist : Object.keys(compatTransforms); let result; // Collect extra data per transform. const extra = {}; transformToApply.forEach(transformName => { if (!compatTransforms.hasOwnProperty(transformName)) { throw new Error( `Unknown compat-transform: ${transformName}. ` + `Available transforms are: ` + Object.keys(compatTransforms).join(', ') ); } const 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; }, }; regexp-tree-0.1.18/src/compat-transpiler/runtime/000077500000000000000000000000001361507317500217565ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/runtime/__tests__/000077500000000000000000000000001361507317500237145ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/runtime/__tests__/compat-transpiler-runtime-test.js000066400000000000000000000031131361507317500323520ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {RegExpTree} = require('..'); describe('compat-transpiler-runtime', () => { it('named capturing groups', () => { const originalSource = '(?\\d{4})-(?\\d{2})-(?\\d{2})'; const originalFlags = 'gs'; const originalRe = `/${originalSource}/${originalFlags}`; // This is what regexp-tree produces. const compat = /(\d{4})-(\d{2})-(\d{2})/g; const re = new RegExpTree(compat, { flags: originalFlags, source: originalSource, groups: { year: 1, month: 2, day: 3, }, }); expect(re.flags).toBe(originalFlags); expect(re.source).toBe(originalSource); expect(re.toString()).toBe(originalRe); // Flag properties. expect(re.dotAll).toBe(true); expect(re.global).toBe(true); expect(re.ignoreCase).toBe(false); expect(re.multiline).toBe(false); expect(re.sticky).not.toBe(true); // Testing runtime. const string = '2017-04-14'; let result = re.exec(string); expect(result).not.toBe(null); expect(result[0]).toBe(string); expect(result[1]).toBe('2017'); expect(result[2]).toBe('04'); expect(result[3]).toBe('14'); // Named groups. expect(result.groups).not.toBe(undefined); expect(result.groups.year).toBe(result[1]); expect(result.groups.month).toBe(result[2]); expect(result.groups.day).toBe(result[3]); // null result result = re.exec('foo-bar'); expect(result).toBe(null); }); });regexp-tree-0.1.18/src/compat-transpiler/runtime/index.js000066400000000000000000000040641361507317500234270ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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. */ class RegExpTree { /** * 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 */ constructor(re, { flags, groups, source, }) { 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. */ test(string) { return this._re.test(string); } /** * Facade wrapper for RegExp `compile` method. */ compile(string) { return this._re.compile(string); } /** * Facade wrapper for RegExp `toString` method. */ toString() { if (!this._toStringResult) { this._toStringResult = `/${this.source}/${this.flags}`; } return this._toStringResult; } /** * Facade wrapper for RegExp `exec` method. */ exec(string) { const result = this._re.exec(string); if (!this._groups || !result) { return result; } result.groups = {}; for (const group in this._groups) { const groupNumber = this._groups[group]; result.groups[group] = result[groupNumber]; } return result; } } module.exports = { RegExpTree, };regexp-tree-0.1.18/src/compat-transpiler/transforms/000077500000000000000000000000001361507317500224715ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/transforms/__tests__/000077500000000000000000000000001361507317500244275ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/transforms/__tests__/compat-dotall-s-transform-test.js000066400000000000000000000014031361507317500327510ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const compatDotAllSTransform = require('../compat-dotall-s-transform'); describe('compat-dotall-s-transform', () => { it('simple', () => { const re = transform('/a.b/s', [ compatDotAllSTransform, ]); expect(re.toString()).toBe('/a[\\0-\\uFFFF]b/'); }); it('with u flag', () => { const re = transform('/a.b/su', [ compatDotAllSTransform, ]); expect(re.toString()).toBe('/a[\\0-\\u{10FFFF}]b/u'); }); it('no s', () => { const re = transform('/a.b/u', [ compatDotAllSTransform, ]); expect(re.toString()).toBe('/a.b/u'); }); });compat-named-capturing-groups-transform-test.js000066400000000000000000000034501361507317500355520ustar00rootroot00000000000000regexp-tree-0.1.18/src/compat-transpiler/transforms/__tests__/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const compatNamedCapturingGroups = require('../compat-named-capturing-groups-transform'); describe('compat-named-capturing-groups-transform', () => { it('transforms named groups', () => { const re = transform( '/(?:x)(?a)(b)(?:y)(?c)\\k\\1\\2\\k\\k/', [compatNamedCapturingGroups] ); expect(re.toString()).toBe( /(?:x)(a)(b)(?:y)(c)\1\1\2\3\k/.toString() ); // Collected group names. expect(compatNamedCapturingGroups.getExtra()).toEqual({ foo: 1, bar: 3, }); }); it('group numbers', () => { const re = transform( '/(?(?(?a)b)c)(?d)(?e)\\k\\k\\k\\k\\k/', [compatNamedCapturingGroups] ); expect(re.toString()).toBe(/(((a)b)c)(d)(e)\1\2\3\4\5/.toString()); // Collected group names. expect(compatNamedCapturingGroups.getExtra()).toEqual({ c: 1, b: 2, a: 3, d: 4, e: 5, }); }); it('adjucent group numbers', () => { const re = transform('/((?a)(?b))\\k\\k/', [ compatNamedCapturingGroups, ]); expect(re.toString()).toBe(/((a)(b))\2\3/.toString()); // Collected group names. expect(compatNamedCapturingGroups.getExtra()).toEqual({ a: 2, b: 3, }); }); it('adjucent nested group numbers', () => { const re = transform('/((?a)((?b)))\\k\\k/', [ compatNamedCapturingGroups, ]); expect(re.toString()).toBe(/((a)((b)))\2\4/.toString()); // Collected group names. expect(compatNamedCapturingGroups.getExtra()).toEqual({ a: 2, b: 4, }); }); }); regexp-tree-0.1.18/src/compat-transpiler/transforms/__tests__/compat-x-flag-transform-test.js000066400000000000000000000006751361507317500324220ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const compatXFlagTransform = require('../compat-x-flag-transform'); describe('compat-x-flag-transform', () => { it('removes x flag', () => { const re = transform('/foo/x', [ compatXFlagTransform, ]); expect(re.toString()).toBe('/foo/'); }); });regexp-tree-0.1.18/src/compat-transpiler/transforms/compat-dotall-s-transform.js000066400000000000000000000025261361507317500300450ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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(ast) { const 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(path) { const {node} = path; if (node.kind !== 'meta' || node.value !== '.') { return; } let toValue = '\\uFFFF'; let toSymbol = '\uFFFF'; if (this._hasUFlag) { toValue = '\\u{10FFFF}'; toSymbol = '\u{10FFFF}'; } path.replace({ type: 'CharacterClass', expressions: [ { type: 'ClassRange', from: { type: 'Char', value: '\\0', kind: 'decimal', symbol: '\u0000' }, to: { type: 'Char', value: toValue, kind: 'unicode', symbol: toSymbol, } } ] }); } };regexp-tree-0.1.18/src/compat-transpiler/transforms/compat-named-capturing-groups-transform.js000066400000000000000000000017641361507317500327240ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to translate `/(?a)\k/` 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() { this._groupNames = {}; }, /** * Returns extra state, which eventually is returned to */ getExtra() { return this._groupNames; }, Group(path) { const {node} = path; if (!node.name) { return; } // Record group name. this._groupNames[node.name] = node.number; delete node.name; delete node.nameRaw; }, Backreference(path) { const {node} = path; if (node.kind !== 'name') { return; } node.kind = 'number'; node.reference = node.number; delete node.referenceRaw; }, }; regexp-tree-0.1.18/src/compat-transpiler/transforms/compat-x-flag-transform.js000066400000000000000000000006641361507317500275050ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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({node}) { if (node.flags.includes('x')) { node.flags = node.flags.replace('x', ''); } }, };regexp-tree-0.1.18/src/compat-transpiler/transforms/index.js000066400000000000000000000006051361507317500241370ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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'), };regexp-tree-0.1.18/src/generator/000077500000000000000000000000001361507317500166155ustar00rootroot00000000000000regexp-tree-0.1.18/src/generator/README.md000066400000000000000000000013261361507317500200760ustar00rootroot00000000000000# regexp-tree: Code generator module Generates a regexp string from an AST. See [the specification](https://github.com/DmitrySoshnikov/regexp-tree#ast-nodes-specification) for AST nodes format. Example: ```js const regexpTree = require('regexp-tree'); // Get AST. const ast = regexpTree.parse('/[a-z]{1,}/'); // Handle nodes. regexpTree.traverse(ast, { // Handle "Quantifier" node type, // transforming `{1,}` quantifier to `+`. Quantifier({node}) { // {1,} -> + if ( node.kind === 'Range' && node.from === 1 && !node.to ) { node.kind = '+'; delete node.from; } }, }); // Generate the regexp. const re = regexpTree.generate(ast); console.log(re); // '/[a-z]+/' ```regexp-tree-0.1.18/src/generator/__tests__/000077500000000000000000000000001361507317500205535ustar00rootroot00000000000000regexp-tree-0.1.18/src/generator/__tests__/generator-basic-test.js000066400000000000000000000061271361507317500251410ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('../../parser'); const generator = require('..'); function test(re) { const reStr = re.toString(); const parsed = parser.parse(reStr); const generated = generator.generate(parsed); expect(generated).toBe(reStr); } describe('generator-basic', () => { it('simple char', () => { test(/a/); }); it('space char', () => { test(/ /); }); it('escaped char', () => { test(/\z/); }); it('unicode char', () => { test(/\u0036/); }); it('hex char', () => { test(/\x3B/); }); it('octal char', () => { test(/\073/); }); it('meta char', () => { test(/\n/); test(/\r/); test(/\s/); test(/\S/); test(/\d/); test(/\D/); test(/\w/); test(/\W/); test(/\v/); test(/\f/); }); it('alternative', () => { test(/abc/); }); it('disjunction', () => { test(/a|b/); }); it('disjunction - empty left', () => { test(/|a/); }); it('disjunction - empty right', () => { test(/a|/); }); it('capturing group', () => { test(/(ab)/); }); it('empty capturing group', () => { test(/()/); }); it('non-capturing group', () => { test(/(?:ab)/); }); it('named group', () => { test('/(?bar)/u'); }); it('empty named group', () => { test('/(?)/u'); }); it('empty non-capturing group', () => { test(/(?:)/); }); it('numeric backreference', () => { test(/(a)\1/); }); it('named backreference', () => { test('/(?)\\k/u'); }); it('basic-assertion', () => { test(/^/); test(/$/); test(/\b/); test(/\B/); }); it('positive character class', () => { test(/[a-z0]/); }); it('empty positive character class', () => { /*eslint no-empty-character-class:0*/ test(/[]/); }); it('negative character class', () => { test(/[^a-z0]/); }); it('empty negative character class', () => { test(/[^]/); }); it('positive lookahead assertion', () => { test(/(?=abc)/); }); it('empty positive lookahead assertion', () => { test(/(?=)/); }); it('negative lookahead assertion', () => { test(/(?!abc)/); }); it('empty negative lookahead assertion', () => { test(/(?!)/); }); it('positive lookbehind assertion', () => { test('/(?<=abc)/'); }); it('empty positive lookbehind assertion', () => { test('/(?<=)/'); }); it('negative lookbehind assertion', () => { test('/(? { test('/(? { test(/a?/); test(/a*/); test(/a+/); }); it('simple non-greedy quantifier', () => { test(/a??/); test(/a*?/); test(/a+?/); }); it('range greedy quantifier', () => { test(/a{1}/); test(/a{1,}/); test(/a{1,3}/); }); it('range non-greedy quantifier', () => { test(/a{1}?/); test(/a{1,}?/); test(/a{1,3}?/); }); }); regexp-tree-0.1.18/src/generator/index.js000066400000000000000000000070461361507317500202710ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * Helper `gen` function calls node type handler. */ function gen(node) { return node ? generator[node.type](node) : ''; } /** * AST handler. */ const generator = { RegExp(node) { return `/${gen(node.body)}/${node.flags}`; }, Alternative(node) { return (node.expressions || []).map(gen).join(''); }, Disjunction(node) { return `${gen(node.left)}|${gen(node.right)}`; }, Group(node) { const expression = gen(node.expression); if (node.capturing) { // A named group. if (node.name) { return `(?<${node.nameRaw || node.name}>${expression})`; } return `(${expression})`; } return `(?:${expression})`; }, 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(node) { switch (node.kind) { case '^': case '$': case '\\b': case '\\B': return node.kind; case 'Lookahead': { const assertion = gen(node.assertion); if (node.negative) { return `(?!${assertion})`; } return `(?=${assertion})`; } case 'Lookbehind': { const assertion = gen(node.assertion); if (node.negative) { return `(?DFA subset construction"_ technique. Resulting DFA match function is just a loop over string characters, transitioning from state to state based on this table. You can read more on implementation details in [this series of articles](https://medium.com/@DmitrySoshnikov/building-a-regexp-machine-part-1-regular-grammars-d4986b585d7e).regexp-tree-0.1.18/src/interpreter/finite-automaton/__tests__/000077500000000000000000000000001361507317500244135ustar00rootroot00000000000000regexp-tree-0.1.18/src/interpreter/finite-automaton/__tests__/fa-state-test.js000066400000000000000000000022471361507317500274370ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const State = require('../state'); function setIndex(set, index) { return [...set][index]; } describe('fa-state', () => { it('accepting', () => { const A = new State({ accepting: true, }); const B = new State(); B.accepting = true; const C = new State(); expect(A.accepting).toBe(true); expect(B.accepting).toBe(true); expect(C.accepting).toBe(false); }); it('add symbol transitions', () => { const A = new State(); const B = new State(); expect(A.getTransitions().size).toBe(0); expect(B.getTransitions().size).toBe(0); A.addTransition('b', B); expect(A.getTransitions().size).toBe(1); expect(B.getTransitions().size).toBe(0); expect(A.getTransitionsOnSymbol('b').size).toBe(1); expect(setIndex(A.getTransitionsOnSymbol('b'), 0)).toBe(B); const C = new State(); A.addTransition('b', C); expect(A.getTransitions().size).toBe(1); expect(A.getTransitionsOnSymbol('b').size).toBe(2); expect(setIndex(A.getTransitionsOnSymbol('b'), 1)).toBe(C); }); });regexp-tree-0.1.18/src/interpreter/finite-automaton/__tests__/fa-test.js000066400000000000000000000035331361507317500263200ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFA = require('../nfa/nfa'); const DFA = require('../dfa/dfa'); const fa = require('../'); const builders = require('../nfa/builders'); describe('fa', () => { it('API', () => { expect(fa.NFA).toBe(NFA); expect(fa.DFA).toBe(DFA); expect(fa.builders).toBe(builders); expect(typeof fa.test).toBe('function'); expect(typeof fa.toNFA).toBe('function'); expect(typeof fa.toDFA).toBe('function'); }); it('test', () => { expect(fa.test(/a*/, '')).toBe(true); expect(fa.test(/a*/, 'aaa')).toBe(true); expect(fa.test(/a*/, 'ab')).toBe(false); expect(fa.test(/abc/, 'abc')).toBe(true); expect(fa.test(/a|b/, 'a')).toBe(true); expect(fa.test(/a|b/, 'b')).toBe(true); expect(fa.test(/a|b/, 'c')).toBe(false); expect(fa.test(/ab|bcd*/, 'ab')).toBe(true); expect(fa.test(/ab|bcd*/, 'bc')).toBe(true); expect(fa.test(/ab|bcd*/, 'bcddd')).toBe(true); }); it('builders', () => { const { alt, char, or, rep, } = fa.builders; const nfa = or( alt(char('a'), char('b')), rep(char('c')) ); expect(nfa.matches('ab')).toBe(true); expect(nfa.matches('c')).toBe(true); expect(new fa.DFA(nfa).matches('ab')).toBe(true); expect(new fa.DFA(nfa).matches('c')).toBe(true); }); it('toNFA', () => { const nfa = fa.toNFA(/a*/); expect(nfa).toBeInstanceOf(NFA); expect(nfa.matches('')).toBe(true); expect(nfa.matches('a')).toBe(true); expect(nfa.matches('aa')).toBe(true); }); it('toDFA', () => { const dfa = fa.toDFA(/a*/); expect(dfa).toBeInstanceOf(DFA); expect(dfa.matches('')).toBe(true); expect(dfa.matches('a')).toBe(true); expect(dfa.matches('aa')).toBe(true); }); });regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/000077500000000000000000000000001361507317500232075ustar00rootroot00000000000000regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/__tests__/000077500000000000000000000000001361507317500251455ustar00rootroot00000000000000regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/__tests__/dfa-minimizer-test.js000066400000000000000000000034341361507317500312170ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const DFAMinimizer = require('../dfa-minimizer'); const fa = require('../../index'); function testMinimization({ regexp, originalTable, minimizedTable, newAcceptingStates, }) { const dfa = fa.toDFA(regexp); expect(dfa.getTransitionTable()).toEqual(originalTable); const minimized = DFAMinimizer.minimize(dfa); expect(minimized.getTransitionTable()).toEqual(minimizedTable); expect(minimized.getAcceptingStateNumbers()).toEqual(newAcceptingStates); } describe('dfa-minimizer', () => { it('a|b', () => { testMinimization({ regexp: /a|b|c|d/, originalTable: { 1: {a: 5, b: 4, c: 3, d: 2}, 2: {}, 3: {}, 4: {}, 5: {}, }, minimizedTable: { 1: {a: 2, b: 2, c: 2, d: 2}, 2: {}, }, newAcceptingStates: new Set([2]), }); }); it('a*', () => { testMinimization({ regexp: /a*/, originalTable: { 1: {a: 2}, 2: {a: 2}, }, minimizedTable: { 1: {a: 1}, }, newAcceptingStates: new Set([1]), }); }); it('aa*', () => { testMinimization({ regexp: /aa*/, originalTable: { 1: {a: 2}, 2: {a: 3}, 3: {a: 3}, }, minimizedTable: { 1: {a: 2}, 2: {a: 2}, }, newAcceptingStates: new Set([2]), }); }); it('ab', () => { testMinimization({ regexp: /ab/, originalTable: { 1: {a: 3}, 2: {}, 3: {b: 2}, }, // Stays the same, already minimal. minimizedTable: { 1: {a: 3}, 2: {}, 3: {b: 2}, }, newAcceptingStates: new Set([2]), }); }); });regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/__tests__/dfa-test.js000066400000000000000000000055061361507317500272200ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const DFA = require('../dfa'); const NFA = require('../../nfa/nfa'); const NFAState = require('../../nfa/nfa-state'); const { char, rep, } = require('../../nfa/builders'); const { EPSILON, } = require('../../special-symbols'); // x|y function getDefaultNFA() { const A = new NFAState(); const B = new NFAState(); const C = new NFAState(); const D = new NFAState(); const E = new NFAState(); const F = new NFAState({ accepting: true, }); // x|y A.addTransition(EPSILON, B); A.addTransition(EPSILON, C); // x B.addTransition('x', D); // y C.addTransition('y', E); D.addTransition(EPSILON, F); E.addTransition(EPSILON, F); return new NFA(A, F); } describe('dfa', () => { it('alphabet', () => { const dfa = new DFA(getDefaultNFA()); expect(dfa.getAlphabet()).toEqual(new Set(['x', 'y'])); }); it('accepting states', () => { const dfa = new DFA(getDefaultNFA()); expect(dfa.getOriginaAcceptingStateNumbers()) .toEqual(new Set(['6,4', '3,4'])); expect(dfa.getAcceptingStateNumbers()) .toEqual(new Set([2, 3])); }); it('transition table', () => { const dfa = new DFA(getDefaultNFA()); expect(dfa.getOriginalTransitionTable()).toEqual({ '1,2,5': {'x': '3,4', 'y': '6,4'}, '3,4': {}, '6,4': {}, }); expect(dfa.getTransitionTable()).toEqual({ 1: {'x': 3, 'y': 2}, 2: {}, 3: {}, }); }); it('matches', () => { const dfa = new DFA(getDefaultNFA()); expect(dfa.matches('x')).toBe(true); expect(dfa.matches('y')).toBe(true); expect(dfa.matches('z')).toBe(false); expect(dfa.matches('')).toBe(false); }); it('matches rep', () => { const dfa = new DFA(rep(char('a'))); expect(dfa.matches('')).toBe(true); expect(dfa.matches('a')).toBe(true); expect(dfa.matches('aa')).toBe(true); expect(dfa.matches('aaa')).toBe(true); }); it('minimizes', () => { const dfa = new DFA(getDefaultNFA()); expect(dfa.getTransitionTable()).toEqual({ 1: {'x': 3, 'y': 2}, 2: {}, 3: {}, }); dfa.minimize(); expect(dfa.getTransitionTable()).toEqual({ 1: {'x': 2, 'y': 2}, 2: {}, }); }); it('matches minimize', () => { const dfa = new DFA(getDefaultNFA()); dfa.minimize(); expect(dfa.matches('x')).toBe(true); expect(dfa.matches('y')).toBe(true); expect(dfa.matches('z')).toBe(false); expect(dfa.matches('')).toBe(false); }); it('matches rep', () => { const dfa = new DFA(rep(char('a'))); dfa.minimize(); expect(dfa.matches('')).toBe(true); expect(dfa.matches('a')).toBe(true); expect(dfa.matches('aa')).toBe(true); expect(dfa.matches('aaa')).toBe(true); }); }); regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/dfa-minimizer.js000066400000000000000000000131031361507317500262760ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; // DFA minization. /** * Map from state to current set it goes. */ let 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) { const table = dfa.getTransitionTable(); const allStates = Object.keys(table); const alphabet = dfa.getAlphabet(); const accepting = dfa.getAcceptingStateNumbers(); currentTransitionMap = {}; const nonAccepting = new Set(); allStates.forEach(state => { state = Number(state); const 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. const all = [ // 0-equivalent sets. [nonAccepting, accepting] .filter(set => set.size > 0), ]; let current; let previous; // 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. while (!sameRow(current, previous)) { const newTransitionMap = {}; for (const set of current) { // Handled states for this set. const handledStates = {}; const [first, ...rest] = set; handledStates[first] = new Set([first]); // Have to compare each from the rest states with // the already handled states, and see if they are equivalent. restSets: for (const state of rest) { for (const handledState of Object.keys(handledStates)) { // 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. handledStates[state] = new Set([state]); } // Add these handled states to all states map. Object.assign(newTransitionMap, handledStates); } // Update current transition map for the handled row. currentTransitionMap = newTransitionMap; let newSets = new Set( Object.keys(newTransitionMap) .map(state => newTransitionMap[state]) ); all.push([...newSets]); // Top of the stack is the current. current = all[all.length - 1]; // Previous set. previous = all[all.length - 2]; } // --------------------------------------------------------------------------- // Step 2: build minimized table from the equivalent sets. // Remap state numbers from sets to index-based. const remaped = new Map(); let idx = 1; current.forEach(set => remaped.set(set, idx++)); // Build the minimized table from the calculated equivalent sets. const minimizedTable = {}; const minimizedAcceptingStates = new Set(); const updateAcceptingStates = (set, idx) => { for (const state of set) { if (accepting.has(state)) { minimizedAcceptingStates.add(idx); } } }; for (const [set, idx] of remaped.entries()) { minimizedTable[idx] = {}; for (const symbol of alphabet) { updateAcceptingStates(set, idx); // Determine original transition for this symbol from the set. let originalTransition; for (const originalState of set) { originalTransition = table[originalState][symbol]; if (originalTransition) { break; } } if (originalTransition) { minimizedTable[idx][symbol] = remaped.get( currentTransitionMap[originalTransition] ); } } } // Update the table, and accepting states on the original DFA. dfa.setTransitionTable(minimizedTable); dfa.setAcceptingStateNumbers(minimizedAcceptingStates); return dfa; } function sameRow(r1, r2) { if (!r2) { return false; } if (r1.length !== r2.length) { return false; } for (let i = 0; i < r1.length; i++) { const s1 = r1[i]; const s2 = r2[i]; if (s1.size !== s2.size) { return false; } if ([...s1].sort().join(',') !== [...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) { for (const symbol of alphabet) { if (!goToSameSet(s1, s2, table, symbol)) { return false; } } return true; } /** * Checks whether states go to the same set. */ function goToSameSet(s1, s2, table, symbol) { if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) { return false; } const originalTransitionS1 = table[s1][symbol]; const 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, };regexp-tree-0.1.18/src/interpreter/finite-automaton/dfa/dfa.js000066400000000000000000000130711361507317500243010ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const DFAMinimizer = require('./dfa-minimizer'); const {EPSILON_CLOSURE} = require('../special-symbols'); /** * DFA is build by converting from NFA (subset construction). */ class DFA { constructor(nfa) { this._nfa = nfa; } /** * Minimizes DFA. */ minimize() { this.getTransitionTable(); this._originalAcceptingStateNumbers = this._acceptingStateNumbers; this._originalTransitionTable = this._transitionTable; DFAMinimizer.minimize(this); } /** * Returns alphabet for this DFA. */ getAlphabet() { return this._nfa.getAlphabet(); } /** * Returns accepting states. */ getAcceptingStateNumbers() { if (!this._acceptingStateNumbers) { // Accepting states are determined during table construction. this.getTransitionTable(); } return this._acceptingStateNumbers; } /** * Returns original accepting states. */ getOriginaAcceptingStateNumbers() { if (!this._originalAcceptingStateNumbers) { // Accepting states are determined during table construction. this.getTransitionTable(); } return this._originalAcceptingStateNumbers; } /** * Sets transition table. */ setTransitionTable(table) { this._transitionTable = table; } /** * Sets accepting states. */ setAcceptingStateNumbers(stateNumbers) { this._acceptingStateNumbers = stateNumbers; } /** * DFA transition table is built from NFA table. */ getTransitionTable() { if (this._transitionTable) { return this._transitionTable; } // Calculate from NFA transition table. const nfaTable = this._nfa.getTransitionTable(); const nfaStates = Object.keys(nfaTable); this._acceptingStateNumbers = new Set(); // Start state of DFA is E(S[nfa]) const startState = nfaTable[nfaStates[0]][EPSILON_CLOSURE]; // Init the worklist (states which should be in the DFA). const worklist = [startState]; const alphabet = this.getAlphabet(); const nfaAcceptingStates = this._nfa.getAcceptingStateNumbers(); const dfaTable = {}; // Determine whether the combined DFA state is accepting. const updateAcceptingStates = states => { for (const nfaAcceptingState of nfaAcceptingStates) { // 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; } } }; while (worklist.length > 0) { const states = worklist.shift(); const dfaStateLabel = states.join(','); dfaTable[dfaStateLabel] = {}; for (const symbol of alphabet) { let onSymbol = []; // Determine whether the combined state is accepting. updateAcceptingStates(states); for (const state of states) { const nfaStatesOnSymbol = nfaTable[state][symbol]; if (!nfaStatesOnSymbol) { continue; } for (const nfaStateOnSymbol of nfaStatesOnSymbol) { if (!nfaTable[nfaStateOnSymbol]) { continue; } onSymbol.push(...nfaTable[nfaStateOnSymbol][EPSILON_CLOSURE]); } } const dfaStatesOnSymbolSet = new Set(onSymbol); const dfaStatesOnSymbol = [...dfaStatesOnSymbolSet]; if (dfaStatesOnSymbol.length > 0) { const dfaOnSymbolStr = dfaStatesOnSymbol.join(','); dfaTable[dfaStateLabel][symbol] = dfaOnSymbolStr; if (!dfaTable.hasOwnProperty(dfaOnSymbolStr)) { worklist.unshift(dfaStatesOnSymbol); } } } } return (this._transitionTable = this._remapStateNumbers(dfaTable)); } /** * Remaps state numbers in the resulting table: * combined states '1,2,3' -> 1, '3,4' -> 2, etc. */ _remapStateNumbers(calculatedDFATable) { const newStatesMap = {}; this._originalTransitionTable = calculatedDFATable; const transitionTable = {}; Object.keys(calculatedDFATable).forEach((originalNumber, newNumber) => { newStatesMap[originalNumber] = newNumber + 1; }); for (const originalNumber in calculatedDFATable) { const originalRow = calculatedDFATable[originalNumber]; const row = {}; for (const symbol in originalRow) { row[symbol] = newStatesMap[originalRow[symbol]]; } transitionTable[newStatesMap[originalNumber]] = row; } // Remap accepting states. this._originalAcceptingStateNumbers = this._acceptingStateNumbers; this._acceptingStateNumbers = new Set(); for (const originalNumber of this._originalAcceptingStateNumbers) { this._acceptingStateNumbers.add(newStatesMap[originalNumber]); } return transitionTable; } /** * Returns original DFA table, where state numbers * are combined numbers from NFA. */ getOriginalTransitionTable() { if (!this._originalTransitionTable) { // Original table is determined during table construction. this.getTransitionTable(); } return this._originalTransitionTable; } /** * Checks whether this DFA accepts a string. */ matches(string) { let state = 1; let i = 0; const table = this.getTransitionTable(); while (string[i]) { state = table[state][string[i++]]; if (!state) { return false; } } if (!this.getAcceptingStateNumbers().has(state)) { return false; } return true; } } module.exports = DFA; regexp-tree-0.1.18/src/interpreter/finite-automaton/index.js000066400000000000000000000021021361507317500241150ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFA = require('./nfa/nfa'); const DFA = require('./dfa/dfa'); const nfaFromRegExp = require('./nfa/nfa-from-regexp'); const builders = require('./nfa/builders'); module.exports = { /** * Export NFA and DFA classes. */ NFA, DFA, /** * Expose 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(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(regexp) { return new DFA(this.toNFA(regexp)); }, /** * Returns true if regexp accepts the string. */ test(regexp, string) { return this.toDFA(regexp).matches(string); }, };regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/000077500000000000000000000000001361507317500232215ustar00rootroot00000000000000regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/__tests__/000077500000000000000000000000001361507317500251575ustar00rootroot00000000000000regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/__tests__/builders-test.js000066400000000000000000000130131361507317500303010ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFA = require('../nfa'); const NFAState = require('../nfa-state'); const {EPSILON} = require('../../special-symbols'); const { alt, char, e, or, rep, repExplicit, plusRep, questionRep, } = require('../builders'); function getAtIndex(set, index) { return [...set][index]; } describe('nfa-builders', () => { it('char', () => { const a = char('a'); expect(a).toBeInstanceOf(NFA); expect(a.in).toBeInstanceOf(NFAState); expect(a.out).toBeInstanceOf(NFAState); expect(a.in.accepting).toBe(false); expect(a.out.accepting).toBe(true); expect(a.in.getTransitionsOnSymbol('a').size).toBe(1); expect(a.in.getTransitionsOnSymbol('a')).toEqual(new Set([a.out])); expect(a.matches('a')).toBe(true); }); it('e', () => { const epsilon = e(); expect(epsilon).toBeInstanceOf(NFA); expect(epsilon.in).toBeInstanceOf(NFAState); expect(epsilon.out).toBeInstanceOf(NFAState); expect(epsilon.in.accepting).toBe(false); expect(epsilon.out.accepting).toBe(true); expect(epsilon.in.getTransitionsOnSymbol(EPSILON).size).toBe(1); expect(epsilon.in.getTransitionsOnSymbol(EPSILON)).toEqual( new Set([epsilon.out]) ); expect(epsilon.matches('')).toBe(true); }); it('or', () => { const a = char('a'); const b = char('b'); const c = char('c'); // Before patching chars are accepting. expect(a.out.accepting).toBe(true); expect(b.out.accepting).toBe(true); expect(c.out.accepting).toBe(true); const AorBorC = or(a, b, c); expect(AorBorC).toBeInstanceOf(NFA); // After patching chars out are not accepting. expect(a.out.accepting).toBe(false); expect(b.out.accepting).toBe(false); expect(c.out.accepting).toBe(false); expect(AorBorC.out.accepting).toBe(true); const AorB = getAtIndex(AorBorC.in.getTransitionsOnSymbol(EPSILON), 0); const partA = getAtIndex(AorB.getTransitionsOnSymbol(EPSILON), 0); const partB = getAtIndex(AorB.getTransitionsOnSymbol(EPSILON), 1); expect(partA).toBe(a.in); expect(partB).toBe(b.in); const partC = getAtIndex(AorBorC.in.getTransitionsOnSymbol(EPSILON), 1); expect(partC).toBe(c.in); const outFromB = getAtIndex( getAtIndex( b.out.getTransitionsOnSymbol(EPSILON), 0 ).getTransitionsOnSymbol(EPSILON), 0 ); expect(outFromB).toBe(AorBorC.out); const outFromC = getAtIndex(c.out.getTransitionsOnSymbol(EPSILON), 0); expect(outFromC).toBe(AorBorC.out); expect(AorBorC.matches('a')).toBe(true); expect(AorBorC.matches('b')).toBe(true); expect(AorBorC.matches('c')).toBe(true); }); it('alt', () => { const a = char('a'); const b = char('b'); const c = char('c'); // Before patching chars are accepting. expect(a.out.accepting).toBe(true); expect(b.out.accepting).toBe(true); expect(c.out.accepting).toBe(true); const ABC = alt(a, b, c); expect(ABC).toBeInstanceOf(NFA); // After patching chars out are not accepting. expect(a.out.accepting).toBe(false); expect(b.out.accepting).toBe(false); expect(c.out.accepting).toBe(true); expect(ABC.in).toBe(a.in); expect(ABC.out).toBe(c.out); expect(getAtIndex(a.out.getTransitionsOnSymbol(EPSILON), 0)).toBe(b.in); expect(getAtIndex(b.out.getTransitionsOnSymbol(EPSILON), 0)).toBe(c.in); expect(ABC.matches('abc')).toBe(true); }); it('kleene-closure-explicit', () => { const a = char('a'); const aRep = repExplicit(a); expect(aRep).toBeInstanceOf(NFA); const partA = getAtIndex(aRep.in.getTransitionsOnSymbol(EPSILON), 0); expect(partA).toBe(a.in); const partEpsilon = getAtIndex(aRep.in.getTransitionsOnSymbol(EPSILON), 1); expect(partEpsilon.accepting).toBe(true); expect(partEpsilon).toBe(aRep.out); const backToA = getAtIndex(aRep.out.getTransitionsOnSymbol(EPSILON), 0); expect(backToA).toBe(a.in); expect(aRep.matches('')).toBe(true); expect(aRep.matches('a')).toBe(true); expect(aRep.matches('aaa')).toBe(true); }); it('kleene-closure', () => { const a = char('a'); const aRep = rep(a); expect(aRep).toBe(a); expect(aRep).toBeInstanceOf(NFA); const partEpsilon = getAtIndex(aRep.in.getTransitionsOnSymbol(EPSILON), 0); expect(partEpsilon.accepting).toBe(true); expect(partEpsilon).toBe(aRep.out); const backToA = getAtIndex(aRep.out.getTransitionsOnSymbol(EPSILON), 0); expect(backToA).toBe(a.in); expect(aRep.matches('')).toBe(true); expect(aRep.matches('a')).toBe(true); expect(aRep.matches('aaa')).toBe(true); }); it('plusRep', () => { const a = char('a'); const aPlus = plusRep(a); expect(aPlus).toBe(a); expect(aPlus).toBeInstanceOf(NFA); const backToA = getAtIndex(aPlus.out.getTransitionsOnSymbol(EPSILON), 0); expect(backToA).toBe(a.in); expect(aPlus.matches('')).toBe(false); expect(aPlus.matches('a')).toBe(true); expect(aPlus.matches('aaa')).toBe(true); }); it('questionRep', () => { const a = char('a'); const aQuestion = questionRep(a); expect(aQuestion).toBe(a); expect(aQuestion).toBeInstanceOf(NFA); const partEpsilon = getAtIndex( aQuestion.in.getTransitionsOnSymbol(EPSILON), 0 ); expect(partEpsilon).toBe(a.out); expect(aQuestion.matches('')).toBe(true); expect(aQuestion.matches('a')).toBe(true); expect(aQuestion.matches('aa')).toBe(false); }); }); regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/__tests__/nfa-from-regexp-test.js000066400000000000000000000033311361507317500314670ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const nfaFromRegExp = require('../nfa-from-regexp'); const NFA = require('../nfa'); describe('nfa-from-regexp', () => { it('char', () => { const a = nfaFromRegExp.build(/a/); expect(a).toBeInstanceOf(NFA); expect(a.matches('a')).toBe(true); expect(a.matches('b')).toBe(false); }); it('or', () => { const AorBorC = nfaFromRegExp.build(/a|b|c/); expect(AorBorC).toBeInstanceOf(NFA); expect(AorBorC.matches('a')).toBe(true); expect(AorBorC.matches('b')).toBe(true); expect(AorBorC.matches('c')).toBe(true); expect(AorBorC.matches('d')).toBe(false); }); it('alt', () => { const ABC = nfaFromRegExp.build(/abc/); expect(ABC).toBeInstanceOf(NFA); expect(ABC.matches('abc')).toBe(true); expect(ABC.matches('ab')).toBe(false); expect(ABC.matches('a')).toBe(false); expect(ABC.matches('d')).toBe(false); }); it('rep', () => { const aRep = nfaFromRegExp.build(/a*/); expect(aRep).toBeInstanceOf(NFA); expect(aRep.matches('')).toBe(true); expect(aRep.matches('a')).toBe(true); expect(aRep.matches('aa')).toBe(true); expect(aRep.matches('aaa')).toBe(true); expect(aRep.matches('ab')).toBe(false); expect(aRep.matches('b')).toBe(false); }); it('a+', () => { const aRep = nfaFromRegExp.build(/a+/); expect(aRep).toBeInstanceOf(NFA); expect(aRep.matches('')).toBe(false); expect(aRep.matches('a')).toBe(true); expect(aRep.matches('aa')).toBe(true); expect(aRep.matches('aaa')).toBe(true); expect(aRep.matches('ab')).toBe(false); expect(aRep.matches('b')).toBe(false); }); }); regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/__tests__/nfa-state-test.js000066400000000000000000000025161361507317500303600ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFAState = require('../nfa-state'); const {EPSILON} = require('../../special-symbols'); describe('nfa-state', () => { it('epsilon-closure', () => { const A = new NFAState(); const B = new NFAState(); const C = new NFAState({ accepting: true, }); A.addTransition(EPSILON, B); B.addTransition(EPSILON, C); C.addTransition(EPSILON, A); A.addTransition('c', C); expect(A.getEpsilonClosure()).toEqual(new Set([A, B, C])); expect(B.getEpsilonClosure()).toEqual(new Set([B, C, A])); expect(C.getEpsilonClosure()).toEqual(new Set([C, A, B])); }); it('matches', () => { const A = new NFAState(); const B = new NFAState(); const C = new NFAState(); const D = new NFAState(); const E = new NFAState(); const F = new NFAState({ accepting: true, }); // x|y A.addTransition(EPSILON, B); A.addTransition(EPSILON, C); // x B.addTransition('x', D); // y C.addTransition('y', E); D.addTransition(EPSILON, F); E.addTransition(EPSILON, F); expect(A.matches('x')).toBe(true); expect(A.matches('y')).toBe(true); expect(A.matches('z')).toBe(false); expect(A.matches('')).toBe(false); }); });regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/__tests__/nfa-test.js000066400000000000000000000040721361507317500272410ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFA = require('../nfa'); const NFAState = require('../nfa-state'); const { EPSILON, } = require('../../special-symbols'); // x|y function getDefaultNFA() { const A = new NFAState(); const B = new NFAState(); const C = new NFAState(); const D = new NFAState(); const E = new NFAState(); const F = new NFAState({ accepting: true, }); // x|y A.addTransition(EPSILON, B); A.addTransition(EPSILON, C); // x B.addTransition('x', D); // y C.addTransition('y', E); D.addTransition(EPSILON, F); E.addTransition(EPSILON, F); return new NFA(A, F); } describe('nfa', () => { it('alphabet', () => { const A = new NFAState(); const B = new NFAState(); const C = new NFAState({ accepting: true, }); A.addTransition(EPSILON, C); A.addTransition('b', B); B.addTransition('c', C); const nfa = new NFA(A, C); expect(nfa.getAlphabet()).toEqual(new Set(['b', 'c'])); }); it('accepting states', () => { const A = new NFAState(); const B = new NFAState({ accepting: true, }); const C = new NFAState({ accepting: true, }); A.addTransition(EPSILON, C); A.addTransition('b', B); B.addTransition('c', C); const nfa = new NFA(A, C); expect(nfa.getAcceptingStates()).toContain(B); expect(nfa.getAcceptingStates()).toContain(C); expect(nfa.getAcceptingStateNumbers()).toEqual(new Set([2, 3])); }); it('transition table', () => { const nfa = getDefaultNFA(); expect(nfa.getTransitionTable()).toEqual({ 1: {'ε*': [1, 2, 5]}, 2: {'x': [3], 'ε*': [2]}, 3: {'ε*': [3, 4]}, 4: {'ε*': [4]}, 5: {'y': [6], 'ε*': [5]}, 6: {'ε*': [6, 4]}, }); }); it('matches', () => { const nfa = getDefaultNFA(); expect(nfa.matches('x')).toBe(true); expect(nfa.matches('y')).toBe(true); expect(nfa.matches('z')).toBe(false); expect(nfa.matches('')).toBe(false); }); });regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/builders.js000066400000000000000000000071471361507317500254010ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NFA = require('./nfa'); const NFAState = require('./nfa-state'); const {EPSILON} = require('../special-symbols'); // ----------------------------------------------------------------------------- // Char NFA fragment: `c` /** * Char factory. * * Creates an NFA fragment for a single char. * * [in] --c--> [out] */ function char(c) { const inState = new NFAState(); const 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, ...fragments) { for (let fragment of fragments) { first = altPair(first, fragment); } return first; } // ----------------------------------------------------------------------------- // Disjunction NFA fragment: `a|b` /** * Creates a disjunction choice between two fragments. */ function orPair(first, second) { const inState = new NFAState(); const 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, ...fragments) { for (let fragment of fragments) { first = orPair(first, fragment); } return first; } // ----------------------------------------------------------------------------- // Kleene-closure /** * Kleene star/closure. * * a* */ function repExplicit(fragment) { const inState = new NFAState(); const 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, char, e, or, rep, repExplicit, plusRep, questionRep, }; regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/nfa-from-regexp.js000066400000000000000000000033701361507317500265570ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const parser = require('../../../parser'); const {alt, char, or, rep, plusRep, questionRep} = require('./builders'); /** * 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. */ const generator = { RegExp(node) { if (node.flags !== '') { throw new Error(`NFA/DFA: Flags are not supported yet.`); } return gen(node.body); }, Alternative(node) { const fragments = (node.expressions || []).map(gen); return alt(...fragments); }, Disjunction(node) { return or(gen(node.left), gen(node.right)); }, 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(node) { if (node.kind !== 'simple') { throw new Error(`NFA/DFA: Only simple chars are supported yet.`); } return char(node.value); }, Group(node) { return gen(node.expression); }, }; module.exports = { /** * Builds an NFA from the passed regexp. */ build(regexp) { let ast = regexp; if (regexp instanceof RegExp) { regexp = `${regexp}`; } if (typeof regexp === 'string') { ast = parser.parse(regexp, { captureLocations: true, }); } return gen(ast); }, }; regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/nfa-state.js000066400000000000000000000050711361507317500254440ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const State = require('../state'); const {EPSILON} = require('../special-symbols'); /** * NFA state. * * Allows nondeterministic transitions to several states on the * same symbol, and also epsilon-transitions. */ class NFAState extends State { /** * 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. */ matches(string, visited = 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. for (const nextState of this.getTransitionsOnSymbol(EPSILON)) { if (nextState.matches('', visited)) { return true; } } return false; } // Else, we get some symbols. const symbol = string[0]; const rest = string.slice(1); const symbolTransitions = this.getTransitionsOnSymbol(symbol); for (const nextState of symbolTransitions) { 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`). for (const nextState of this.getTransitionsOnSymbol(EPSILON)) { if (nextState.matches(string, visited)) { return true; } } return false; } /** * Returns an ε-closure for this state: * self + all states following ε-transitions. */ getEpsilonClosure() { if (!this._epsilonClosure) { const epsilonTransitions = this.getTransitionsOnSymbol(EPSILON); const closure = this._epsilonClosure = new Set(); closure.add(this); for (const nextState of epsilonTransitions) { if (!closure.has(nextState)) { closure.add(nextState); const nextClosure = nextState.getEpsilonClosure(); nextClosure.forEach(state => closure.add(state)); } } } return this._epsilonClosure; } } module.exports = NFAState;regexp-tree-0.1.18/src/interpreter/finite-automaton/nfa/nfa.js000066400000000000000000000062561361507317500243340ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {EPSILON, EPSILON_CLOSURE} = require('../special-symbols'); /** * 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. */ class NFA { constructor(inState, outState) { this.in = inState; this.out = outState; } /** * Tries to recognize a string based on this NFA fragment. */ matches(string) { return this.in.matches(string); } /** * Returns an alphabet for this NFA. */ getAlphabet() { if (!this._alphabet) { this._alphabet = new Set(); const table = this.getTransitionTable(); for (const state in table) { const transitions = table[state]; for (const symbol in transitions) if (symbol !== EPSILON_CLOSURE) { this._alphabet.add(symbol); } } } return this._alphabet; } /** * Returns set of accepting states. */ getAcceptingStates() { if (!this._acceptingStates) { // States are determined during table construction. this.getTransitionTable(); } return this._acceptingStates; } /** * Returns accepting state numbers. */ getAcceptingStateNumbers() { if (!this._acceptingStateNumbers) { this._acceptingStateNumbers = new Set(); for (const acceptingState of this.getAcceptingStates()) { this._acceptingStateNumbers.add(acceptingState.number); } } return this._acceptingStateNumbers; } /** * Builds and returns transition table. */ getTransitionTable() { if (!this._transitionTable) { this._transitionTable = {}; this._acceptingStates = new Set(); const visited = new Set(); const symbols = new Set(); const 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); } const transitions = state.getTransitions(); for (const [symbol, symbolTransitions] of transitions) { let combinedState = []; symbols.add(symbol); for (const nextState of symbolTransitions) { visitState(nextState); combinedState.push(nextState.number); } this._transitionTable[state.number][symbol] = combinedState; } }; // Traverse the graph starting from the `in`. visitState(this.in); // Append epsilon-closure column. visited.forEach(state => { delete this._transitionTable[state.number][EPSILON]; this._transitionTable[state.number][EPSILON_CLOSURE] = [ ...state.getEpsilonClosure(), ].map(s => s.number); }); } return this._transitionTable; } } module.exports = NFA; regexp-tree-0.1.18/src/interpreter/finite-automaton/special-symbols.js000066400000000000000000000004611361507317500261220ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * Epsilon, the empty string. */ const EPSILON = 'ε'; /** * Epsilon-closure. */ const EPSILON_CLOSURE = `${EPSILON}*`; module.exports = { EPSILON, EPSILON_CLOSURE, };regexp-tree-0.1.18/src/interpreter/finite-automaton/state.js000066400000000000000000000021141361507317500241310ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A generic FA State class (base for NFA and DFA). * * Maintains the transition map, and the flag whether * the state is accepting. */ class State { constructor({ accepting = false, } = {}) { /** * Outgoing transitions to other states. */ this._transitions = new Map(); /** * Whether the state is accepting. */ this.accepting = accepting; } /** * Returns transitions for this state. */ getTransitions() { return this._transitions; } /** * Creates a transition on symbol. */ addTransition(symbol, toState) { this.getTransitionsOnSymbol(symbol).add(toState); return this; } /** * Returns transitions set on symbol. */ getTransitionsOnSymbol(symbol) { let transitions = this._transitions.get(symbol); if (!transitions) { transitions = new Set(); this._transitions.set(symbol, transitions); } return transitions; } } module.exports = State;regexp-tree-0.1.18/src/optimizer/000077500000000000000000000000001361507317500166515ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/README.md000066400000000000000000000076011361507317500201340ustar00rootroot00000000000000# regexp-tree: Optimizer module ## Overview Optimizer transforms your regexp into an _optimized_ version, replacing some sub-expressions with their idiomatic patterns. Advantages: - Optimized regexps are smaller -- good for minification. - Optimized regexps may be easier to read. - Some optimizations will reduce your risk of REDOS. Example: ```js /[a-zA-Z_0-9][A-Z_\da-z]*\e{1,}/ ``` becomes: ```js /\w+e+/ ``` ## API `optimize(regexp, [transformsWhitelist])`: Optimize the regexp. Optionally request specific transforms. Transforms will be applied until no further optimization progress is made. If you wish to specify a whitelist, give an array of transform names from the table below. ```js const optimizer = require('./index.js'); const inefficient = /[0-9]/; const optimized1 = optimizer.optimize(inefficient); const optimized2 = optimizer.optimize(inefficient, [ 'charClassToMeta', // [0-9] -> [\d] 'charClassToSingleChar', // [\d] -> \d ]); console.log(`${inefficient} -> ${optimized1} === ${optimized2}`); ``` ### Transforms Here is the list of transforms supported by the Optimizer module. | Transform name | Description | Example | |-------------------------------------------------------|----------------------------------------------------------------|-------------------------------| | charSurrogatePairToSingleUnicode | Unicode pairs to single Unicode char | `\ud83d\ud380` -> `\u{1f680}` | | charCodeToSimpleChar | Don't use fancy char codes unless we have to | `\u0061` -> `a` | | charCaseInsensitiveLowerCaseTransform | If regex is case insensitive, use lower-case everywhere | `/Aa/i` -> `/aa/i` | | charClassRemoveDuplicates | Remove duplicates from char classes | `[\d\d]` -> `[\d]` | | quantifiersMerge | Merge quantifiers where possible | `a{1,2}a{2,3}` -> `a{3,5}` | | quantifierRangeToSymbol | Reduce visual of quantifier ranges | `a{1,}` -> `a+` | | charClassClassrangesToChars | Replace char class ranges with chars | `[a-a]` -> `[a]` | | charClassClassrangesMerge | Merge adjacent class ranges | `[a-de-f]` -> `[a-f]` | | charClassToMeta | Use meta-chars like `\d` and \`w` where possible | `[0-9]` -> `[\d]` | | charClassToSingleChar | Replace a char class with a single meta-char where possible | `[\d]` -> `\d` | | charEscapeUnescape | Remove unnecessary escapes | `\e` -> `e` | | disjunctionRemoveDuplicates\* | Remove duplicate disjunctions | `(ab\|ab)` -> `(ab)` | | groupSingleCharsToCharClass\* | Reduce disjunction complexity | `(a\|b\|c)` -> `[abc]` | | removeEmptyGroup | Remove empty groups | `(?:)a` -> `a` | | ungroup | Remove unnecessary groups | `(?:a)` -> `a` | | combineRepeatingPatterns | Replace repetition with quantifiers where possible | `abcabcabc` -> `(?:abc){3}` | \*: May reduce the risk of REDOS in your regexes. regexp-tree-0.1.18/src/optimizer/__tests__/000077500000000000000000000000001361507317500206075ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/__tests__/optimizer-integration-test.js000066400000000000000000000063271361507317500264750ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const optimizer = require('..'); describe('optimizer-integration-test', () => { it('optimizes a regexp', () => { const original = /[0-90-9a-zA-Z_$][0-9a-zA-Za-z_$]*/; const optimized = /[\w$]+/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('preserves dash', () => { let original = '/[^a-zа-яё -]+gi/'; let optimized = '/[^ -a-zа-яё]+gi/'; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); original = '/[0-9-a-z]/'; optimized = '/[\\d-a-z]/'; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('handles a named capturing group', () => { const original = '/(?.*)/'; const optimized = '/(?.*)/'; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('to single char', () => { const original = /[0-90-9a-zA-Z_][0-9a-zA-Za-z_]*/; const optimized = /\w+/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('preserve escape', () => { const original = /^[\^\*\$\(\)]\^\*\$\(\)\.\[\/\\$/; const optimized = /^[$()*^]\^\*\$\(\)\.\[\/\\$/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('whitespace', () => { const original = /[ \n\r\t\f]+/; const optimized = /\s+/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('quantifier {1,}', () => { const original = /[a-z0-9A-Za-z_]{1,}/; const optimized = /\w+/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('quantifier {1}', () => { const original = /[a-z0-9A-Za-z_]{1}/; const optimized = /\w/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('quantifier {3,3}', () => { const original = /[a-z0-9A-Za-z_]{3,3}/; const optimized = /\w{3}/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('quantifier other', () => { const original = /[a-z0-9A-Za-z_]{1,3}/; const optimized = /\w{1,3}/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('finds the best optimization', () => { const original = /a|a/; const optimized = /a/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('applies whitelist only', () => { const original = '/(?:[a])/'; const optimized = '/[a]/'; expect( optimizer.optimize(original, {whitelist: ['ungroup']}).toString() ).toBe(optimized.toString()); }); it('sorts characters', () => { const original = /[åä]/; const optimized = /[äå]/; expect(optimizer.optimize(original).toString()).toBe(optimized.toString()); }); it('preserves character order when blacklisted charClassClassrangesMerge', () => { const original = /[åä]/; const optimized = /[åä]/; expect( optimizer .optimize(original, {blacklist: ['charClassClassrangesMerge']}) .toString() ).toBe(optimized.toString()); }); }); regexp-tree-0.1.18/src/optimizer/babel/000077500000000000000000000000001361507317500177165ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/babel/README.md000066400000000000000000000010771361507317500212020ustar00rootroot00000000000000# regexp-tree: babel-plugin-transform-regexp-optimizer TODO Applies [regexp-tree](https://www.npmjs.com/package/regexp-tree) optimizations on JavaScript regular expressions. ## Examples ```js /[a-zA-Z_0-9][A-Z_\da-z]*\e{1,}/ ``` Is transformed into: ```js /\w+e+/ ``` ## Usage ### Via `.babelrc` `.babelrc` ```json { "plugins": ["transform-regexp-optimizer"] } ``` ### Via CLI ```sh $ babel --plugins transform-regexp-optimizer script.js ``` ### Via Node.js API ```js require('babel-core').transform(code, { 'plugins': ['transform-regexp-optimizer'] }); ```regexp-tree-0.1.18/src/optimizer/index.js000066400000000000000000000050201361507317500203130ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const clone = require('../utils/clone'); const parser = require('../parser'); const transform = require('../transform'); const 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(regexp, {whitelist = [], blacklist = []} = {}) { const transformsRaw = whitelist.length > 0 ? whitelist : Array.from(optimizationTransforms.keys()); const transformToApply = transformsRaw.filter( transform => !blacklist.includes(transform) ); let ast = regexp; if (regexp instanceof RegExp) { regexp = `${regexp}`; } if (typeof regexp === 'string') { ast = parser.parse(regexp); } let result = new transform.TransformResult(ast); let prevResultString; 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(transformName => { if (!optimizationTransforms.has(transformName)) { throw new Error( `Unknown optimization-transform: ${transformName}. ` + `Available transforms are: ` + Array.from(optimizationTransforms.keys()).join(', ') ); } const transformer = optimizationTransforms.get(transformName); // Don't override result just yet since we // might want to rollback the transform let 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; }, }; regexp-tree-0.1.18/src/optimizer/transforms/000077500000000000000000000000001361507317500210475ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/transforms/__tests__/000077500000000000000000000000001361507317500230055ustar00rootroot00000000000000char-case-insensitive-lowercase-transform-test.js000066400000000000000000000051201361507317500344160ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/transforms/__tests__/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charCaseInsensitiveLowercase = require('../char-case-insensitive-lowercase-transform'); describe('char-case-insensitive-lowercase', () => { it('lowercases chars when i flag is set', () => { let re = transform(/aA[bB]Ï/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/aa[bb]ï/i'); re = transform(/\65/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\97/i'); re = transform(/\x41/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\x61/i'); re = transform(/\u0041/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\u0061/i'); re = transform('/\\u{41}/iu', [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\u{61}/iu'); }); it('lowercases char >= \\u1000 when u flag is set', () => { const re = transform('/\\u10a0/iu', [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\u2d00/iu'); }); it('does not lowercase char >= \\u1000 without u flag set', () => { const re = transform(/\u10a0/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/\\u10a0/i'); }); it('lowercases chars in A-Z class ranges when i flag is set', () => { let re = transform(/[A-ZB-H]/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[a-zb-h]/i'); re = transform(/[\65-\90\66-\72]/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[\\97-\\122\\98-\\104]/i'); re = transform(/[\x41-\x5a\x42-\x48]/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[\\x61-\\x7a\\x62-\\x68]/i'); re = transform(/[\u0041-\u005a\u0042-\u0048]/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[\\u0061-\\u007a\\u0062-\\u0068]/i'); re = transform('/[\\u{41}-\\u{5a}\\u{42}-\\u{48}]/iu', [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[\\u{61}-\\u{7a}\\u{62}-\\u{68}]/iu'); }); it('does not run when i flag is absent', () => { const re = transform(/aA[bB]Ï/, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/aA[bB]Ï/'); }); it('ignores class ranges outside A-Z', () => { const re = transform(/[0-BA-z]/i, [ charCaseInsensitiveLowercase, ]); expect(re.toString()).toBe('/[0-BA-z]/i'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-class-classranges-merge-transform-test.js000066400000000000000000000107721361507317500337600ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charClassClassrangesMerge = require('../char-class-classranges-merge-transform'); describe('char-class-classranges-merge', () => { it('merges chars in meta', () => { let re = transform(/[\wa_z]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\w]/'); re = transform(/[\d1509]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\d]/'); re = transform(/[\s\t\u2000 ]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\s]/'); }); it('merges chars in \w with i and u flags', () => { const re = transform('/[\\u212a\\w\\u017fa]/iu', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\w]/iu'); }); it('merges class ranges in meta', () => { let re = transform(/[\wa-dh-u]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\w]/'); re = transform(/[\d1-5]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\d]/'); re = transform(/[\s\u2000-\u2007]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\s]/'); }); it('merges meta in meta', () => { let re = transform(/[\w\d]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\w]/'); re = transform(/[\W\D\s]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\D]/'); re = transform(/[\W\s]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\W]/'); re = transform(/[\w\S\d]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\S]/'); }); it('merges \\w chars and char codes in class ranges', () => { let re = transform(/[fb-eg]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[b-g]/'); re = transform(/[bva-g15dt-z0-7]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[0-7a-gt-z]/'); re = transform(/[\u0024-\u0035\u0027]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\u0024-\\u0035]/'); re = transform('/[\\u{24}-\\u{35}\\u0027\\u{28}]/u', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\u{24}-\\u{35}]/u'); re = transform('/[\\ud83d\\ude80-\\ud83d\\ude88\\ud83d\\ude83]/u', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\ud83d\\ude80-\\ud83d\\ude88]/u'); }); it('does not merge non \\w chars into class ranges to keep readability', () => { let re = transform(/[@A-E]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[@A-E]/'); }); it('merges ranges together', () => { let re = transform(/[a-fc-u0-56-8]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[0-8a-u]/'); re = transform(/[\u0024-\u0035\u0036-\u0042]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\u0024-\\u0042]/'); re = transform('/[\\u{24}-\\u{35}\\u0036-\\u0042]/u', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\u{24}-\\u0042]/u'); re = transform( '/[\\u{1F680}-\\ud83d\\ude88\\ud83d\\ude89-\\ud83d\\ude9b]/u', [ charClassClassrangesMerge, ] ); expect(re.toString()).toBe('/[\\u{1F680}-\\ud83d\\ude9b]/u'); }); it('combines \\w sequential chars and char codes into class ranges', () => { let re = transform(/[facbdemlpqno]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[a-fl-q]/'); re = transform(/[\u0014\u0015\u0016]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\u0014-\\u0016]/'); re = transform(/[a\u0062c\u0064]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[a-\\u0064]/'); re = transform('/[a\\u{62}c\\u{64}]/u', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[a-\\u{64}]/u'); re = transform('/[\\ud83d\\ude88\\ud83d\\ude89\\ud83d\\ude8a]/u', [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[\\ud83d\\ude88-\\ud83d\\ude8a]/u'); }); it('does not combine sequential chars that are nor \\w chars nor char codes', () => { const re = transform(/[<=>?]/, [ charClassClassrangesMerge, ]); expect(re.toString()).toBe('/[<=>?]/'); }); });char-class-classranges-to-chars-transform-test.js000066400000000000000000000012041361507317500343100ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/transforms/__tests__/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charClassClassrangesToChars = require('../char-class-classranges-to-chars-transform'); describe('char-class-classranges-to-chars', () => { it('[a-a] -> [a]', () => { const re = transform('/[a-a]/', [ charClassClassrangesToChars, ]); expect(re.toString()).toBe('/[a]/'); }); it('[a-b] -> [ab]', () => { const re = transform('/[a-b]/', [ charClassClassrangesToChars, ]); expect(re.toString()).toBe('/[ab]/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-class-remove-duplicates-transform-test.js000066400000000000000000000010141361507317500337730ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charClassRemoveDuplicates = require('../char-class-remove-duplicates-transform'); describe('char-class-remove-duplicates', () => { it('removes duplicates', () => { const re = transform('/[0-90-9\\d\\daba\\w\\n\\w$\\\]]/', [ charClassRemoveDuplicates, ]); expect(re.toString()).toBe('/[0-9\\dab\\w\\n$\\\]]/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-class-to-meta-transform-test.js000066400000000000000000000022141361507317500317140ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charClassToMeta = require('../char-class-to-meta-transform'); describe('char-class-to-meta', () => { it('replaces number ranges', () => { const re = transform(/[0-9$]/, [ charClassToMeta, ]); expect(re.toString()).toBe('/[\\d$]/'); }); it('replaces word ranges', () => { const re = transform(/[0-9a-zA-Z_$]/, [ charClassToMeta, ]); expect(re.toString()).toBe('/[\\w$]/'); }); it('replaces word ranges when regexp has i flag', () => { const re = transform(/[0-9a-z_$]/i, [ charClassToMeta, ]); expect(re.toString()).toBe('/[\\w$]/i'); }); it('replaces word ranges when regexp has i and u flags', () => { const re = transform('/[\\da-zA-Z_\\u017F\\u212A$]/iu', [ charClassToMeta, ]); expect(re.toString()).toBe('/[\\w$]/iu'); }); it('whitespace ranges', () => { const re = transform(/[ \t\r\n\f]/, [ charClassToMeta, ]); expect(re.toString()).toBe('/[\\s]/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-class-to-single-char-transform-test.js000066400000000000000000000041271361507317500331670ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charClassToSingleChar = require('../char-class-to-single-char-transform'); describe('char-class-to-single-char', () => { it('simple char', () => { const re = transform('/[a]/', [charClassToSingleChar]); expect(re.toString()).toBe('/a/'); }); it('only one char', () => { const re = transform('/[ab]/', [charClassToSingleChar]); expect(re.toString()).toBe('/[ab]/'); }); it('escaped char', () => { const re = transform('/[\\a]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\a/'); }); it('should escape', () => { const re = transform('/[(]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\(/'); }); it('meta', () => { let re = transform('/[\\n]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\n/'); re = transform('/[\\d]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\d/'); }); it('backspace', () => { const re = transform('/[\\b]/', [charClassToSingleChar]); // Stays the same, since \b would have different semantics. expect(re.toString()).toBe('/[\\b]/'); }); it('inverse meta', () => { let re = transform('/[^\\d]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\D/'); re = transform('/[^\\w]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\W/'); re = transform('/[^\\W]/', [charClassToSingleChar]); expect(re.toString()).toBe('/\\w/'); }); it('do not extract simple negative', () => { const re = transform('/[^a]/', [charClassToSingleChar]); expect(re.toString()).toBe('/[^a]/'); }); it('does not extract with reference sibling', () => { const re = transform('/(.)\\1[0]/', [charClassToSingleChar]); expect(re.toString()).toBe('/(.)\\1[0]/'); }); it('does not extract with char number sibling', () => { const re = transform('/(.)\\2[0]/', [charClassToSingleChar]); expect(re.toString()).toBe('/(.)\\2[0]/'); }); }); regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-code-to-simple-char-transform-test.js000066400000000000000000000061511361507317500330030ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charCodeToSimpleChar = require('../char-code-to-simple-char-transform'); describe('char-code-to-simple-char', () => { it('converts coded chars to simple chars', () => { let re = transform(/\u0041\u0020\u007e[\u0042-\u004c\u006d-\u0072]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/A ~[B-Lm-r]/'); re = transform('/\\u{41}\\u{20}\\u{7e}[\\u{42}-\\u{4c}\\u{6d}-\\u{72}]/u', [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/A ~[B-Lm-r]/u'); re = transform(/\x41\x20\x7e[\x42-\x4c\x6d-\x72]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/A ~[B-Lm-r]/'); re = transform(/\040\071[\061-\065]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/ 9[1-5]/'); re = transform(/\65\32\126[\66-\76\109-\114]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/A ~[B-Lm-r]/'); }); it('does not convert coded chars outside of ASCII printable range', () => { let re = transform(/\u0016\u00a9/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\u0016\\u00a9/'); re = transform('/\\u{16}\\u{a9}/u', [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\u{16}\\u{a9}/u'); re = transform(/\x16\xa9/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\x16\\xa9/'); re = transform(/\026/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\026/'); re = transform(/\22\169/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\22\\169/'); }); it('does not convert class ranges other than included in 0-9, a-z or A-Z', () => { let re = transform(/[\u005a-\u0061]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\u005a-\\u0061]/'); re = transform('/[\\u{5a}-\\u{61}]/u', [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\u{5a}-\\u{61}]/u'); re = transform(/[\x5a-\x61]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\x5a-\\x61]/'); re = transform(/[\054-\061]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\054-\\061]/'); re = transform(/[\90-\97]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\90-\\97]/'); }); it('escapes converted chars when needed', () => { let re = transform(/[\055]/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\-]/'); re = transform('/[\\u005d\\u{5c}\\x5e]/u', [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/[\\]\\\\\\^]/u'); re = transform(/\052\43\077\47\125/, [ charCodeToSimpleChar, ]); expect(re.toString()).toBe('/\\*\\+\\?\\/\\}/'); re = transform( '/\\u005b\\u{28}\\x29\\u005e\\u{24}\\x2e\\u005c\\u{7c}\\x7b/u', [ charCodeToSimpleChar, ] ); expect(re.toString()).toBe('/\\[\\(\\)\\^\\$\\.\\\\\\|\\{/u'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/char-escape-unescape-transform-test.js000066400000000000000000000062201361507317500323050ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charUnescape = require('../char-escape-unescape-transform'); describe('e -> e', () => { it('simple chars', () => { const re = transform(/\e\*\]/, [charUnescape]); expect(re.toString()).toBe(/e\*]/.toString()); }); it('preserve escape', () => { const re = transform(/\*\^\$\(\)\[\|/, [charUnescape]); expect(re.toString()).toBe(/\*\^\$\(\)\[\|/.toString()); }); it('unescapes curly braces', () => { const re = transform(/\{\}/, [charUnescape]); expect(re.toString()).toBe(/{}/.toString()); }); it('preserves dash', () => { let re = transform('/[^a-zа-яё -]+gi/', [charUnescape]); expect(re.toString()).toBe('/[^a-zа-яё -]+gi/'); re = transform('/[0-9-a-z]/', [charUnescape]); expect(re.toString()).toBe('/[0-9-a-z]/'); }); it('unescapes curly in parent with no index', () => { let re = transform(/(?:\{)/, [charUnescape]); expect(re.toString()).toBe(/(?:{)/.toString()); re = transform(/(?:\})/, [charUnescape]); expect(re.toString()).toBe(/(?:})/.toString()); re = transform(/\{/, [charUnescape]); expect(re.toString()).toBe(/{/.toString()); re = transform(/\}/, [charUnescape]); expect(re.toString()).toBe(/}/.toString()); re = transform(/\{|v/, [charUnescape]); expect(re.toString()).toBe(/{|v/.toString()); re = transform(/\}|v/, [charUnescape]); expect(re.toString()).toBe(/}|v/.toString()); }); it('does not unescape { when looking like a quantifier', () => { let re = transform(/a\{3}/, [charUnescape]); expect(re.toString()).toBe(/a\{3}/.toString()); re = transform(/a\{3,}/, [charUnescape]); expect(re.toString()).toBe(/a\{3,}/.toString()); re = transform(/a\{10,12}/, [charUnescape]); expect(re.toString()).toBe(/a\{10,12}/.toString()); }); it('does not unescape } when looking like a quantifier', () => { let re = transform(/a{3\}/, [charUnescape]); expect(re.toString()).toBe(/a{3\}/.toString()); re = transform(/a{3,\}/, [charUnescape]); expect(re.toString()).toBe(/a{3,\}/.toString()); re = transform(/a{10,12\}/, [charUnescape]); expect(re.toString()).toBe(/a{10,12\}/.toString()); }); it('char class', () => { const re = transform(/[\e\*\(\]\ \^\$\/-\?\-]\(\n/, [charUnescape]); // Can't use native toString() conversion on a regexp here // because Node 6 stringifies /[/]/ as /[\/]/ expect(re.toString()).toBe('/[e*(\\] ^$/-?-]\\(\\n/'); }); it('does not unescape ^ in char class when in first position', () => { const re = transform(/[\^a]/, [charUnescape]); expect(re.toString()).toBe(/[\^a]/.toString()); }); it('does not unescape - in char class when not in first or last position', () => { const re = transform(/[a\-z]/, [charUnescape]); expect(re.toString()).toBe(/[a\-z]/.toString()); }); it('does not unescape space and # when x flag is set', () => { const re = transform('/\\ \\#[\\ \\#]/x', [charUnescape]); expect(re.toString()).toBe('/\\ \\#[ #]/x'); }); }); char-surrogate-pair-to-single-unicode-transform-test.js000066400000000000000000000013441361507317500354560ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/transforms/__tests__/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const charSurrogatePairToSingleUnicode = require('../char-surrogate-pair-to-single-unicode-transform'); describe('\\ud83d\\ude80 -> \\u{1f680}', () => { it('\\ud83d\\ude80 -> \\u{1f680}', () => { const re = transform('/\\ud83d\\ude80/u', [ charSurrogatePairToSingleUnicode ]); expect(re.toString()).toBe('/\\u{1f680}/u'); }); it('does not run when unicode flag is absent', () => { const re = transform('/\\ud83d\\ude80/', [ charSurrogatePairToSingleUnicode ]); expect(re.toString()).toBe('/\\ud83d\\ude80/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/combine-repeating-patterns-transform-test.js000066400000000000000000000034521361507317500335630ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const combineRepeatingPatterns = require('../combine-repeating-patterns-transform'); describe('abcdefabcdef -> (?:abcdef){2}', () => { it('combines repeating patterns', () => { const re = transform(/abcdefabcdef/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:abcdef){2}/'); }); it('combines complex repeating patterns', () => { const re = transform(/a{5,}[\d\W]{3}a{5,}[\d\W]{3}/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:a{5,}[\\d\\W]{3}){2}/'); }); it('combines multiple repeating patterns', () => { const re = transform(/(?:a|bc)(?:a|bc)(?:a|bc)defdefdef/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:a|bc){3}(?:def){3}/'); }); }); describe('(?:abc){4}abc -> (?:abc){5}', () => { it('combines pattern with repetition on the left', () => { let re = transform(/(?:abc){4}abc/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:abc){5}/'); re = transform(/(?:abc)+abc/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:abc){2,}/'); }); }); describe('abc(?:abc){4} -> (?:abc){5}', () => { it('combines repetition with pattern on the left', () => { const re = transform(/abc(?:abc){4}/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:abc){5}/'); }); }); describe('abc(?:abc){4}abcabc -> (?:abc){7}', () => { it('combine all those repeating patterns at once', () => { const re = transform(/abc(?:abc){4}abcabc/, [ combineRepeatingPatterns ]); expect(re.toString()).toBe('/(?:abc){7}/'); }); });disjunction-remove-duplicates-transform-test.js000066400000000000000000000027731361507317500342420ustar00rootroot00000000000000regexp-tree-0.1.18/src/optimizer/transforms/__tests__/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const disjunctionRemoveDuplicates = require('../disjunction-remove-duplicates-transform'); describe('(ab|bc|ab) -> (ab|bc)', () => { it('removes duplicates from disjunction', () => { const re = transform(/ab|bc|ab/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/ab|bc/'); }); it('preserves order', () => { const re = transform(/bc|bc|ab/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/bc|ab/'); }); it('replaces disjunction containing only one part', () => { const re = transform(/ab|ab/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/ab/'); }); it('does not merge capturing groups', () => { const re = transform(/(ab)|(ab)/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/(ab)|(ab)/'); }); it('handles recursion', () => { const re = transform(/(ab|bc|ab)|(bc|cd|bc)/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/(ab|bc)|(bc|cd)/'); const re2 = transform(/(?:ab|bc|ab)|(?:ab|bc|ab)/, [ disjunctionRemoveDuplicates ]); expect(re2.toString()).toBe('/(?:ab|bc)/'); }); it('handles empty parts', () => { const re = transform(/a|b|||/, [ disjunctionRemoveDuplicates ]); expect(re.toString()).toBe('/a|b|/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/group-single-chars-to-char-class-test.js000066400000000000000000000035301361507317500324700ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const singleCharsGroupToCharClass = require('../group-single-chars-to-char-class'); describe('(a|b|c) -> ([abc])', () => { it('replaces single chars disjunction to char class', () => { const re = transform(/a|b|c/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/[abc]/'); }); it('merges single chars disjunction with character class', () => { const re = transform(/[43]|a|b|[53]|c|[9]/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/[3459abc]/'); }); it('replaces single chars group to char class', () => { const re = transform(/(a|b|c)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/([abc])/'); }); it('works with metachars too', () => { const re = transform(/(\d|\n)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe(/([\d\n])/.toString()); }); it('merges duplicates in group', () => { const re = transform(/(a|b|a|b)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/([ab])/'); }); it('removes non-capturing group', () => { const re = transform(/(?:a|b|c)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/[abc]/'); }); it('have no effect on multichar values', () => { const re = transform(/(a|b|no)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/(a|b|no)/'); }); it('has no effet on empty values', () => { const re = transform(/(a|b|)/, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/(a|b|)/'); }); it('does not merge in negative class', () => { const re = transform(/(_|[^\s\w])/g, [singleCharsGroupToCharClass]); expect(re.toString()).toBe('/(_|[^\\s\\w])/g'); }); }); regexp-tree-0.1.18/src/optimizer/transforms/__tests__/quantifier-range-to-symbol-transform-test.js000066400000000000000000000021261361507317500335160ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const quantifierRangeToSymbol = require('../quantifier-range-to-symbol-transform'); describe('quantifier range to symbol', () => { it('a{0,} -> a*', () => { const re = transform(/[a-z]{0,}/, [ quantifierRangeToSymbol, ]); expect(re.toString()).toBe('/[a-z]*/'); }); it('a{1,} -> a+', () => { const re = transform(/[a-z]{1,}/, [ quantifierRangeToSymbol, ]); expect(re.toString()).toBe('/[a-z]+/'); }); it('a{1} -> a', () => { const re = transform('/[a-z]{1}/', [ quantifierRangeToSymbol, ]); expect(re.toString()).toBe('/[a-z]/'); }); it('a{1,1} -> a', () => { const re = transform('/[a-z]{1,1}/', [ quantifierRangeToSymbol, ]); expect(re.toString()).toBe('/[a-z]/'); }); it('a{3,3} -> a{3}', () => { const re = transform('/[a-z]{3,3}/', [ quantifierRangeToSymbol, ]); expect(re.toString()).toBe('/[a-z]{3}/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/quantifiers-merge-transform-test.js000066400000000000000000000526071361507317500317720ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const quantifiersMerge = require('../quantifiers-merge-transform'); describe('merge quantifiers with simple previous sibling', () => { it('aa* -> a+', () => { const re = transform(/aa*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a+/'); const re2 = transform(/aa*?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a+?/'); }); it('aa+ -> a{2,}', () => { const re = transform(/aa+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/aa+?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?/'); }); it('aa? -> a{1,2}', () => { const re = transform(/aa?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}/'); const re2 = transform(/aa??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,2}?/'); }); it('aa{3} -> a{4}', () => { const re = transform(/aa{3}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4}/'); const re2 = transform(/aa{3}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4}?/'); }); it('aa{3,} -> a{4,}', () => { const re = transform(/aa{3,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}/'); const re2 = transform(/aa{3,}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,}?/'); }); it('aa{3,5} -> a{4,6}', () => { const re = transform(/aa{3,5}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,6}/'); const re2 = transform(/aa{3,5}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,6}?/'); }); }); describe('merge closed greedy quantifier with closed greedy quantifier', () => { it('a?a?', () => { const re = transform(/a?a?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,2}/'); }); it('a?a{2}', () => { const re = transform(/a?a{2}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,3}/'); const re2 = transform(/a{2}a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,3}/'); }); it('a?a{1,2}', () => { const re = transform(/a?a{1,2}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,3}/'); const re2 = transform(/a{1,2}a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,3}/'); }); it('a{2}a{2}', () => { const re = transform(/a{2}a{2}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4}/'); }); it('a{2}a{1,2}', () => { const re = transform(/a{2}a{1,2}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,4}/'); const re2 = transform(/a{1,2}a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,4}/'); }); it('a{1,2}a{1,2}', () => { const re = transform(/a{1,2}a{1,2}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,4}/'); }); }); describe('do not merge closed greedy quantifier with closed ungreedy quantifier', () => { it('a?a??', () => { const re = transform(/a?a??/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a??/'); const re2 = transform(/a??a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a??a?/'); }); it('a?a{2}?', () => { const re = transform(/a?a{2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a{2}?/'); const re2 = transform(/a{2}?a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2}?a?/'); }); it('a?a{1,2}?', () => { const re = transform(/a?a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a{1,2}?/'); const re2 = transform(/a{1,2}?a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,2}?a?/'); }); it('a{2}a??', () => { const re = transform(/a{2}a??/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a??/'); const re2 = transform(/a??a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a??a{2}/'); }); it('a{2}a{2}?', () => { const re = transform(/a{2}a{2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a{2}?/'); const re2 = transform(/a{2}?a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2}?a{2}/'); }); it('a{2}a{1,2}?', () => { const re = transform(/a{2}a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a{1,2}?/'); const re2 = transform(/a{1,2}?a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,2}?a{2}/'); }); it('a{1,2}a??', () => { const re = transform(/a{1,2}a??/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a??/'); const re2 = transform(/a??a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a??a{1,2}/'); }); it('a{1,2}a{2}?', () => { const re = transform(/a{1,2}a{2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a{2}?/'); const re2 = transform(/a{2}?a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2}?a{1,2}/'); }); it('a{1,2}a{1,2}?', () => { const re = transform(/a{1,2}a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a{1,2}?/'); const re2 = transform(/a{1,2}?a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,2}?a{1,2}/'); }); }); describe('merge closed greedy quantifier with open greedy quantifier', () => { it('a?a+', () => { const re = transform(/a?a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a+a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a?a*', () => { const re = transform(/a?a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}/'); const re2 = transform(/a*a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{0,}/'); }); it('a?a{2,}', () => { const re = transform(/a?a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a{2,}a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2}a+', () => { const re = transform(/a{2}a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a+a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); it('a{2}a*', () => { const re = transform(/a{2}a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a*a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2}a{2,}', () => { const re = transform(/a{2}a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}/'); const re2 = transform(/a{2,}a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,}/'); }); it('a{1,2}a+', () => { const re = transform(/a{1,2}a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a+a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{1,2}a*', () => { const re = transform(/a{1,2}a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a*a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a{1,2}a{2,}', () => { const re = transform(/a{1,2}a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a{2,}a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); }); describe('do not merge closed greedy quantifier with open ungreedy quantifier', () => { it('a?a+?', () => { const re = transform(/a?a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a+?/'); const re2 = transform(/a+?a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a+?a?/'); }); it('a?a*?', () => { const re = transform(/a?a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a*?/'); const re2 = transform(/a*?a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a*?a?/'); }); it('a?a{2,}?', () => { const re = transform(/a?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a?a{2,}?/'); const re2 = transform(/a{2,}?a?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?a?/'); }); it('a{2}a+?', () => { const re = transform(/a{2}a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a+?/'); const re2 = transform(/a+?a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a+?a{2}/'); }); it('a{2}a*?', () => { const re = transform(/a{2}a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a*?/'); const re2 = transform(/a*?a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a*?a{2}/'); }); it('a{2}a{2,}?', () => { const re = transform(/a{2}a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2}a{2,}?/'); const re2 = transform(/a{2,}?a{2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?a{2}/'); }); it('a{1,2}a+?', () => { const re = transform(/a{1,2}a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a+?/'); const re2 = transform(/a+?a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a+?a{1,2}/'); }); it('a{1,2}a*?', () => { const re = transform(/a{1,2}a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a*?/'); const re2 = transform(/a*?a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a*?a{1,2}/'); }); it('a{1,2}a{2,}?', () => { const re = transform(/a{1,2}a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,2}a{2,}?/'); const re2 = transform(/a{2,}?a{1,2}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?a{1,2}/'); }); }); describe('merge closed ungreedy quantifier with closed ungreedy quantifier', () => { it('a??a??', () => { const re = transform(/a??a??/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,2}?/'); }); it('a??a{2}?', () => { const re = transform(/a??a{2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,3}?/'); const re2 = transform(/a{2}?a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,3}?/'); }); it('a??a{1,2}?', () => { const re = transform(/a??a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,3}?/'); const re2 = transform(/a{1,2}?a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,3}?/'); }); it('a{2}?a{2}?', () => { const re = transform(/a{2}?a{2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4}?/'); }); it('a{2}?a{1,2}?', () => { const re = transform(/a{2}?a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,4}?/'); const re2 = transform(/a{1,2}?a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,4}?/'); }); it('a{1,2}?a{1,2}?', () => { const re = transform(/a{1,2}?a{1,2}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,4}?/'); }); }); describe('merge closed ungreedy quantifier with open greedy quantifier', () => { it('a??a+', () => { const re = transform(/a??a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a+a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a??a*', () => { const re = transform(/a??a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}/'); const re2 = transform(/a*a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{0,}/'); }); it('a??a{2,}', () => { const re = transform(/a??a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a{2,}a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2}?a+', () => { const re = transform(/a{2}?a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a+a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); it('a{2}?a*', () => { const re = transform(/a{2}?a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a*a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2}?a{2,}', () => { const re = transform(/a{2}?a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}/'); const re2 = transform(/a{2,}a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,}/'); }); it('a{1,2}?a+', () => { const re = transform(/a{1,2}?a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a+a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{1,2}?a*', () => { const re = transform(/a{1,2}?a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a*a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a{1,2}?a{2,}', () => { const re = transform(/a{1,2}?a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a{2,}a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); }); describe('merge closed ungreedy quantifier with open ungreedy quantifier', () => { it('a??a+?', () => { const re = transform(/a??a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}?/'); const re2 = transform(/a+?a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}?/'); }); it('a??a*?', () => { const re = transform(/a??a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}?/'); const re2 = transform(/a*?a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{0,}?/'); }); it('a??a{2,}?', () => { const re = transform(/a??a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}?/'); const re2 = transform(/a{2,}?a??/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?/'); }); it('a{2}?a+?', () => { const re = transform(/a{2}?a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}?/'); const re2 = transform(/a+?a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}?/'); }); it('a{2}?a*?', () => { const re = transform(/a{2}?a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}?/'); const re2 = transform(/a*?a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?/'); }); it('a{2}?a{2,}?', () => { const re = transform(/a{2}?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}?/'); const re2 = transform(/a{2,}?a{2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,}?/'); }); it('a{1,2}?a+?', () => { const re = transform(/a{1,2}?a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}?/'); const re2 = transform(/a+?a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?/'); }); it('a{1,2}?a*?', () => { const re = transform(/a{1,2}?a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}?/'); const re2 = transform(/a*?a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}?/'); }); it('a{1,2}?a{2,}?', () => { const re = transform(/a{1,2}?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}?/'); const re2 = transform(/a{2,}?a{1,2}?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}?/'); }); }); describe('merge open greedy quantifier with open greedy quantifier', () => { it('a+a+', () => { const re = transform(/a+a+/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); }); it('a+a*', () => { const re = transform(/a+a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a*a+/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a+a{2,}', () => { const re = transform(/a+a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a{2,}a+/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); it('a*a*', () => { const re = transform(/a*a*/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}/'); }); it('a*a{2,}', () => { const re = transform(/a*a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a{2,}a*/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2,}a{2,}', () => { const re = transform(/a{2,}a{2,}/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}/'); }); }); describe('merge open greedy quantifier with open ungreedy quantifier', () => { it('a+a+?', () => { const re = transform(/a+a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a+?a+/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a+a*?', () => { const re = transform(/a+a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a*?a+/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a+a{2,}?', () => { const re = transform(/a+a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a{2,}?a+/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); it('a*a+?', () => { const re = transform(/a*a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}/'); const re2 = transform(/a+?a*/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}/'); }); it('a*a*?', () => { const re = transform(/a*a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}/'); const re2 = transform(/a*?a*/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{0,}/'); }); it('a*a{2,}?', () => { const re = transform(/a*a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a{2,}?a*/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2,}a+?', () => { const re = transform(/a{2,}a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}/'); const re2 = transform(/a+?a{2,}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}/'); }); it('a{2,}a*?', () => { const re = transform(/a{2,}a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}/'); const re2 = transform(/a*?a{2,}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}/'); }); it('a{2,}a{2,}?', () => { const re = transform(/a{2,}a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}/'); const re2 = transform(/a{2,}?a{2,}/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{4,}/'); }); }); describe('merge open ungreedy quantifier with open ungreedy quantifier', () => { it('a+?a+?', () => { const re = transform(/a+?a+?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}?/'); }); it('a+?a*?', () => { const re = transform(/a+?a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{1,}?/'); const re2 = transform(/a*?a+?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{1,}?/'); }); it('a+?a{2,}?', () => { const re = transform(/a+?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{3,}?/'); const re2 = transform(/a{2,}?a+?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{3,}?/'); }); it('a*?a*?', () => { const re = transform(/a*?a*?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{0,}?/'); }); it('a*?a{2,}?', () => { const re = transform(/a*?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{2,}?/'); const re2 = transform(/a{2,}?a*?/, [ quantifiersMerge ]); expect(re2.toString()).toBe('/a{2,}?/'); }); it('a{2,}?a{2,}?', () => { const re = transform(/a{2,}?a{2,}?/, [ quantifiersMerge ]); expect(re.toString()).toBe('/a{4,}?/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/remove-empty-group-transform-test.js000066400000000000000000000015541361507317500321210ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const removeEmptyGroup = require('../remove-empty-group-transform'); describe('remove empty groups', () => { it('removes empty groups', () => { const re = transform(/a(?:)b/, [ removeEmptyGroup ]); expect(re.toString()).toBe('/ab/'); const re2 = transform(/((?:))/, [ removeEmptyGroup ]); expect(re2.toString()).toBe('/()/'); }); it('does not remove empty regexp', () => { const re = transform(/(?:)/, [ removeEmptyGroup ]); expect(re.toString()).toBe('/(?:)/'); }); it('removes empty group quantifier', () => { const re = transform(/(?:)+/, [ removeEmptyGroup ]); expect(re.toString()).toBe('/(?:)/'); }); });regexp-tree-0.1.18/src/optimizer/transforms/__tests__/ungroup-transform-test.js000066400000000000000000000044131361507317500300320ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {transform} = require('../../../transform'); const ungroup = require('../ungroup-transform'); describe('ungroup', () => { it('ungroups simple groups', () => { const re = transform(/(?:a)/, [ungroup]); expect(re.toString()).toBe('/a/'); const re2 = transform(/a(?:[\da]b)c/, [ungroup]); expect(re2.toString()).toBe('/a[\\da]bc/'); }); it('ungroups disjunctions if top-level', () => { const re = transform(/(?:ab|bc)/, [ungroup]); expect(re.toString()).toBe('/ab|bc/'); }); it('does not ungroup other disjunctions', () => { const re = transform(/^(?:ab|bc)/, [ungroup]); expect(re.toString()).toBe('/^(?:ab|bc)/'); }); it('ungroups groups with quantifier if child is a Char', () => { const re = transform(/(?:a)+/, [ungroup]); expect(re.toString()).toBe('/a+/'); }); it('ungroups groups with quantifier if child is a CharacterClass', () => { const re = transform(/(?:[ab])+/, [ungroup]); expect(re.toString()).toBe('/[ab]+/'); }); it('merges Group content of type Alternative with parent Alternative', () => { const re = transform(/a(?:bc)d/, [ungroup]); expect(re.toString()).toBe('/abcd/'); const ast = re.getAST(); expect( ast.body.type === 'Alternative' && ast.body.expressions.every(function(expression) { return expression.type === 'Char'; }) ).toBe(true); }); it('does not ungroup groups with quantifier otherwise', () => { const re = transform(/(?:ab)+/, [ungroup]); expect(re.toString()).toBe('/(?:ab)+/'); }); it('does not ungroup capturing groups', () => { const re = transform(/(a)/, [ungroup]); expect(re.toString()).toBe('/(a)/'); }); it('does not ungroup empty groups', () => { const re = transform(/(?:)+/, [ungroup]); expect(re.toString()).toBe('/(?:)+/'); }); it('does not extract with reference sibling', () => { const re = transform('/(.)\\1(?:0)/', [ungroup]); expect(re.toString()).toBe('/(.)\\1(?:0)/'); }); it('does not extract with char number sibling', () => { const re = transform('/(.)\\2(?:0)/', [ungroup]); expect(re.toString()).toBe('/(.)\\2(?:0)/'); }); }); regexp-tree-0.1.18/src/optimizer/transforms/char-case-insensitive-lowercase-transform.js000066400000000000000000000057051361507317500315730ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const UPPER_A_CP = 'A'.codePointAt(0); const UPPER_Z_CP = 'Z'.codePointAt(0); /** * Transforms case-insensitive regexp to lowercase * * /AaBbÏ/i -> /aabbï/i */ module.exports = { _AZClassRanges: null, _hasUFlag: false, init(ast) { this._AZClassRanges = new Set(); this._hasUFlag = ast.flags.includes('u'); }, shouldRun(ast) { return ast.flags.includes('i'); }, Char(path) { const {node, parent} = path; 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); } const lower = node.symbol.toLowerCase(); if (lower !== node.symbol) { node.value = displaySymbolAsValue(lower, node); node.symbol = lower; node.codePoint = lower.codePointAt(0); } } }; function isAZClassRange(classRange) { const {from, to} = classRange; // 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) { const 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) { const {lead, trail} = getSurrogatePairFromCodePoint(codePoint); 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 { const 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) { const lead = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800; const trail = (codePoint - 0x10000) % 0x400 + 0xdc00; return { lead: lead.toString(16), trail: trail.toString(16) }; }regexp-tree-0.1.18/src/optimizer/transforms/char-class-classranges-merge-transform.js000066400000000000000000000243571361507317500310510ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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(ast) { this._hasIUFlags = ast.flags.includes('i') && ast.flags.includes('u'); }, CharacterClass(path) { const {node} = path; let expressions = node.expressions; const metas = []; // Extract metas expressions.forEach(expression => { if (isMeta(expression)) { metas.push(expression.value); } }); expressions.sort(sortCharClass); for (let i = 0; i < expressions.length; i++) { let 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 { const 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) { const aValue = getSortValue(a); const 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, value = 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 (let 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; } let nbMergedChars = 0; while (index > 0) { let currentExpression = expressions[index]; let 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' ); }regexp-tree-0.1.18/src/optimizer/transforms/char-class-classranges-to-chars-transform.js000066400000000000000000000011071361507317500314560ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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(path) { const {node} = path; 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); } } }; regexp-tree-0.1.18/src/optimizer/transforms/char-class-remove-duplicates-transform.js000066400000000000000000000012511361507317500310630ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to remove duplicates from character classes. */ module.exports = { CharacterClass(path) { const {node} = path; const sources = {}; for (let i = 0; i < node.expressions.length; i++) { const childPath = path.getChild(i); const 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; } } }; regexp-tree-0.1.18/src/optimizer/transforms/char-class-to-meta-transform.js000066400000000000000000000114221361507317500270020ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to replace standard character classes with * their meta symbols equivalents. */ module.exports = { _hasIFlag: false, _hasUFlag: false, init(ast) { this._hasIFlag = ast.flags.includes('i'); this._hasUFlag = ast.flags.includes('u'); }, 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) { const {node} = path; node.expressions.forEach((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) { const {node} = path; let numberPath = null; let lowerCasePath = null; let upperCasePath = null; let underscorePath = null; let u017fPath = null; let u212aPath = null; node.expressions.forEach((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) { const {node} = path; let spacePath = null; let tPath = null; let nPath = null; let rPath = null; let fPath = null; node.expressions.forEach((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, kind = '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 ); } regexp-tree-0.1.18/src/optimizer/transforms/char-class-to-single-char-transform.js000066400000000000000000000036551361507317500302610ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to replace single char character classes with * just that character. * * [\d] -> \d, [^\w] -> \W */ module.exports = { CharacterClass(path) { const {node} = path; if ( node.expressions.length !== 1 || !hasAppropriateSiblings(path) || !isAppropriateChar(node.expressions[0]) ) { return; } let {value, kind, escaped} = node.expressions[0]; 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, 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(); } function hasAppropriateSiblings(path) { const {parent, index} = path; if (parent.type !== 'Alternative') { return true; } const previousNode = parent.expressions[index - 1]; if (previousNode == null) { return true; } // Don't optimized \1[0] to \10 if (previousNode.type === 'Backreference' && previousNode.kind === 'number') { return false; } // Don't optimized \2[0] to \20 if (previousNode.type === 'Char' && previousNode.kind === 'decimal') { return false; } return true; } // Note: \{ and \} are always preserved to avoid `a[{]2[}]` turning // into `a{2}`. function shouldEscape(value) { return /[*[()+?$./{}|]/.test(value); } regexp-tree-0.1.18/src/optimizer/transforms/char-code-to-simple-char-transform.js000066400000000000000000000041241361507317500300660ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const UPPER_A_CP = 'A'.codePointAt(0); const UPPER_Z_CP = 'Z'.codePointAt(0); const LOWER_A_CP = 'a'.codePointAt(0); const LOWER_Z_CP = 'z'.codePointAt(0); const DIGIT_0_CP = '0'.codePointAt(0); const DIGIT_9_CP = '9'.codePointAt(0); /** * A regexp-tree plugin to transform coded chars into simple chars. * * \u0061 -> a */ module.exports = { Char(path) { const {node, parent} = path; if (isNaN(node.codePoint) || node.kind === 'simple') { return; } if (parent.type === 'ClassRange') { if (!isSimpleRange(parent)) { return; } } if (!isPrintableASCIIChar(node.codePoint)) { return; } const symbol = String.fromCodePoint(node.codePoint); const 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) { const {from, to} = classRange; 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); }regexp-tree-0.1.18/src/optimizer/transforms/char-escape-unescape-transform.js000066400000000000000000000072711361507317500274010ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to remove unnecessary escape. * * \e -> e * * [\(] -> [(] */ module.exports = { _hasXFlag: false, init(ast) { this._hasXFlag = ast.flags.includes('x'); }, Char(path) { const {node} = path; if (!node.escaped) { return; } if (shouldUnescape(path, this._hasXFlag)) { delete node.escaped; } }, }; function shouldUnescape(path, hasXFlag) { const {node: {value}, index, parent} = path; // 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) { let i = startIndex; let 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) { // (?:\{) -> (?:{) if (index == null) { return false; } let nbFollowingNumbers = consumeNumbers(index + 1, parent); let i = index + nbFollowingNumbers + 1; let 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) { // (?:\{) -> (?:{) if (index == null) { return false; } let nbPrecedingNumbers = consumeNumbers(index - 1, parent, true); let i = index - nbPrecedingNumbers - 1; let 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; } regexp-tree-0.1.18/src/optimizer/transforms/char-surrogate-pair-to-single-unicode-transform.js000066400000000000000000000010701361507317500326160ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to transform surrogate pairs into single unicode code point * * \ud83d\ude80 -> \u{1f680} */ module.exports = { shouldRun(ast) { return ast.flags.includes('u'); }, Char(path) { const {node} = path; if (node.kind !== 'unicode' || !node.isSurrogatePair || isNaN(node.codePoint)) { return; } node.value = `\\u{${node.codePoint.toString(16)}}`; delete node.isSurrogatePair; } };regexp-tree-0.1.18/src/optimizer/transforms/combine-repeating-patterns-transform.js000066400000000000000000000106631361507317500306520ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NodePath = require('../../traverse/node-path'); const {increaseQuantifierByOne} = require('../../transform/utils'); /** * A regexp-tree plugin to combine repeating patterns. * * /^abcabcabc/ -> /^abc{3}/ * /^(?:abc){2}abc/ -> /^(?:abc){3}/ * /^abc(?:abc){2}/ -> /^(?:abc){3}/ */ module.exports = { Alternative(path) { const {node} = path; // We can skip the first child let index = 1; while (index < node.expressions.length) { let 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) { const {node} = alternative; const nbPossibleLengths = Math.ceil(index / 2); let i = 0; while (i < nbPossibleLengths) { const startIndex = index - 2 * i - 1; let right, left; if (i === 0) { right = child; left = alternative.getChild(startIndex); } else { right = NodePath.getForNode({ type: 'Alternative', expressions: [...node.expressions.slice(index - i, index), child.node] }); left = NodePath.getForNode({ type: 'Alternative', expressions: [...node.expressions.slice(startIndex, index - i)] }); } if (right.hasEqualSource(left)) { for (let 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) { const {node} = alternative; let i = 0; while (i < index) { let previousChild = alternative.getChild(i); if (previousChild.node.type === 'Repetition' && previousChild.node.quantifier.greedy) { let left = previousChild.getChild(); let right; 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: [...node.expressions.slice(i + 1, index + 1)] }); } if (left.hasEqualSource(right)) { for (let 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) { const {node} = alternative; if (child.node.type === 'Repetition' && child.node.quantifier.greedy) { let right = child.getChild(); let left; if (right.node.type === 'Group' && !right.node.capturing) { right = right.getChild(); } let rightLength; if (right.node.type === 'Alternative') { rightLength = right.node.expressions.length; left = NodePath.getForNode({ type: 'Alternative', expressions: [...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 (let j = index - rightLength; j < index; j++) { alternative.getChild(index - rightLength).remove(); } increaseQuantifierByOne(child.node.quantifier); return index - rightLength; } } return index; }regexp-tree-0.1.18/src/optimizer/transforms/disjunction-remove-duplicates-transform.js000066400000000000000000000016461361507317500314040ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const NodePath = require('../../traverse/node-path'); const { disjunctionToList, listToDisjunction, } = require('../../transform/utils'); /** * Removes duplicates from a disjunction sequence: * * /(ab|bc|ab)+(xy|xy)+/ -> /(ab|bc)+(xy)+/ */ module.exports = { Disjunction(path) { const {node} = path; // Make unique nodes. const uniqueNodesMap = {}; const parts = disjunctionToList(node).filter(part => { const 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)); } };regexp-tree-0.1.18/src/optimizer/transforms/group-single-chars-to-char-class.js000066400000000000000000000032401361507317500275530ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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(path) { const {node, parent} = path; if (!handlers[parent.type]) { return; } const charset = new Map(); if (!shouldProcess(node, charset) || !charset.size) { return; } const characterClass = { type: 'CharacterClass', expressions: Array.from(charset.keys()) .sort() .map(key => charset.get(key)), }; handlers[parent.type](path.getParent(), characterClass); }, }; const handlers = { RegExp(path, characterClass) { const {node} = path; node.body = characterClass; }, Group(path, characterClass) { const {node} = path; if (node.capturing) { node.expression = characterClass; } else { path.replace(characterClass); } }, }; function shouldProcess(expression, charset) { if (!expression) { // Abort on empty disjunction part return false; } const {type} = expression; if (type === 'Disjunction') { const {left, right} = expression; return shouldProcess(left, charset) && shouldProcess(right, charset); } else if (type === 'Char') { const {value} = expression; charset.set(value, expression); return true; } else if (type === 'CharacterClass' && !expression.negative) { return expression.expressions.every(expression => shouldProcess(expression, charset) ); } return false; } regexp-tree-0.1.18/src/optimizer/transforms/index.js000066400000000000000000000036301361507317500225160ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; module.exports = new Map([ // \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'), ], // [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')], // [a-de-f] -> [a-f] [ 'charClassClassrangesMerge', require('./char-class-classranges-merge-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'), ], ]); regexp-tree-0.1.18/src/optimizer/transforms/quantifier-range-to-symbol-transform.js000066400000000000000000000021361361507317500306040ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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(path) { const {node} = path; if (node.kind !== 'Range') { return; } // a{0,} -> a* rewriteOpenZero(path); // a{1,} -> a+ rewriteOpenOne(path); // a{1} -> a rewriteExactOne(path); } }; function rewriteOpenZero(path) { const {node} = path; if (node.from !== 0 || node.to) { return; } node.kind = '*'; delete node.from; } function rewriteOpenOne(path) { const {node} = path; if (node.from !== 1 || node.to) { return; } node.kind = '+'; delete node.from; } function rewriteExactOne(path) { const {node} = path; if (node.from !== 1 || node.to !== 1) { return; } path.parentPath.replace(path.parentPath.node.expression); } regexp-tree-0.1.18/src/optimizer/transforms/quantifiers-merge-transform.js000066400000000000000000000053621361507317500270530ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const {increaseQuantifierByOne} = require('../../transform/utils'); /** * 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(path) { const {node, parent} = path; if ( parent.type !== 'Alternative' || !path.index ) { return; } const previousSibling = path.getPreviousSibling(); if (!previousSibling) { return; } if (previousSibling.node.type === 'Repetition') { if (!previousSibling.getChild().hasEqualSource(path.getChild())) { return; } let { from: previousSiblingFrom, to: previousSiblingTo } = extractFromTo(previousSibling.node.quantifier); let { from: nodeFrom, to: nodeTo } = extractFromTo(node.quantifier); // 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) { let from, to; 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, to}; }regexp-tree-0.1.18/src/optimizer/transforms/remove-empty-group-transform.js000066400000000000000000000010641361507317500272020ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to remove non-capturing empty groups. * * /(?:)a/ -> /a/ * /a|(?:)/ -> /a|/ */ module.exports = { Group(path) { const {node, parent} = path; const childPath = path.getChild(); if (node.capturing || childPath) { return; } if (parent.type === 'Repetition') { path.getParent().replace(node); } else if (parent.type !== 'RegExp') { path.remove(); } } }; regexp-tree-0.1.18/src/optimizer/transforms/ungroup-transform.js000066400000000000000000000040061361507317500251150ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * A regexp-tree plugin to remove unnecessary groups. * * /(?:a)/ -> /a/ */ module.exports = { Group(path) { const {node, parent} = path; const childPath = path.getChild(); if (node.capturing || !childPath) { return; } // Don't optimize \1(?:0) to \10 if (!hasAppropriateSiblings(path)) { 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') { const 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: [ ...parent.expressions.slice(0, path.index), ...childPath.node.expressions, ...parent.expressions.slice(path.index + 1), ], }); } } else { path.replace(childPath.node); } }, }; function hasAppropriateSiblings(path) { const {parent, index} = path; if (parent.type !== 'Alternative') { return true; } const previousNode = parent.expressions[index - 1]; if (previousNode == null) { return true; } // Don't optimized \1(?:0) to \10 if (previousNode.type === 'Backreference' && previousNode.kind === 'number') { return false; } // Don't optimized \2(?:0) to \20 if (previousNode.type === 'Char' && previousNode.kind === 'decimal') { return false; } return true; } regexp-tree-0.1.18/src/parser/000077500000000000000000000000001361507317500161235ustar00rootroot00000000000000regexp-tree-0.1.18/src/parser/README.md000066400000000000000000000015661361507317500174120ustar00rootroot00000000000000# regexp-tree: Parser module Parses a regexp string into an AST. See [the specification](https://github.com/DmitrySoshnikov/regexp-tree#ast-nodes-specification) for AST nodes format. Example: ```js console.log(regexpTreeParser.parse('/[a-z]+/')); ``` Result: ```js { type: 'RegExp', body: { type: 'Repetition', expression: { type: 'CharacterClass', expressions: [ { type: 'ClassRange', from: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 97 }, to: { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 122 } } ] }, quantifier: { type: 'Quantifier', kind: '+', greedy: true } }, flags: 'i', } ```regexp-tree-0.1.18/src/parser/__tests__/000077500000000000000000000000001361507317500200615ustar00rootroot00000000000000regexp-tree-0.1.18/src/parser/__tests__/parser-basic-test.js000066400000000000000000000574101361507317500237560ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('..'); function re(regexp) { return parser.parse(regexp.toString()); } describe('basic', () => { it('char', () => { expect(re(/a/)).toEqual({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, flags: '', }); }); it('parens char', () => { expect(re(/\(\)/)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Char', value: '(', symbol: '(', kind: 'simple', escaped: true, codePoint: '('.codePointAt(0), }, { type: 'Char', value: ')', symbol: ')', kind: 'simple', escaped: true, codePoint: ')'.codePointAt(0), }, ], }, flags: '', }); }); it('disjunction', () => { expect(re(/a|b/)).toEqual({ type: 'RegExp', body: { type: 'Disjunction', left: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, right: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 'b'.codePointAt(0), }, }, flags: '', }); }); it('alternative', () => { expect(re(/ab/)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 'b'.codePointAt(0), }, ], }, flags: '', }); }); it('character class', () => { expect(re(/[a-z\d]/i)).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [ { type: 'ClassRange', from: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, to: { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 'z'.codePointAt(0), }, }, { type: 'Char', value: '\\d', kind: 'meta', codePoint: NaN, }, ], }, flags: 'i', }); }); it('empty class', () => { /*eslint no-empty-character-class:0*/ expect(re(/[]/)).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [], }, flags: '', }); expect(re(/[^]/)).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', negative: true, expressions: [], }, flags: '', }); }); it('character class with punctuation symbols', () => { expect(re('/[!"#$%&\'()*+,./:;<=>?@^_`{|}~-]/')).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [ { type: 'Char', kind: 'simple', value: '!', symbol: '!', codePoint: '!'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '"', symbol: '"', codePoint: '"'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '#', symbol: '#', codePoint: '#'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '$', symbol: '$', codePoint: '$'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '%', symbol: '%', codePoint: '%'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '&', symbol: '&', codePoint: '&'.codePointAt(0), }, { type: 'Char', kind: 'simple', // prettier-ignore value: '\'', // prettier-ignore symbol: '\'', // prettier-ignore codePoint: '\''.codePointAt(0) }, { type: 'Char', kind: 'simple', value: '(', symbol: '(', codePoint: '('.codePointAt(0), }, { type: 'Char', kind: 'simple', value: ')', symbol: ')', codePoint: ')'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '*', symbol: '*', codePoint: '*'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '+', symbol: '+', codePoint: '+'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: ',', symbol: ',', codePoint: ','.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '.', symbol: '.', codePoint: '.'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '/', symbol: '/', codePoint: '/'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: ':', symbol: ':', codePoint: ':'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: ';', symbol: ';', codePoint: ';'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '<', symbol: '<', codePoint: '<'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '=', symbol: '=', codePoint: '='.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '>', symbol: '>', codePoint: '>'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '?', symbol: '?', codePoint: '?'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '@', symbol: '@', codePoint: '@'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '^', symbol: '^', codePoint: '^'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '_', symbol: '_', codePoint: '_'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '`', symbol: '`', codePoint: '`'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '{', symbol: '{', codePoint: '{'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '|', symbol: '|', codePoint: '|'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '}', symbol: '}', codePoint: '}'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '~', symbol: '~', codePoint: '~'.codePointAt(0), }, { type: 'Char', kind: 'simple', value: '-', symbol: '-', codePoint: '-'.codePointAt(0), }, ], }, flags: '', }); }); it('named capturing group duplicate', () => { expect(() => parser.parse('/(?)(?)/')).toThrowError( new SyntaxError(`Duplicate of the named group "foo".`) ); }); it('named unicode name', () => { expect(() => parser.parse('/(?<\\u{41}\\u0042>)/')).toThrowError( new SyntaxError( `invalid group Unicode name "\\u{41}\\u0042", use \`u\` flag.` ) ); expect(() => parser.parse('/(?)\\k<\\u{41}>/')).toThrowError( new SyntaxError(`invalid group Unicode name "\\u{41}", use \`u\` flag.`) ); expect(() => parser.parse('/(?<\\u{41}>)/u')).not.toThrow(); expect(() => parser.parse('/(?)\\k<\\u{41}>/u')).not.toThrow(); }); it('capturing group numbers', () => { expect(re('/(?:)(a)(?:)(?b)/')).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: false, expression: null, }, { type: 'Group', capturing: true, number: 1, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, { type: 'Group', capturing: false, expression: null, }, { type: 'Group', capturing: true, name: 'name', nameRaw: 'name', number: 2, expression: { type: 'Char', value: 'b', symbol: 'b', kind: 'simple', codePoint: 'b'.codePointAt(0), }, }, ], }, flags: '', }); }); it('empty group', () => { expect(re(/()/)).toEqual({ type: 'RegExp', body: { type: 'Group', capturing: true, number: 1, expression: null, }, flags: '', }); // Not using `re` helper here because named groups are not yet implemented. expect(parser.parse('/(?)/')).toEqual({ type: 'RegExp', body: { type: 'Group', capturing: true, name: 'foo', nameRaw: 'foo', number: 1, expression: null, }, flags: '', }); expect(re(/(?:)/)).toEqual({ type: 'RegExp', body: { type: 'Group', capturing: false, expression: null, }, flags: '', }); }); it('non-empty group', () => { expect(re(/(a)/)).toEqual({ type: 'RegExp', body: { type: 'Group', capturing: true, number: 1, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); // Not using `re` helper here because named groups are not yet implemented. expect(parser.parse('/(?a)/')).toEqual({ type: 'RegExp', body: { type: 'Group', name: 'foo', nameRaw: 'foo', capturing: true, number: 1, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); expect(re(/(?:a)/)).toEqual({ type: 'RegExp', body: { type: 'Group', capturing: false, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); }); it('empty LA assertion', () => { expect(re(/(?=)/)).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookahead', assertion: null, }, flags: '', }); expect(re(/(?!)/)).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookahead', negative: true, assertion: null, }, flags: '', }); }); it('non-empty LA assertion', () => { expect(re(/(?=a)/)).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookahead', assertion: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); expect(re(/(?!a)/)).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookahead', negative: true, assertion: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); }); it('empty LB assertion', () => { // Not using `re` helper here because lookbehind // assertions are not yet implemented in JS. expect(parser.parse('/(?<=)/')).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookbehind', assertion: null, }, flags: '', }); expect(parser.parse('/(? { // Not using `re` helper here because lookbehind // assertions are not yet implemented in JS. expect(re('/(?<=a)/')).toEqual({ type: 'RegExp', body: { type: 'Assertion', kind: 'Lookbehind', assertion: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, flags: '', }); expect(parser.parse('/(? { expect(re(/(a)\1\2/)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 1, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, { type: 'Backreference', kind: 'number', number: 1, reference: 1, }, { type: 'Char', value: '\\2', kind: 'decimal', symbol: String.fromCodePoint(2), codePoint: 2, }, ], }, flags: '', }); }); it('named backreference', () => { // Not using `re` helper here because named groups are not yet implemented expect(parser.parse('/(?y)\\k\\k/')).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 1, name: 'x', nameRaw: 'x', expression: { type: 'Char', value: 'y', symbol: 'y', kind: 'simple', codePoint: 'y'.codePointAt(0), }, }, { type: 'Backreference', kind: 'name', number: 1, reference: 'x', referenceRaw: 'x', }, { type: 'Char', value: 'k', symbol: 'k', kind: 'simple', escaped: true, codePoint: 'k'.codePointAt(0), }, { type: 'Char', value: '<', symbol: '<', kind: 'simple', codePoint: '<'.codePointAt(0), }, { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 'z'.codePointAt(0), }, { type: 'Char', value: '>', symbol: '>', kind: 'simple', codePoint: '>'.codePointAt(0), }, ], }, flags: '', }); }); it('non-backreferences', () => { expect(re(/(?:a)\1\k/)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: false, expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, }, { type: 'Char', value: '\\1', symbol: String.fromCodePoint(1), kind: 'decimal', codePoint: 1, }, { type: 'Char', value: 'k', symbol: 'k', kind: 'simple', escaped: true, codePoint: 'k'.codePointAt(0), }, { type: 'Char', value: '<', symbol: '<', kind: 'simple', codePoint: '<'.codePointAt(0), }, { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 'z'.codePointAt(0), }, { type: 'Char', value: '>', symbol: '>', kind: 'simple', codePoint: '>'.codePointAt(0), }, ], }, flags: '', }); }); it('meta chars', () => { function LettersRange(start, stop) { const range = []; for ( let idx = start.charCodeAt(0), end = stop.charCodeAt(0); idx <= end; ++idx ) { range.push(String.fromCodePoint(idx)); } return range; } const metaChars = new Set([ 't', 'n', 'r', 'd', 'D', 's', 'S', 'w', 'W', 'v', 'f', ]); const azAZRange = LettersRange('a', 'z').concat(LettersRange('A', 'Z')); for (const letter of azAZRange) { const parsedChar = parser.parse(`/\\${letter}/`).body; if (metaChars.has(letter)) { expect(parsedChar.kind).toBe('meta'); } else { expect(parsedChar.kind).not.toBe('meta'); } } // Special case for [\b] - Backspace const backspace = parser.parse('/[\\b]/').body.expressions[0]; expect(backspace.kind).toBe('meta'); }); it('unicode', () => { expect(re(/\u003B/).body).toEqual({ type: 'Char', value: '\\u003B', symbol: String.fromCodePoint(0x003b), kind: 'unicode', codePoint: 0x003b, }); // Using `u` flag, 1 digit. expect(re(/\u{9}/u).body).toEqual({ type: 'Char', value: '\\u{9}', symbol: String.fromCodePoint(9), kind: 'unicode', codePoint: 9, }); // Using `u` flag, 6 digits, 10FFFF is max. expect(re(/\u{10FFFF}/u).body).toEqual({ type: 'Char', value: '\\u{10FFFF}', symbol: String.fromCodePoint(0x10ffff), kind: 'unicode', codePoint: 0x10ffff, }); // Using `u` flag, leading zeros. expect(re(/\u{000001D306}/u).body).toEqual({ type: 'Char', value: '\\u{000001D306}', symbol: String.fromCodePoint(0x000001d306), kind: 'unicode', codePoint: 0x000001d306, }); // Not using `u` flag, not parsed as a unicode code point, // but as an (escaped) `u` character repeated 1234 times. expect(re(/\u{1234}/).body).toEqual({ type: 'Repetition', expression: { type: 'Char', value: 'u', symbol: 'u', kind: 'simple', escaped: true, codePoint: 'u'.codePointAt(0), }, quantifier: { type: 'Quantifier', kind: 'Range', from: 1234, to: 1234, greedy: true, }, }); // Using `u` flag, surrogate pairs. expect(re(/\ud83d\ude80/u).body).toEqual({ type: 'Char', value: '\\ud83d\\ude80', kind: 'unicode', symbol: String.fromCodePoint(0x1f680), codePoint: 0x1f680, isSurrogatePair: true, }); // Using `u` flag, surrogate pairs in character class. expect(re(/[\ud83d\ude80]/u).body).toEqual({ type: 'CharacterClass', expressions: [ { type: 'Char', value: '\\ud83d\\ude80', kind: 'unicode', symbol: String.fromCodePoint(0x1f680), codePoint: 0x1f680, isSurrogatePair: true, }, ], }); // Not using `u` flag, surrogate pairs are treated as two characters expect(re(/\ud83d\ude80/).body).toEqual({ type: 'Alternative', expressions: [ { type: 'Char', value: '\\ud83d', kind: 'unicode', symbol: String.fromCodePoint(0xd83d), codePoint: 0xd83d, }, { type: 'Char', value: '\\ude80', kind: 'unicode', symbol: String.fromCodePoint(0xde80), codePoint: 0xde80, }, ], }); }); it('valid sorted flags', () => { expect(re(/a/gimuy)).toEqual({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, flags: 'gimuy', }); }); it('valid not sorted flags', () => { // Not using `re` helper here because `RegExp.prototype.toString` sorts flags expect(parser.parse('/a/mgyiu')).toEqual({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, flags: 'gimuy', }); }); it('hex escape', () => { expect(re(/\x33/)).toEqual({ type: 'RegExp', body: { type: 'Char', value: '\\x33', kind: 'hex', symbol: String.fromCodePoint(0x33), codePoint: 0x33, }, flags: '', }); }); it('decimal escape', () => { expect(re(/\99/)).toEqual({ type: 'RegExp', body: { type: 'Char', value: '\\99', kind: 'decimal', symbol: String.fromCodePoint(99), codePoint: 99, }, flags: '', }); }); it('dotAll (/s) flag', () => { // Not using `re` helper here because /s flag is not yet implemented expect(parser.parse('/a/s')).toEqual({ type: 'RegExp', body: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), }, flags: 's', }); }); it('throws error on invalid Unicode escape', () => { expect(() => parser.parse('/\\p/u')).toThrowError(SyntaxError); expect(() => parser.parse('/\\e/u')).toThrowError(SyntaxError); expect(() => parser.parse('/\\g/u')).toThrowError(SyntaxError); expect(() => parser.parse('/\\-/u')).toThrowError(SyntaxError); }); it('should not throw when identity escape is a syntax character', () => { expect(() => parser.parse('/\\./u')).not.toThrowError(); }); }); regexp-tree-0.1.18/src/parser/__tests__/parser-extended-test.js000066400000000000000000000211231361507317500244650ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('..'); describe('extended', () => { it('x flag', () => { const re = `/ # A regular expression for date. (?\d{4})- # year part of a date (?\d{2})- # month part of a date (?\d{2}) # day part of a date /x`; expect(parser.parse(re)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', name: 'year', nameRaw: 'year', number: 1, capturing: true, expression: { type: 'Repetition', expression: { type: 'Char', kind: 'simple', value: 'd', symbol: 'd', codePoint: 'd'.codePointAt(0), }, quantifier: { type: 'Quantifier', from: 4, greedy: true, kind: 'Range', to: 4, }, }, }, { type: 'Char', kind: 'simple', value: '-', symbol: '-', codePoint: '-'.codePointAt(0), }, { type: 'Group', name: 'month', nameRaw: 'month', number: 2, capturing: true, expression: { type: 'Repetition', expression: { type: 'Char', kind: 'simple', value: 'd', symbol: 'd', codePoint: 'd'.codePointAt(0), }, quantifier: { type: 'Quantifier', from: 2, greedy: true, kind: 'Range', to: 2, }, }, }, { type: 'Char', kind: 'simple', value: '-', symbol: '-', codePoint: '-'.codePointAt(0), }, { type: 'Group', name: 'day', nameRaw: 'day', number: 3, capturing: true, expression: { type: 'Repetition', expression: { kind: 'simple', type: 'Char', value: 'd', symbol: 'd', codePoint: 'd'.codePointAt(0), }, quantifier: { type: 'Quantifier', from: 2, greedy: true, kind: 'Range', to: 2, }, }, }, ], }, flags: 'x', }); }); it('escaped space and # with x flag', () => { const re = '/\\ \\#/x'; expect(parser.parse(re)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Char', value: ' ', symbol: ' ', kind: 'simple', escaped: true, codePoint: ' '.codePointAt(0), }, { type: 'Char', value: '#', symbol: '#', kind: 'simple', escaped: true, codePoint: '#'.codePointAt(0), }, ], }, flags: 'x', }); }); it('non-escaped space and # in class with x flag', () => { const re = '/[ #]/x'; expect(parser.parse(re)).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [ { type: 'Char', value: ' ', symbol: ' ', kind: 'simple', codePoint: ' '.codePointAt(0), }, { type: 'Char', value: '#', symbol: '#', kind: 'simple', codePoint: '#'.codePointAt(0), }, ], }, flags: 'x', }); }); it('group numbers', () => { const re = /(((a)b)c)(d)(e)/; expect(parser.parse(re)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 1, expression: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 2, expression: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, number: 3, expression: { type: 'Char', value: 'a', kind: 'simple', symbol: 'a', codePoint: 97, }, }, { type: 'Char', value: 'b', kind: 'simple', symbol: 'b', codePoint: 98, }, ], }, }, { type: 'Char', value: 'c', kind: 'simple', symbol: 'c', codePoint: 99, }, ], }, }, { type: 'Group', capturing: true, number: 4, expression: { type: 'Char', value: 'd', kind: 'simple', symbol: 'd', codePoint: 100, }, }, { type: 'Group', capturing: true, number: 5, expression: { type: 'Char', value: 'e', kind: 'simple', symbol: 'e', codePoint: 101, }, }, ], }, flags: '', }); }); it('group numbers', () => { const re = '/(?(?(?a)b)c)(?d)(?e)/'; expect(parser.parse(re)).toEqual({ type: 'RegExp', body: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, name: 'c', nameRaw: 'c', number: 1, expression: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, name: 'b', nameRaw: 'b', number: 2, expression: { type: 'Alternative', expressions: [ { type: 'Group', capturing: true, name: 'a', nameRaw: 'a', number: 3, expression: { type: 'Char', value: 'a', kind: 'simple', symbol: 'a', codePoint: 97, }, }, { type: 'Char', value: 'b', kind: 'simple', symbol: 'b', codePoint: 98, }, ], }, }, { type: 'Char', value: 'c', kind: 'simple', symbol: 'c', codePoint: 99, }, ], }, }, { type: 'Group', capturing: true, name: 'd', nameRaw: 'd', number: 4, expression: { type: 'Char', value: 'd', kind: 'simple', symbol: 'd', codePoint: 100, }, }, { type: 'Group', capturing: true, name: 'e', nameRaw: 'e', number: 5, expression: { type: 'Char', value: 'e', kind: 'simple', symbol: 'e', codePoint: 101, }, }, ], }, flags: '', }); }); }); regexp-tree-0.1.18/src/parser/__tests__/parser-locations-test.js000066400000000000000000000053361361507317500246700ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('..'); const traverse = require('../../traverse'); parser.setOptions({captureLocations: true}); function re(regexp) { return parser.parse(regexp.toString()); } describe('locations', () => { it('char class', () => { const ast = re(/[a-z]/); // RegExp. expect(ast.loc).toEqual({ source: '/[a-z]/', start: { line: 1, column: 0, offset: 0, }, end: { line: 1, column: 7, offset: 7, }, }); // CharacterClass. expect(ast.body.loc).toEqual({ source: '[a-z]', start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 6, offset: 6, }, }); // ClassRange. const classRange = ast.body.expressions[0]; expect(classRange.loc).toEqual({ source: 'a-z', start: { line: 1, column: 2, offset: 2, }, end: { line: 1, column: 5, offset: 5, }, }); expect(classRange.from.loc).toEqual({ source: 'a', start: { line: 1, column: 2, offset: 2, }, end: { line: 1, column: 3, offset: 3, }, }); expect(classRange.to.loc).toEqual({ source: 'z', start: { line: 1, column: 4, offset: 4, }, end: { line: 1, column: 5, offset: 5, }, }); }); it('location source', () => { const ast = parser.parse('/([a-z]+?)|abc/i'); const sources = []; traverse.traverse(ast, { '*': ({node}) => { sources.push(node.loc.source); }, }); expect(sources).toEqual([ '/([a-z]+?)|abc/i', '([a-z]+?)|abc', '([a-z]+?)', '[a-z]+?', '[a-z]', 'a-z', 'a', 'z', '+', 'abc', 'a', 'b', 'c', ]); }); it('empty disjunction', () => { const ast = parser.parse('/|/'); expect(ast).toEqual({ type: 'RegExp', body: { type: 'Disjunction', left: null, right: null, loc: { source: '|', start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 2, offset: 2, }, } }, flags: '', loc: { source: '/|/', start: { line: 1, column: 0, offset: 0, }, end: { line: 1, column: 3, offset: 3, }, } }); }); }); regexp-tree-0.1.18/src/parser/__tests__/parser-test262-test.js000066400000000000000000000062511361507317500241030ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ // Below are the RegExp tests defined by `test262` suite: // https://github.com/tc39/test262/tree/master/test/built-ins/RegExp/ const parser = require('..'); function invalid(regexp, message) { try { parser.parse(regexp); throw new Error('expected `parse` to throw'); } catch (e) { expect(e).toBeInstanceOf(SyntaxError); if (message) { expect(e.message.includes(message)).toBe(true); } } } function valid(regexp) { expect(() => parser.parse(regexp)).not.toThrow(SyntaxError); } function ut(message) { return `Unexpected token: ${message}`; } describe('test262', () => { it('invalid char range', () => { // 15.10.2.15-6-1 invalid('/^[z-a]$/', 'out of order'); invalid('/^[-z-a]$/', 'out of order'); }); it('invalid quantifier range', () => { // 15.10.2.5-3-1 invalid('/0{2,1}/', 'Numbers out of order'); }); it('invalid flags', () => { // 15.10.4.1-3 invalid('/abc/a'); }); it('valid flags', () => { // 15.10.4.1-4 valid('/abc/gimuy'); }); it('invalid * char', () => { // S15.10.1_A1_T1 invalid('/a**/', ut(`"*" at 1:3`)); }); it('invalid + char', () => { // S15.10.1_A1_T10 invalid('/++a/', ut(`"+" at 1:1`)); }); it('invalid ? char', () => { // S15.10.1_A1_T11 invalid('/?a/', ut(`"?" at 1:1`)); }); it('invalid ?? char', () => { // S15.10.1_A1_T12 invalid('/??a/', ut(`"?" at 1:1`)); }); it('nothing to repeat 1', () => { // S15.10.1_A1_T13 invalid('/x{1}{1,}/', ut(`"{1,}" at 1:5`)); }); it('nothing to repeat 2', () => { // S15.10.1_A1_T14 invalid('/x{1,2}{1}/', ut(`"{1}" at 1:7`)); }); it('nothing to repeat 3', () => { // S15.10.1_A1_T15 invalid('/x{1,}{1}/', ut(`"{1}" at 1:6`)); }); it('nothing to repeat 4', () => { // S15.10.1_A1_T16 invalid('/x{0,1}{1,}/', ut(`"{1,}" at 1:7`)); }); it('invalid * 2', () => { // S15.10.1_A1_T2 invalid('/a***/', ut(`"*" at 1:3`)); }); it('invalid + 2', () => { // S15.10.1_A1_T3 invalid('/a++/', ut(`"+" at 1:3`)); }); it('invalid + 3', () => { // S15.10.1_A1_T4 invalid('/a+++/', ut(`"+" at 1:3`)); }); it('invalid ? 2', () => { // S15.10.1_A1_T5 invalid('/a???/', ut(`"?" at 1:4`)); }); it('invalid ? 3', () => { // S15.10.1_A1_T6 invalid('/a????/', ut(`"?" at 1:4`)); }); it('invalid * 3', () => { // S15.10.1_A1_T7 invalid('/*a/', ut(`"*" at 1:1`)); }); it('invalid * 4', () => { // S15.10.1_A1_T8 invalid('/**a/', ut(`"*" at 1:1`)); }); it('invalid + 4', () => { // S15.10.1_A1_T9 invalid('/+a/', ut(`"+" at 1:1`)); }); it('invalid unicode escape', () => { invalid('/\\u{11FFFF}/u', 'Bad character escape'); }); it('unicode group names', () => { valid('/(?<π>a)/u'); valid('/(?<\\u{03C0}>a)/u'); invalid('/(?<π>a)(?<\\u{03C0}>a)/u', 'Duplicate of the named group'); valid('/(?<$𐒤>a)/u'); valid('/(?<_\\u200C>a)/u'); valid('/(?<_\\u200D>a)/u'); valid('/(?<ಠ_ಠ>a)/u'); valid('/(?<$>a)/u'); valid('/(?<_>a)/u'); }); }); regexp-tree-0.1.18/src/parser/__tests__/parser-unicode-properties-test.js000066400000000000000000000200631361507317500265070ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('..'); const { getCanonicalValue, BINARY_PROP_NAMES_TO_ALIASES, BINARY_ALIASES_TO_PROP_NAMES, GENERAL_CATEGORY_VALUE_TO_ALIASES, GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES, SCRIPT_VALUE_TO_ALIASES, SCRIPT_VALUE_ALIASES_TO_VALUE, } = require('../unicode/parser-unicode-properties'); describe('parser-unicode-properties', () => { it('general-category', () => { expect(parser.parse('/\\p{General_Category=Letter}/u').body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value: 'Letter', negative: false, shorthand: false, binary: false, canonicalName: 'General_Category', canonicalValue: 'Letter', }); }); it('shorthand', () => { expect(parser.parse('/\\p{Letter}/u').body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value: 'Letter', negative: false, shorthand: true, binary: false, canonicalName: 'General_Category', canonicalValue: 'Letter', }); }); it('negative', () => { expect(parser.parse('/\\P{Letter}/u').body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value: 'Letter', negative: true, shorthand: true, binary: false, canonicalName: 'General_Category', canonicalValue: 'Letter', }); }); it('binary', () => { expect(parser.parse('/\\p{Hex_Digit}/u').body).toEqual({ type: 'UnicodeProperty', name: 'Hex_Digit', value: 'Hex_Digit', negative: false, shorthand: false, binary: true, canonicalName: 'Hex_Digit', canonicalValue: 'Hex_Digit', }); }); it('script', () => { expect(parser.parse('/\\p{Script=Cyrillic}/u').body).toEqual({ type: 'UnicodeProperty', name: 'Script', value: 'Cyrillic', negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: 'Cyrillic', }); }); it('script-extensions', () => { expect(parser.parse('/\\p{Script_Extensions=Cyrillic}/u').body).toEqual({ type: 'UnicodeProperty', name: 'Script_Extensions', value: 'Cyrillic', negative: false, shorthand: false, binary: false, canonicalName: 'Script_Extensions', canonicalValue: 'Cyrillic', }); }); it('auto-general-category', () => { for (const value in GENERAL_CATEGORY_VALUE_TO_ALIASES) { expect(parser.parse(`/\\p{General_Category=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value, negative: false, shorthand: false, binary: false, canonicalName: 'General_Category', canonicalValue: value, }); expect(parser.parse(`/\\p{gc=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'gc', value, negative: false, shorthand: false, binary: false, canonicalName: 'General_Category', canonicalValue: value, }); } for (const value in GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES) { expect(parser.parse(`/\\p{General_Category=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value, negative: false, shorthand: false, binary: false, canonicalName: 'General_Category', canonicalValue: getCanonicalValue(value), }); expect(parser.parse(`/\\p{gc=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'gc', value, negative: false, shorthand: false, binary: false, canonicalName: 'General_Category', canonicalValue: getCanonicalValue(value), }); } }); it('auto-script', () => { for (const value in SCRIPT_VALUE_TO_ALIASES) { expect(parser.parse(`/\\p{Script=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'Script', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: value, }); expect(parser.parse(`/\\p{sc=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'sc', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: value, }); } for (const value in SCRIPT_VALUE_ALIASES_TO_VALUE) { expect(parser.parse(`/\\p{Script=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'Script', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: getCanonicalValue(value), }); expect(parser.parse(`/\\p{sc=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'sc', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script', canonicalValue: getCanonicalValue(value), }); } }); it('auto-script-extensions', () => { for (const value in SCRIPT_VALUE_TO_ALIASES) { expect(parser.parse(`/\\p{Script_Extensions=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'Script_Extensions', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script_Extensions', canonicalValue: value, }); expect(parser.parse(`/\\p{scx=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'scx', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script_Extensions', canonicalValue: value, }); } for (const value in SCRIPT_VALUE_ALIASES_TO_VALUE) { expect(parser.parse(`/\\p{Script_Extensions=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'Script_Extensions', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script_Extensions', canonicalValue: getCanonicalValue(value), }); expect(parser.parse(`/\\p{scx=${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'scx', value, negative: false, shorthand: false, binary: false, canonicalName: 'Script_Extensions', canonicalValue: getCanonicalValue(value), }); } }); it('auto-shorthand', () => { for (const value in GENERAL_CATEGORY_VALUE_TO_ALIASES) { expect(parser.parse(`/\\p{${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value, negative: false, shorthand: true, binary: false, canonicalName: 'General_Category', canonicalValue: value, }); } for (const value in GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES) { expect(parser.parse(`/\\p{${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: 'General_Category', value, negative: false, shorthand: true, binary: false, canonicalName: 'General_Category', canonicalValue: getCanonicalValue(value), }); } }); it('auto-binary', () => { for (const value in BINARY_PROP_NAMES_TO_ALIASES) { expect(parser.parse(`/\\p{${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: value, value, negative: false, shorthand: false, binary: true, canonicalName: value, canonicalValue: value, }); } for (const value in BINARY_ALIASES_TO_PROP_NAMES) { expect(parser.parse(`/\\p{${value}}/u`).body).toEqual({ type: 'UnicodeProperty', name: value, value, negative: false, shorthand: false, binary: true, canonicalName: getCanonicalValue(value), canonicalValue: getCanonicalValue(value), }); } }); }); regexp-tree-0.1.18/src/parser/generated/000077500000000000000000000000001361507317500200615ustar00rootroot00000000000000regexp-tree-0.1.18/src/parser/generated/regexp-tree.js000066400000000000000000002744121361507317500226600ustar00rootroot00000000000000/** * LR parser generated by the Syntax tool. * * https://www.npmjs.com/package/syntax-cli * * npm install -g syntax-cli * * syntax-cli --help * * To regenerate run: * * syntax-cli \ * --grammar ~/path-to-grammar-file \ * --mode \ * --output ~/path-to-output-parser-file.js */ 'use strict'; /** * Matched token text. */ let yytext; /** * Length of the matched token text. */ let yyleng; /** * Storage object. */ let yy = {}; /** * Result of semantic action. */ let __; /** * Result location object. */ let __loc; function yyloc(start, end) { if (!yy.options.captureLocations) { return null; } // Epsilon doesn't produce location. if (!start || !end) { return start || end; } return { startOffset: start.startOffset, endOffset: end.endOffset, startLine: start.startLine, endLine: end.endLine, startColumn: start.startColumn, endColumn: end.endColumn, }; } const EOF = '$'; /** * List of productions (generated by Syntax tool). */ const productions = [[-1,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [0,4,(_1,_2,_3,_4,_1loc,_2loc,_3loc,_4loc) => { __loc = yyloc(_1loc, _4loc); __ = Node({ type: 'RegExp', body: _2, flags: checkFlags(_4), }, loc(_1loc, _4loc || _3loc)) }], [1,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [1,0,() => { __loc = null; __ = '' }], [2,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [2,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); __ = _1 + _2 }], [3,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [4,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [4,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); // Location for empty disjunction: /|/ let _loc = null; if (_2loc) { _loc = loc(_1loc || _2loc, _3loc || _2loc); }; __ = Node({ type: 'Disjunction', left: _1, right: _3, }, _loc) }], [5,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); if (_1.length === 0) { __ = null; return; } if (_1.length === 1) { __ = Node(_1[0], __loc); } else { __ = Node({ type: 'Alternative', expressions: _1, }, __loc) } }], [6,0,() => { __loc = null; __ = [] }], [6,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); __ = _1.concat(_2) }], [7,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Node(Object.assign({type: 'Assertion'}, _1), __loc) }], [7,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); __ = _1; if (_2) { __ = Node({ type: 'Repetition', expression: _1, quantifier: _2, }, __loc) } }], [8,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = { kind: '^' } }], [8,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = { kind: '$' } }], [8,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = { kind: '\\b' } }], [8,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = { kind: '\\B' } }], [8,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = { kind: 'Lookahead', assertion: _2, } }], [8,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = { kind: 'Lookahead', negative: true, assertion: _2, } }], [8,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = { kind: 'Lookbehind', assertion: _2, } }], [8,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = { kind: 'Lookbehind', negative: true, assertion: _2, } }], [9,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [9,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [9,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'simple', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1.slice(1), 'simple', __loc); __.escaped = true; }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'unicode', __loc); __.isSurrogatePair = true; }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'unicode', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = UnicodeProperty(_1, __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'control', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'hex', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'oct', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = GroupRefOrDecChar(_1, __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'meta', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'meta', __loc) }], [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = NamedGroupRefOrChars(_1, _1loc) }], [11,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [11,0], [12,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [12,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); _1.greedy = false; __ = _1; }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Node({ type: 'Quantifier', kind: _1, greedy: true, }, __loc) }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Node({ type: 'Quantifier', kind: _1, greedy: true, }, __loc) }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Node({ type: 'Quantifier', kind: _1, greedy: true, }, __loc) }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); const range = getRange(_1); __ = Node({ type: 'Quantifier', kind: 'Range', from: range[0], to: range[0], greedy: true, }, __loc) }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Node({ type: 'Quantifier', kind: 'Range', from: getRange(_1)[0], greedy: true, }, __loc) }], [13,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); const range = getRange(_1); __ = Node({ type: 'Quantifier', kind: 'Range', from: range[0], to: range[1], greedy: true, }, __loc) }], [14,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [14,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [15,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); const nameRaw = String(_1); const name = decodeUnicodeGroupName(nameRaw); if (namedGroups.hasOwnProperty(name)) { throw new SyntaxError(`Duplicate of the named group "${name}".`); } namedGroups[name] = _1.groupNumber; __ = Node({ type: 'Group', capturing: true, name, nameRaw, number: _1.groupNumber, expression: _2, }, __loc); }], [15,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = Node({ type: 'Group', capturing: true, number: _1.groupNumber, expression: _2, }, __loc); }], [16,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = Node({ type: 'Group', capturing: false, expression: _2, }, __loc) }], [17,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = Node({ type: 'CharacterClass', negative: true, expressions: _2, }, __loc) }], [17,3,(_1,_2,_3,_1loc,_2loc,_3loc) => { __loc = yyloc(_1loc, _3loc); __ = Node({ type: 'CharacterClass', expressions: _2, }, __loc) }], [18,0,() => { __loc = null; __ = [] }], [18,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [19,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = [_1] }], [19,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); __ = [_1].concat(_2) }], [19,4,(_1,_2,_3,_4,_1loc,_2loc,_3loc,_4loc) => { __loc = yyloc(_1loc, _4loc); checkClassRange(_1, _3); __ = [ Node({ type: 'ClassRange', from: _1, to: _3, }, loc(_1loc, _3loc)) ]; if (_4) { __ = __.concat(_4); } }], [20,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [20,2,(_1,_2,_1loc,_2loc) => { __loc = yyloc(_1loc, _2loc); __ = [_1].concat(_2) }], [20,4,(_1,_2,_3,_4,_1loc,_2loc,_3loc,_4loc) => { __loc = yyloc(_1loc, _4loc); checkClassRange(_1, _3); __ = [ Node({ type: 'ClassRange', from: _1, to: _3, }, loc(_1loc, _3loc)), ]; if (_4) { __ = __.concat(_4); } }], [21,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'simple', __loc) }], [21,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [22,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }], [22,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'meta', __loc) }]]; /** * Encoded tokens map. */ const tokens = {"SLASH":"23","CHAR":"24","BAR":"25","BOS":"26","EOS":"27","ESC_b":"28","ESC_B":"29","POS_LA_ASSERT":"30","R_PAREN":"31","NEG_LA_ASSERT":"32","POS_LB_ASSERT":"33","NEG_LB_ASSERT":"34","ESC_CHAR":"35","U_CODE_SURROGATE":"36","U_CODE":"37","U_PROP_VALUE_EXP":"38","CTRL_CH":"39","HEX_CODE":"40","OCT_CODE":"41","DEC_CODE":"42","META_CHAR":"43","ANY":"44","NAMED_GROUP_REF":"45","Q_MARK":"46","STAR":"47","PLUS":"48","RANGE_EXACT":"49","RANGE_OPEN":"50","RANGE_CLOSED":"51","NAMED_CAPTURE_GROUP":"52","L_PAREN":"53","NON_CAPTURE_GROUP":"54","NEG_CLASS":"55","R_BRACKET":"56","L_BRACKET":"57","DASH":"58","$":"59"}; /** * Parsing table (generated by Syntax tool). */ const table = [{"0":1,"23":"s2"},{"59":"acc"},{"3":3,"4":4,"5":5,"6":6,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"23":"s7"},{"23":"r6","25":"s12"},{"23":"r7","25":"r7","31":"r7"},{"7":14,"8":15,"9":16,"10":25,"14":27,"15":42,"16":43,"17":26,"23":"r9","24":"s28","25":"r9","26":"s17","27":"s18","28":"s19","29":"s20","30":"s21","31":"r9","32":"s22","33":"s23","34":"s24","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","52":"s44","53":"s45","54":"s46","55":"s40","57":"s41"},{"1":8,"2":9,"24":"s10","59":"r3"},{"59":"r1"},{"24":"s11","59":"r2"},{"24":"r4","59":"r4"},{"24":"r5","59":"r5"},{"5":13,"6":6,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"23":"r8","25":"r8","31":"r8"},{"23":"r11","24":"r11","25":"r11","26":"r11","27":"r11","28":"r11","29":"r11","30":"r11","31":"r11","32":"r11","33":"r11","34":"r11","35":"r11","36":"r11","37":"r11","38":"r11","39":"r11","40":"r11","41":"r11","42":"r11","43":"r11","44":"r11","45":"r11","52":"r11","53":"r11","54":"r11","55":"r11","57":"r11"},{"23":"r12","24":"r12","25":"r12","26":"r12","27":"r12","28":"r12","29":"r12","30":"r12","31":"r12","32":"r12","33":"r12","34":"r12","35":"r12","36":"r12","37":"r12","38":"r12","39":"r12","40":"r12","41":"r12","42":"r12","43":"r12","44":"r12","45":"r12","52":"r12","53":"r12","54":"r12","55":"r12","57":"r12"},{"11":47,"12":48,"13":49,"23":"r38","24":"r38","25":"r38","26":"r38","27":"r38","28":"r38","29":"r38","30":"r38","31":"r38","32":"r38","33":"r38","34":"r38","35":"r38","36":"r38","37":"r38","38":"r38","39":"r38","40":"r38","41":"r38","42":"r38","43":"r38","44":"r38","45":"r38","46":"s52","47":"s50","48":"s51","49":"s53","50":"s54","51":"s55","52":"r38","53":"r38","54":"r38","55":"r38","57":"r38"},{"23":"r14","24":"r14","25":"r14","26":"r14","27":"r14","28":"r14","29":"r14","30":"r14","31":"r14","32":"r14","33":"r14","34":"r14","35":"r14","36":"r14","37":"r14","38":"r14","39":"r14","40":"r14","41":"r14","42":"r14","43":"r14","44":"r14","45":"r14","52":"r14","53":"r14","54":"r14","55":"r14","57":"r14"},{"23":"r15","24":"r15","25":"r15","26":"r15","27":"r15","28":"r15","29":"r15","30":"r15","31":"r15","32":"r15","33":"r15","34":"r15","35":"r15","36":"r15","37":"r15","38":"r15","39":"r15","40":"r15","41":"r15","42":"r15","43":"r15","44":"r15","45":"r15","52":"r15","53":"r15","54":"r15","55":"r15","57":"r15"},{"23":"r16","24":"r16","25":"r16","26":"r16","27":"r16","28":"r16","29":"r16","30":"r16","31":"r16","32":"r16","33":"r16","34":"r16","35":"r16","36":"r16","37":"r16","38":"r16","39":"r16","40":"r16","41":"r16","42":"r16","43":"r16","44":"r16","45":"r16","52":"r16","53":"r16","54":"r16","55":"r16","57":"r16"},{"23":"r17","24":"r17","25":"r17","26":"r17","27":"r17","28":"r17","29":"r17","30":"r17","31":"r17","32":"r17","33":"r17","34":"r17","35":"r17","36":"r17","37":"r17","38":"r17","39":"r17","40":"r17","41":"r17","42":"r17","43":"r17","44":"r17","45":"r17","52":"r17","53":"r17","54":"r17","55":"r17","57":"r17"},{"4":57,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"4":59,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"4":61,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"4":63,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"23":"r22","24":"r22","25":"r22","26":"r22","27":"r22","28":"r22","29":"r22","30":"r22","31":"r22","32":"r22","33":"r22","34":"r22","35":"r22","36":"r22","37":"r22","38":"r22","39":"r22","40":"r22","41":"r22","42":"r22","43":"r22","44":"r22","45":"r22","46":"r22","47":"r22","48":"r22","49":"r22","50":"r22","51":"r22","52":"r22","53":"r22","54":"r22","55":"r22","57":"r22"},{"23":"r23","24":"r23","25":"r23","26":"r23","27":"r23","28":"r23","29":"r23","30":"r23","31":"r23","32":"r23","33":"r23","34":"r23","35":"r23","36":"r23","37":"r23","38":"r23","39":"r23","40":"r23","41":"r23","42":"r23","43":"r23","44":"r23","45":"r23","46":"r23","47":"r23","48":"r23","49":"r23","50":"r23","51":"r23","52":"r23","53":"r23","54":"r23","55":"r23","57":"r23"},{"23":"r24","24":"r24","25":"r24","26":"r24","27":"r24","28":"r24","29":"r24","30":"r24","31":"r24","32":"r24","33":"r24","34":"r24","35":"r24","36":"r24","37":"r24","38":"r24","39":"r24","40":"r24","41":"r24","42":"r24","43":"r24","44":"r24","45":"r24","46":"r24","47":"r24","48":"r24","49":"r24","50":"r24","51":"r24","52":"r24","53":"r24","54":"r24","55":"r24","57":"r24"},{"23":"r25","24":"r25","25":"r25","26":"r25","27":"r25","28":"r25","29":"r25","30":"r25","31":"r25","32":"r25","33":"r25","34":"r25","35":"r25","36":"r25","37":"r25","38":"r25","39":"r25","40":"r25","41":"r25","42":"r25","43":"r25","44":"r25","45":"r25","46":"r25","47":"r25","48":"r25","49":"r25","50":"r25","51":"r25","52":"r25","53":"r25","54":"r25","55":"r25","56":"r25","57":"r25","58":"r25"},{"23":"r26","24":"r26","25":"r26","26":"r26","27":"r26","28":"r26","29":"r26","30":"r26","31":"r26","32":"r26","33":"r26","34":"r26","35":"r26","36":"r26","37":"r26","38":"r26","39":"r26","40":"r26","41":"r26","42":"r26","43":"r26","44":"r26","45":"r26","46":"r26","47":"r26","48":"r26","49":"r26","50":"r26","51":"r26","52":"r26","53":"r26","54":"r26","55":"r26","56":"r26","57":"r26","58":"r26"},{"23":"r27","24":"r27","25":"r27","26":"r27","27":"r27","28":"r27","29":"r27","30":"r27","31":"r27","32":"r27","33":"r27","34":"r27","35":"r27","36":"r27","37":"r27","38":"r27","39":"r27","40":"r27","41":"r27","42":"r27","43":"r27","44":"r27","45":"r27","46":"r27","47":"r27","48":"r27","49":"r27","50":"r27","51":"r27","52":"r27","53":"r27","54":"r27","55":"r27","56":"r27","57":"r27","58":"r27"},{"23":"r28","24":"r28","25":"r28","26":"r28","27":"r28","28":"r28","29":"r28","30":"r28","31":"r28","32":"r28","33":"r28","34":"r28","35":"r28","36":"r28","37":"r28","38":"r28","39":"r28","40":"r28","41":"r28","42":"r28","43":"r28","44":"r28","45":"r28","46":"r28","47":"r28","48":"r28","49":"r28","50":"r28","51":"r28","52":"r28","53":"r28","54":"r28","55":"r28","56":"r28","57":"r28","58":"r28"},{"23":"r29","24":"r29","25":"r29","26":"r29","27":"r29","28":"r29","29":"r29","30":"r29","31":"r29","32":"r29","33":"r29","34":"r29","35":"r29","36":"r29","37":"r29","38":"r29","39":"r29","40":"r29","41":"r29","42":"r29","43":"r29","44":"r29","45":"r29","46":"r29","47":"r29","48":"r29","49":"r29","50":"r29","51":"r29","52":"r29","53":"r29","54":"r29","55":"r29","56":"r29","57":"r29","58":"r29"},{"23":"r30","24":"r30","25":"r30","26":"r30","27":"r30","28":"r30","29":"r30","30":"r30","31":"r30","32":"r30","33":"r30","34":"r30","35":"r30","36":"r30","37":"r30","38":"r30","39":"r30","40":"r30","41":"r30","42":"r30","43":"r30","44":"r30","45":"r30","46":"r30","47":"r30","48":"r30","49":"r30","50":"r30","51":"r30","52":"r30","53":"r30","54":"r30","55":"r30","56":"r30","57":"r30","58":"r30"},{"23":"r31","24":"r31","25":"r31","26":"r31","27":"r31","28":"r31","29":"r31","30":"r31","31":"r31","32":"r31","33":"r31","34":"r31","35":"r31","36":"r31","37":"r31","38":"r31","39":"r31","40":"r31","41":"r31","42":"r31","43":"r31","44":"r31","45":"r31","46":"r31","47":"r31","48":"r31","49":"r31","50":"r31","51":"r31","52":"r31","53":"r31","54":"r31","55":"r31","56":"r31","57":"r31","58":"r31"},{"23":"r32","24":"r32","25":"r32","26":"r32","27":"r32","28":"r32","29":"r32","30":"r32","31":"r32","32":"r32","33":"r32","34":"r32","35":"r32","36":"r32","37":"r32","38":"r32","39":"r32","40":"r32","41":"r32","42":"r32","43":"r32","44":"r32","45":"r32","46":"r32","47":"r32","48":"r32","49":"r32","50":"r32","51":"r32","52":"r32","53":"r32","54":"r32","55":"r32","56":"r32","57":"r32","58":"r32"},{"23":"r33","24":"r33","25":"r33","26":"r33","27":"r33","28":"r33","29":"r33","30":"r33","31":"r33","32":"r33","33":"r33","34":"r33","35":"r33","36":"r33","37":"r33","38":"r33","39":"r33","40":"r33","41":"r33","42":"r33","43":"r33","44":"r33","45":"r33","46":"r33","47":"r33","48":"r33","49":"r33","50":"r33","51":"r33","52":"r33","53":"r33","54":"r33","55":"r33","56":"r33","57":"r33","58":"r33"},{"23":"r34","24":"r34","25":"r34","26":"r34","27":"r34","28":"r34","29":"r34","30":"r34","31":"r34","32":"r34","33":"r34","34":"r34","35":"r34","36":"r34","37":"r34","38":"r34","39":"r34","40":"r34","41":"r34","42":"r34","43":"r34","44":"r34","45":"r34","46":"r34","47":"r34","48":"r34","49":"r34","50":"r34","51":"r34","52":"r34","53":"r34","54":"r34","55":"r34","56":"r34","57":"r34","58":"r34"},{"23":"r35","24":"r35","25":"r35","26":"r35","27":"r35","28":"r35","29":"r35","30":"r35","31":"r35","32":"r35","33":"r35","34":"r35","35":"r35","36":"r35","37":"r35","38":"r35","39":"r35","40":"r35","41":"r35","42":"r35","43":"r35","44":"r35","45":"r35","46":"r35","47":"r35","48":"r35","49":"r35","50":"r35","51":"r35","52":"r35","53":"r35","54":"r35","55":"r35","56":"r35","57":"r35","58":"r35"},{"23":"r36","24":"r36","25":"r36","26":"r36","27":"r36","28":"r36","29":"r36","30":"r36","31":"r36","32":"r36","33":"r36","34":"r36","35":"r36","36":"r36","37":"r36","38":"r36","39":"r36","40":"r36","41":"r36","42":"r36","43":"r36","44":"r36","45":"r36","46":"r36","47":"r36","48":"r36","49":"r36","50":"r36","51":"r36","52":"r36","53":"r36","54":"r36","55":"r36","56":"r36","57":"r36","58":"r36"},{"10":70,"18":65,"19":66,"21":67,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r54","58":"s68"},{"10":70,"18":83,"19":66,"21":67,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r54","58":"s68"},{"23":"r47","24":"r47","25":"r47","26":"r47","27":"r47","28":"r47","29":"r47","30":"r47","31":"r47","32":"r47","33":"r47","34":"r47","35":"r47","36":"r47","37":"r47","38":"r47","39":"r47","40":"r47","41":"r47","42":"r47","43":"r47","44":"r47","45":"r47","46":"r47","47":"r47","48":"r47","49":"r47","50":"r47","51":"r47","52":"r47","53":"r47","54":"r47","55":"r47","57":"r47"},{"23":"r48","24":"r48","25":"r48","26":"r48","27":"r48","28":"r48","29":"r48","30":"r48","31":"r48","32":"r48","33":"r48","34":"r48","35":"r48","36":"r48","37":"r48","38":"r48","39":"r48","40":"r48","41":"r48","42":"r48","43":"r48","44":"r48","45":"r48","46":"r48","47":"r48","48":"r48","49":"r48","50":"r48","51":"r48","52":"r48","53":"r48","54":"r48","55":"r48","57":"r48"},{"4":85,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"4":87,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"4":89,"5":5,"6":6,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","45":"r10","52":"r10","53":"r10","54":"r10","55":"r10","57":"r10"},{"23":"r13","24":"r13","25":"r13","26":"r13","27":"r13","28":"r13","29":"r13","30":"r13","31":"r13","32":"r13","33":"r13","34":"r13","35":"r13","36":"r13","37":"r13","38":"r13","39":"r13","40":"r13","41":"r13","42":"r13","43":"r13","44":"r13","45":"r13","52":"r13","53":"r13","54":"r13","55":"r13","57":"r13"},{"23":"r37","24":"r37","25":"r37","26":"r37","27":"r37","28":"r37","29":"r37","30":"r37","31":"r37","32":"r37","33":"r37","34":"r37","35":"r37","36":"r37","37":"r37","38":"r37","39":"r37","40":"r37","41":"r37","42":"r37","43":"r37","44":"r37","45":"r37","52":"r37","53":"r37","54":"r37","55":"r37","57":"r37"},{"23":"r39","24":"r39","25":"r39","26":"r39","27":"r39","28":"r39","29":"r39","30":"r39","31":"r39","32":"r39","33":"r39","34":"r39","35":"r39","36":"r39","37":"r39","38":"r39","39":"r39","40":"r39","41":"r39","42":"r39","43":"r39","44":"r39","45":"r39","46":"s56","52":"r39","53":"r39","54":"r39","55":"r39","57":"r39"},{"23":"r41","24":"r41","25":"r41","26":"r41","27":"r41","28":"r41","29":"r41","30":"r41","31":"r41","32":"r41","33":"r41","34":"r41","35":"r41","36":"r41","37":"r41","38":"r41","39":"r41","40":"r41","41":"r41","42":"r41","43":"r41","44":"r41","45":"r41","46":"r41","52":"r41","53":"r41","54":"r41","55":"r41","57":"r41"},{"23":"r42","24":"r42","25":"r42","26":"r42","27":"r42","28":"r42","29":"r42","30":"r42","31":"r42","32":"r42","33":"r42","34":"r42","35":"r42","36":"r42","37":"r42","38":"r42","39":"r42","40":"r42","41":"r42","42":"r42","43":"r42","44":"r42","45":"r42","46":"r42","52":"r42","53":"r42","54":"r42","55":"r42","57":"r42"},{"23":"r43","24":"r43","25":"r43","26":"r43","27":"r43","28":"r43","29":"r43","30":"r43","31":"r43","32":"r43","33":"r43","34":"r43","35":"r43","36":"r43","37":"r43","38":"r43","39":"r43","40":"r43","41":"r43","42":"r43","43":"r43","44":"r43","45":"r43","46":"r43","52":"r43","53":"r43","54":"r43","55":"r43","57":"r43"},{"23":"r44","24":"r44","25":"r44","26":"r44","27":"r44","28":"r44","29":"r44","30":"r44","31":"r44","32":"r44","33":"r44","34":"r44","35":"r44","36":"r44","37":"r44","38":"r44","39":"r44","40":"r44","41":"r44","42":"r44","43":"r44","44":"r44","45":"r44","46":"r44","52":"r44","53":"r44","54":"r44","55":"r44","57":"r44"},{"23":"r45","24":"r45","25":"r45","26":"r45","27":"r45","28":"r45","29":"r45","30":"r45","31":"r45","32":"r45","33":"r45","34":"r45","35":"r45","36":"r45","37":"r45","38":"r45","39":"r45","40":"r45","41":"r45","42":"r45","43":"r45","44":"r45","45":"r45","46":"r45","52":"r45","53":"r45","54":"r45","55":"r45","57":"r45"},{"23":"r46","24":"r46","25":"r46","26":"r46","27":"r46","28":"r46","29":"r46","30":"r46","31":"r46","32":"r46","33":"r46","34":"r46","35":"r46","36":"r46","37":"r46","38":"r46","39":"r46","40":"r46","41":"r46","42":"r46","43":"r46","44":"r46","45":"r46","46":"r46","52":"r46","53":"r46","54":"r46","55":"r46","57":"r46"},{"23":"r40","24":"r40","25":"r40","26":"r40","27":"r40","28":"r40","29":"r40","30":"r40","31":"r40","32":"r40","33":"r40","34":"r40","35":"r40","36":"r40","37":"r40","38":"r40","39":"r40","40":"r40","41":"r40","42":"r40","43":"r40","44":"r40","45":"r40","52":"r40","53":"r40","54":"r40","55":"r40","57":"r40"},{"25":"s12","31":"s58"},{"23":"r18","24":"r18","25":"r18","26":"r18","27":"r18","28":"r18","29":"r18","30":"r18","31":"r18","32":"r18","33":"r18","34":"r18","35":"r18","36":"r18","37":"r18","38":"r18","39":"r18","40":"r18","41":"r18","42":"r18","43":"r18","44":"r18","45":"r18","52":"r18","53":"r18","54":"r18","55":"r18","57":"r18"},{"25":"s12","31":"s60"},{"23":"r19","24":"r19","25":"r19","26":"r19","27":"r19","28":"r19","29":"r19","30":"r19","31":"r19","32":"r19","33":"r19","34":"r19","35":"r19","36":"r19","37":"r19","38":"r19","39":"r19","40":"r19","41":"r19","42":"r19","43":"r19","44":"r19","45":"r19","52":"r19","53":"r19","54":"r19","55":"r19","57":"r19"},{"25":"s12","31":"s62"},{"23":"r20","24":"r20","25":"r20","26":"r20","27":"r20","28":"r20","29":"r20","30":"r20","31":"r20","32":"r20","33":"r20","34":"r20","35":"r20","36":"r20","37":"r20","38":"r20","39":"r20","40":"r20","41":"r20","42":"r20","43":"r20","44":"r20","45":"r20","52":"r20","53":"r20","54":"r20","55":"r20","57":"r20"},{"25":"s12","31":"s64"},{"23":"r21","24":"r21","25":"r21","26":"r21","27":"r21","28":"r21","29":"r21","30":"r21","31":"r21","32":"r21","33":"r21","34":"r21","35":"r21","36":"r21","37":"r21","38":"r21","39":"r21","40":"r21","41":"r21","42":"r21","43":"r21","44":"r21","45":"r21","52":"r21","53":"r21","54":"r21","55":"r21","57":"r21"},{"56":"s72"},{"56":"r55"},{"10":70,"20":73,"21":75,"22":76,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r56","58":"s74"},{"24":"r62","28":"r62","35":"r62","36":"r62","37":"r62","38":"r62","39":"r62","40":"r62","41":"r62","42":"r62","43":"r62","44":"r62","45":"r62","56":"r62","58":"r62"},{"24":"r63","28":"r63","35":"r63","36":"r63","37":"r63","38":"r63","39":"r63","40":"r63","41":"r63","42":"r63","43":"r63","44":"r63","45":"r63","56":"r63","58":"r63"},{"24":"r64","28":"r64","35":"r64","36":"r64","37":"r64","38":"r64","39":"r64","40":"r64","41":"r64","42":"r64","43":"r64","44":"r64","45":"r64","56":"r64","58":"r64"},{"24":"r65","28":"r65","35":"r65","36":"r65","37":"r65","38":"r65","39":"r65","40":"r65","41":"r65","42":"r65","43":"r65","44":"r65","45":"r65","56":"r65","58":"r65"},{"23":"r52","24":"r52","25":"r52","26":"r52","27":"r52","28":"r52","29":"r52","30":"r52","31":"r52","32":"r52","33":"r52","34":"r52","35":"r52","36":"r52","37":"r52","38":"r52","39":"r52","40":"r52","41":"r52","42":"r52","43":"r52","44":"r52","45":"r52","46":"r52","47":"r52","48":"r52","49":"r52","50":"r52","51":"r52","52":"r52","53":"r52","54":"r52","55":"r52","57":"r52"},{"56":"r57"},{"10":70,"21":77,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r62","58":"s68"},{"56":"r59"},{"10":70,"20":79,"21":75,"22":76,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r63","58":"s80"},{"10":70,"18":78,"19":66,"21":67,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r54","58":"s68"},{"56":"r58"},{"56":"r60"},{"10":70,"21":81,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r62","58":"s68"},{"10":70,"18":82,"19":66,"21":67,"22":69,"24":"s28","28":"s71","35":"s29","36":"s30","37":"s31","38":"s32","39":"s33","40":"s34","41":"s35","42":"s36","43":"s37","44":"s38","45":"s39","56":"r54","58":"s68"},{"56":"r61"},{"56":"s84"},{"23":"r53","24":"r53","25":"r53","26":"r53","27":"r53","28":"r53","29":"r53","30":"r53","31":"r53","32":"r53","33":"r53","34":"r53","35":"r53","36":"r53","37":"r53","38":"r53","39":"r53","40":"r53","41":"r53","42":"r53","43":"r53","44":"r53","45":"r53","46":"r53","47":"r53","48":"r53","49":"r53","50":"r53","51":"r53","52":"r53","53":"r53","54":"r53","55":"r53","57":"r53"},{"25":"s12","31":"s86"},{"23":"r49","24":"r49","25":"r49","26":"r49","27":"r49","28":"r49","29":"r49","30":"r49","31":"r49","32":"r49","33":"r49","34":"r49","35":"r49","36":"r49","37":"r49","38":"r49","39":"r49","40":"r49","41":"r49","42":"r49","43":"r49","44":"r49","45":"r49","46":"r49","47":"r49","48":"r49","49":"r49","50":"r49","51":"r49","52":"r49","53":"r49","54":"r49","55":"r49","57":"r49"},{"25":"s12","31":"s88"},{"23":"r50","24":"r50","25":"r50","26":"r50","27":"r50","28":"r50","29":"r50","30":"r50","31":"r50","32":"r50","33":"r50","34":"r50","35":"r50","36":"r50","37":"r50","38":"r50","39":"r50","40":"r50","41":"r50","42":"r50","43":"r50","44":"r50","45":"r50","46":"r50","47":"r50","48":"r50","49":"r50","50":"r50","51":"r50","52":"r50","53":"r50","54":"r50","55":"r50","57":"r50"},{"25":"s12","31":"s90"},{"23":"r51","24":"r51","25":"r51","26":"r51","27":"r51","28":"r51","29":"r51","30":"r51","31":"r51","32":"r51","33":"r51","34":"r51","35":"r51","36":"r51","37":"r51","38":"r51","39":"r51","40":"r51","41":"r51","42":"r51","43":"r51","44":"r51","45":"r51","46":"r51","47":"r51","48":"r51","49":"r51","50":"r51","51":"r51","52":"r51","53":"r51","54":"r51","55":"r51","57":"r51"}]; /** * Parsing stack. */ const stack = []; /** * Tokenizer instance. */ let tokenizer; /** * Generic tokenizer used by the parser in the Syntax tool. * * https://www.npmjs.com/package/syntax-cli * * See `--custom-tokinzer` to skip this generation, and use a custom one. */ const lexRules = [[/^#[^\n]+/, function() { /* skip comments */ }], [/^\s+/, function() { /* skip whitespace */ }], [/^-/, function() { return 'DASH' }], [/^\//, function() { return 'CHAR' }], [/^#/, function() { return 'CHAR' }], [/^\|/, function() { return 'CHAR' }], [/^\./, function() { return 'CHAR' }], [/^\{/, function() { return 'CHAR' }], [/^\{\d+\}/, function() { return 'RANGE_EXACT' }], [/^\{\d+,\}/, function() { return 'RANGE_OPEN' }], [/^\{\d+,\d+\}/, function() { return 'RANGE_CLOSED' }], [/^\\k<(([\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4-\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5-\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a-\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\ude80-\ude9c\udea0-\uded0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf75\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00\ude10-\ude13\ude15-\ude17\ude19-\ude35\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee4\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd23\udf00-\udf1c\udf27\udf30-\udf45\udfe0-\udff6]|\ud804[\udc03-\udc37\udc83-\udcaf\udcd0-\udce8\udd03-\udd26\udd44\udd50-\udd72\udd76\udd83-\uddb2\uddc1-\uddc4\uddda\udddc\ude00-\ude11\ude13-\ude2b\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udede\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3d\udf50\udf5d-\udf61]|\ud805[\udc00-\udc34\udc47-\udc4a\udc5f\udc80-\udcaf\udcc4-\udcc5\udcc7\udd80-\uddae\uddd8-\udddb\ude00-\ude2f\ude44\ude80-\udeaa\udeb8\udf00-\udf1a]|\ud806[\udc00-\udc2b\udca0-\udcdf\udcff\udda0-\udda7\uddaa-\uddd0\udde1\udde3\ude00\ude0b-\ude32\ude3a\ude50\ude5c-\ude89\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc2e\udc40\udc72-\udc8f\udd00-\udd06\udd08-\udd09\udd0b-\udd30\udd46\udd60-\udd65\udd67-\udd68\udd6a-\udd89\udd98\udee0-\udef2]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udf00-\udf2f\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf50\udf93-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb]|\ud838[\udd00-\udd2c\udd37-\udd3d\udd4e\udec0-\udeeb]|\ud83a[\udc00-\udcc4\udd00-\udd43\udd4b]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\}))(([\u0030-\u0039\u0041-\u005a\u005f\u0061-\u007a\u00aa\u00b5\u00b7\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u05d0-\u05ea\u05ef-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u07fd\u0800-\u082d\u0840-\u085b\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u08d3-\u08e1\u08e3-\u0963\u0966-\u096f\u0971-\u0983\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7-\u09c8\u09cb-\u09ce\u09d7\u09dc-\u09dd\u09df-\u09e3\u09e6-\u09f1\u09fc\u09fe\u0a01-\u0a03\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0af9-\u0aff\u0b01-\u0b03\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b5c-\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82-\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c00-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c58-\u0c5a\u0c60-\u0c63\u0c66-\u0c6f\u0c80-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1-\u0cf2\u0d00-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d54-\u0d57\u0d5f-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82-\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18-\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1369-\u1371\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772-\u1773\u1780-\u17d3\u17d7\u17dc-\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1878\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1ab0-\u1abd\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1cd0-\u1cd2\u1cd4-\u1cfa\u1d00-\u1df9\u1dfb-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua8fd-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\ua9e0-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabea\uabec-\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2f\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udca0-\udca9\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00-\ude03\ude05-\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude35\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd27\udd30-\udd39\udf00-\udf1c\udf27\udf30-\udf50\udfe0-\udff6]|\ud804[\udc00-\udc46\udc66-\udc6f\udc7f-\udcba\udcd0-\udce8\udcf0-\udcf9\udd00-\udd34\udd36-\udd3f\udd44-\udd46\udd50-\udd73\udd76\udd80-\uddc4\uddc9-\uddcc\uddd0-\uddda\udddc\ude00-\ude11\ude13-\ude37\ude3e\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udeea\udef0-\udef9\udf00-\udf03\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3b-\udf44\udf47-\udf48\udf4b-\udf4d\udf50\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc00-\udc4a\udc50-\udc59\udc5e-\udc5f\udc80-\udcc5\udcc7\udcd0-\udcd9\udd80-\uddb5\uddb8-\uddc0\uddd8-\udddd\ude00-\ude40\ude44\ude50-\ude59\ude80-\udeb8\udec0-\udec9\udf00-\udf1a\udf1d-\udf2b\udf30-\udf39]|\ud806[\udc00-\udc3a\udca0-\udce9\udcff\udda0-\udda7\uddaa-\uddd7\uddda-\udde1\udde3-\udde4\ude00-\ude3e\ude47\ude50-\ude99\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc36\udc38-\udc40\udc50-\udc59\udc72-\udc8f\udc92-\udca7\udca9-\udcb6\udd00-\udd06\udd08-\udd09\udd0b-\udd36\udd3a\udd3c-\udd3d\udd3f-\udd47\udd50-\udd59\udd60-\udd65\udd67-\udd68\udd6a-\udd8e\udd90-\udd91\udd93-\udd98\udda0-\udda9\udee0-\udef6]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\ude60-\ude69\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf50-\udf59\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf4f-\udf87\udf8f-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d-\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb\udfce-\udfff]|\ud836[\ude00-\ude36\ude3b-\ude6c\ude75\ude84\ude9b-\ude9f\udea1-\udeaf]|\ud838[\udc00-\udc06\udc08-\udc18\udc1b-\udc21\udc23-\udc24\udc26-\udc2a\udd00-\udd2c\udd30-\udd3d\udd40-\udd49\udd4e\udec0-\udef9]|\ud83a[\udc00-\udcc4\udcd0-\udcd6\udd00-\udd4b\udd50-\udd59]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\})|[\u200c\u200d])*>/, function() { const groupName = yytext.slice(3, -1); validateUnicodeGroupName(groupName, this.getCurrentState()); return 'NAMED_GROUP_REF'; }], [/^\\b/, function() { return 'ESC_b' }], [/^\\B/, function() { return 'ESC_B' }], [/^\\c[a-zA-Z]/, function() { return 'CTRL_CH' }], [/^\\0\d{1,2}/, function() { return 'OCT_CODE' }], [/^\\0/, function() { return 'DEC_CODE' }], [/^\\\d{1,3}/, function() { return 'DEC_CODE' }], [/^\\u[dD][89abAB][0-9a-fA-F]{2}\\u[dD][c-fC-F][0-9a-fA-F]{2}/, function() { return 'U_CODE_SURROGATE' }], [/^\\u\{[0-9a-fA-F]{1,}\}/, function() { return 'U_CODE' }], [/^\\u[0-9a-fA-F]{4}/, function() { return 'U_CODE' }], [/^\\[pP]\{\w+(?:=\w+)?\}/, function() { return 'U_PROP_VALUE_EXP' }], [/^\\x[0-9a-fA-F]{2}/, function() { return 'HEX_CODE' }], [/^\\[tnrdDsSwWvf]/, function() { return 'META_CHAR' }], [/^\\\//, function() { return 'ESC_CHAR' }], [/^\\[ #]/, function() { return 'ESC_CHAR' }], [/^\\[\^\$\.\*\+\?\(\)\\\[\]\{\}\|\/]/, function() { return 'ESC_CHAR' }], [/^\\[^*?+\[()\\|]/, function() { const s = this.getCurrentState(); if (s === 'u' || s === 'xu' || s === 'u_class') { throw new SyntaxError(`invalid Unicode escape ${yytext}`); } return 'ESC_CHAR'; }], [/^\(/, function() { return 'CHAR' }], [/^\)/, function() { return 'CHAR' }], [/^\(\?=/, function() { return 'POS_LA_ASSERT' }], [/^\(\?!/, function() { return 'NEG_LA_ASSERT' }], [/^\(\?<=/, function() { return 'POS_LB_ASSERT' }], [/^\(\?/, function() { yytext = yytext.slice(3, -1); validateUnicodeGroupName(yytext, this.getCurrentState()); return 'NAMED_CAPTURE_GROUP'; }], [/^\(/, function() { return 'L_PAREN' }], [/^\)/, function() { return 'R_PAREN' }], [/^[*?+[^$]/, function() { return 'CHAR' }], [/^\\\]/, function() { return 'ESC_CHAR' }], [/^\]/, function() { this.popState(); return 'R_BRACKET' }], [/^\^/, function() { return 'BOS' }], [/^\$/, function() { return 'EOS' }], [/^\*/, function() { return 'STAR' }], [/^\?/, function() { return 'Q_MARK' }], [/^\+/, function() { return 'PLUS' }], [/^\|/, function() { return 'BAR' }], [/^\./, function() { return 'ANY' }], [/^\//, function() { return 'SLASH' }], [/^[^*?+\[()\\|]/, function() { return 'CHAR' }], [/^\[\^/, function() { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'NEG_CLASS' }], [/^\[/, function() { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'L_BRACKET' }]]; const lexRulesByConditions = {"INITIAL":[8,9,10,11,12,13,14,15,16,17,20,22,23,24,26,27,30,31,32,33,34,35,36,37,41,42,43,44,45,46,47,48,49,50,51],"u":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,30,31,32,33,34,35,36,37,41,42,43,44,45,46,47,48,49,50,51],"xu":[0,1,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,30,31,32,33,34,35,36,37,41,42,43,44,45,46,47,48,49,50,51],"x":[0,1,8,9,10,11,12,13,14,15,16,17,20,22,23,24,26,27,30,31,32,33,34,35,36,37,41,42,43,44,45,46,47,48,49,50,51],"u_class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],"class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51]}; const EOF_TOKEN = { type: EOF, value: '', }; tokenizer = { initString(string) { this._string = string; this._cursor = 0; this._states = ['INITIAL']; this._tokensQueue = []; this._currentLine = 1; this._currentColumn = 0; this._currentLineBeginOffset = 0; /** * Matched token location data. */ this._tokenStartOffset = 0; this._tokenEndOffset = 0; this._tokenStartLine = 1; this._tokenEndLine = 1; this._tokenStartColumn = 0; this._tokenEndColumn = 0; return this; }, /** * Returns tokenizer states. */ getStates() { return this._states; }, getCurrentState() { return this._states[this._states.length - 1]; }, pushState(state) { this._states.push(state); }, begin(state) { this.pushState(state); }, popState() { if (this._states.length > 1) { return this._states.pop(); } return this._states[0]; }, getNextToken() { // Something was queued, return it. if (this._tokensQueue.length > 0) { return this.onToken(this._toToken(this._tokensQueue.shift())); } if (!this.hasMoreTokens()) { return this.onToken(EOF_TOKEN); } let string = this._string.slice(this._cursor); let lexRulesForState = lexRulesByConditions[this.getCurrentState()]; for (let i = 0; i < lexRulesForState.length; i++) { let lexRuleIndex = lexRulesForState[i]; let lexRule = lexRules[lexRuleIndex]; let matched = this._match(string, lexRule[0]); // Manual handling of EOF token (the end of string). Return it // as `EOF` symbol. if (string === '' && matched === '') { this._cursor++; } if (matched !== null) { yytext = matched; yyleng = yytext.length; let token = lexRule[1].call(this); if (!token) { return this.getNextToken(); } // If multiple tokens are returned, save them to return // on next `getNextToken` call. if (Array.isArray(token)) { const tokensToQueue = token.slice(1); token = token[0]; if (tokensToQueue.length > 0) { this._tokensQueue.unshift(...tokensToQueue); } } return this.onToken(this._toToken(token, yytext)); } } if (this.isEOF()) { this._cursor++; return EOF_TOKEN; } this.throwUnexpectedToken( string[0], this._currentLine, this._currentColumn ); }, /** * Throws default "Unexpected token" exception, showing the actual * line from the source, pointing with the ^ marker to the bad token. * In addition, shows `line:column` location. */ throwUnexpectedToken(symbol, line, column) { const lineSource = this._string.split('\n')[line - 1]; let lineData = ''; if (lineSource) { const pad = ' '.repeat(column); lineData = '\n\n' + lineSource + '\n' + pad + '^\n'; } throw new SyntaxError( `${lineData}Unexpected token: "${symbol}" ` + `at ${line}:${column}.` ); }, getCursor() { return this._cursor; }, getCurrentLine() { return this._currentLine; }, getCurrentColumn() { return this._currentColumn; }, _captureLocation(matched) { const nlRe = /\n/g; // Absolute offsets. this._tokenStartOffset = this._cursor; // Line-based locations, start. this._tokenStartLine = this._currentLine; this._tokenStartColumn = this._tokenStartOffset - this._currentLineBeginOffset; // Extract `\n` in the matched token. let nlMatch; while ((nlMatch = nlRe.exec(matched)) !== null) { this._currentLine++; this._currentLineBeginOffset = this._tokenStartOffset + nlMatch.index + 1; } this._tokenEndOffset = this._cursor + matched.length; // Line-based locations, end. this._tokenEndLine = this._currentLine; this._tokenEndColumn = this._currentColumn = (this._tokenEndOffset - this._currentLineBeginOffset); }, _toToken(tokenType, yytext = '') { return { // Basic data. type: tokenType, value: yytext, // Location data. startOffset: this._tokenStartOffset, endOffset: this._tokenEndOffset, startLine: this._tokenStartLine, endLine: this._tokenEndLine, startColumn: this._tokenStartColumn, endColumn: this._tokenEndColumn, }; }, isEOF() { return this._cursor === this._string.length; }, hasMoreTokens() { return this._cursor <= this._string.length; }, _match(string, regexp) { let matched = string.match(regexp); if (matched) { // Handle `\n` in the matched token to track line numbers. this._captureLocation(matched[0]); this._cursor += matched[0].length; return matched[0]; } return null; }, /** * Allows analyzing, and transforming token. Default implementation * just passes the token through. */ onToken(token) { return token; }, }; /** * Expose tokenizer so it can be accessed in semantic actions. */ yy.lexer = tokenizer; yy.tokenizer = tokenizer; /** * Global parsing options. Some options can be shadowed per * each `parse` call, if the optations are passed. * * Initalized to the `captureLocations` which is passed * from the generator. Other options can be added at runtime. */ yy.options = { captureLocations: true, }; /** * Parsing module. */ const yyparse = { /** * Sets global parsing options. */ setOptions(options) { yy.options = options; return this; }, /** * Returns parsing options. */ getOptions() { return yy.options; }, /** * Parses a string. */ parse(string, parseOptions) { if (!tokenizer) { throw new Error(`Tokenizer instance wasn't specified.`); } tokenizer.initString(string); /** * If parse options are passed, override global parse options for * this call, and later restore global options. */ let globalOptions = yy.options; if (parseOptions) { yy.options = Object.assign({}, yy.options, parseOptions); } /** * Allow callers to do setup work based on the * parsing string, and passed options. */ yyparse.onParseBegin(string, tokenizer, yy.options); stack.length = 0; stack.push(0); let token = tokenizer.getNextToken(); let shiftedToken = null; do { if (!token) { // Restore options. yy.options = globalOptions; unexpectedEndOfInput(); } let state = stack[stack.length - 1]; let column = tokens[token.type]; if (!table[state].hasOwnProperty(column)) { yy.options = globalOptions; unexpectedToken(token); } let entry = table[state][column]; // Shift action. if (entry[0] === 's') { let loc = null; if (yy.options.captureLocations) { loc = { startOffset: token.startOffset, endOffset: token.endOffset, startLine: token.startLine, endLine: token.endLine, startColumn: token.startColumn, endColumn: token.endColumn, }; } shiftedToken = this.onShift(token); stack.push( {symbol: tokens[shiftedToken.type], semanticValue: shiftedToken.value, loc}, Number(entry.slice(1)) ); token = tokenizer.getNextToken(); } // Reduce action. else if (entry[0] === 'r') { let productionNumber = entry.slice(1); let production = productions[productionNumber]; let hasSemanticAction = typeof production[2] === 'function'; let semanticValueArgs = hasSemanticAction ? [] : null; const locationArgs = ( hasSemanticAction && yy.options.captureLocations ? [] : null ); if (production[1] !== 0) { let rhsLength = production[1]; while (rhsLength-- > 0) { stack.pop(); let stackEntry = stack.pop(); if (hasSemanticAction) { semanticValueArgs.unshift(stackEntry.semanticValue); if (locationArgs) { locationArgs.unshift(stackEntry.loc); } } } } const reduceStackEntry = {symbol: production[0]}; if (hasSemanticAction) { yytext = shiftedToken ? shiftedToken.value : null; yyleng = shiftedToken ? shiftedToken.value.length : null; const semanticActionArgs = ( locationArgs !== null ? semanticValueArgs.concat(locationArgs) : semanticValueArgs ); production[2](...semanticActionArgs); reduceStackEntry.semanticValue = __; if (locationArgs) { reduceStackEntry.loc = __loc; } } const nextState = stack[stack.length - 1]; const symbolToReduceWith = production[0]; stack.push( reduceStackEntry, table[nextState][symbolToReduceWith] ); } // Accept. else if (entry === 'acc') { stack.pop(); let parsed = stack.pop(); if (stack.length !== 1 || stack[0] !== 0 || tokenizer.hasMoreTokens()) { // Restore options. yy.options = globalOptions; unexpectedToken(token); } if (parsed.hasOwnProperty('semanticValue')) { yy.options = globalOptions; yyparse.onParseEnd(parsed.semanticValue); return parsed.semanticValue; } yyparse.onParseEnd(); // Restore options. yy.options = globalOptions; return true; } } while (tokenizer.hasMoreTokens() || stack.length > 1); }, setTokenizer(customTokenizer) { tokenizer = customTokenizer; return yyparse; }, getTokenizer() { return tokenizer; }, onParseBegin(string, tokenizer, options) {}, onParseEnd(parsed) {}, /** * Allows analyzing, and transforming shifted token. Default implementation * just passes the token through. */ onShift(token) { return token; }, }; /** * Tracks capturing groups. */ let capturingGroupsCount = 0; /** * Tracks named groups. */ let namedGroups = {}; /** * Parsing string. */ let parsingString = ''; yyparse.onParseBegin = (string, lexer) => { parsingString = string; capturingGroupsCount = 0; namedGroups = {}; const lastSlash = string.lastIndexOf('/'); const flags = string.slice(lastSlash); if (flags.includes('x') && flags.includes('u')) { lexer.pushState('xu'); } else { if (flags.includes('x')) { lexer.pushState('x'); } if (flags.includes('u')) { lexer.pushState('u'); } } }; /** * On shifting `(` remember its number to used on reduce. */ yyparse.onShift = token => { if (token.type === 'L_PAREN' || token.type === 'NAMED_CAPTURE_GROUP') { token.value = new String(token.value); token.value.groupNumber = ++capturingGroupsCount; } return token; }; /** * Extracts ranges from the range string. */ function getRange(text) { const range = text.match(/\d+/g).map(Number); if (Number.isFinite(range[1]) && range[1] < range[0]) { throw new SyntaxError(`Numbers out of order in ${text} quantifier`); } return range; } /** * Checks class range */ function checkClassRange(from, to) { if (from.kind === 'control' || to.kind === 'control' || (!isNaN(from.codePoint) && !isNaN(to.codePoint) && from.codePoint > to.codePoint)) { throw new SyntaxError(`Range ${from.value}-${to.value} out of order in character class`); } } // ---------------------- Unicode property ------------------------------------------- const unicodeProperties = require('../unicode/parser-unicode-properties.js'); /** * Unicode property. */ function UnicodeProperty(matched, loc) { const negative = matched[1] === 'P'; const separatorIdx = matched.indexOf('='); let name = matched.slice(3, separatorIdx !== -1 ? separatorIdx : -1); let value; // General_Category allows using only value as a shorthand. const isShorthand = separatorIdx === -1 && unicodeProperties.isGeneralCategoryValue(name); // Binary propery name. const isBinaryProperty = separatorIdx === -1 && unicodeProperties.isBinaryPropertyName(name); if (isShorthand) { value = name; name = 'General_Category'; } else if (isBinaryProperty) { value = name; } else { if (!unicodeProperties.isValidName(name)) { throw new SyntaxError(`Invalid unicode property name: ${name}.`); } value = matched.slice(separatorIdx + 1, -1); if (!unicodeProperties.isValidValue(name, value)) { throw new SyntaxError(`Invalid ${name} unicode property value: ${value}.`); } } return Node({ type: 'UnicodeProperty', name, value, negative, shorthand: isShorthand, binary: isBinaryProperty, canonicalName: unicodeProperties.getCanonicalName(name) || name, canonicalValue: unicodeProperties.getCanonicalValue(value) || value, }, loc); } // ---------------------------------------------------------------------------------- /** * Creates a character node. */ function Char(value, kind, loc) { let symbol; let codePoint; switch (kind) { case 'decimal': { codePoint = Number(value.slice(1)); symbol = String.fromCodePoint(codePoint); break; } case 'oct': { codePoint = parseInt(value.slice(1), 8); symbol = String.fromCodePoint(codePoint); break; } case 'hex': case 'unicode': { if (value.lastIndexOf('\\u') > 0) { let [lead, trail] = value.split('\\u').slice(1); lead = parseInt(lead, 16); trail = parseInt(trail, 16); codePoint = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000; symbol = String.fromCodePoint(codePoint); } else { const hex = value.slice(2).replace('{', ''); codePoint = parseInt(hex, 16); if (codePoint > 0x10ffff) { throw new SyntaxError(`Bad character escape sequence: ${value}`); } symbol = String.fromCodePoint(codePoint); } break; } case 'meta': { switch (value) { case '\\t': symbol = '\t'; codePoint = symbol.codePointAt(0); break; case '\\n': symbol = '\n'; codePoint = symbol.codePointAt(0); break; case '\\r': symbol = '\r'; codePoint = symbol.codePointAt(0); break; case '\\v': symbol = '\v'; codePoint = symbol.codePointAt(0); break; case '\\f': symbol = '\f'; codePoint = symbol.codePointAt(0); break; case '\\b': symbol = '\b'; codePoint = symbol.codePointAt(0); case '\\0': symbol = '\0'; codePoint = 0; case '.': symbol = '.'; codePoint = NaN; break; default: codePoint = NaN; } break; } case 'simple': { symbol = value; codePoint = symbol.codePointAt(0); break; } } return Node({ type: 'Char', value, kind, symbol, codePoint, }, loc); } /** * Valid flags per current ECMAScript spec and * stage 3+ proposals. */ const validFlags = 'gimsuxy'; /** * Checks the flags are valid, and that * we don't duplicate flags. */ function checkFlags(flags) { const seen = new Set(); for (const flag of flags) { if (seen.has(flag) || !validFlags.includes(flag)) { throw new SyntaxError(`Invalid flags: ${flags}`); } seen.add(flag); } return flags.split('').sort().join(''); } /** * Parses patterns like \1, \2, etc. either as a backreference * to a group, or a deciaml char code. */ function GroupRefOrDecChar(text, textLoc) { const reference = Number(text.slice(1)); if (reference > 0 && reference <= capturingGroupsCount) { return Node({ type: 'Backreference', kind: 'number', number: reference, reference, }, textLoc); } return Char(text, 'decimal', textLoc); } /** * Unicode names. */ const uReStart = /^\\u[0-9a-fA-F]{4}/; // only matches start of string const ucpReStart = /^\\u\{[0-9a-fA-F]{1,}\}/; // only matches start of string const ucpReAnywhere = /\\u\{[0-9a-fA-F]{1,}\}/; // matches anywhere in string /** * Validates Unicode group name. */ function validateUnicodeGroupName(name, state) { const isUnicodeName = ucpReAnywhere.test(name); const isUnicodeState = (state === 'u' || state === 'xu' || state === 'u_class'); if (isUnicodeName && !isUnicodeState) { throw new SyntaxError(`invalid group Unicode name "${name}", use \`u\` flag.`); } return name; } // Matches the following production: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence // // RegExpUnicodeEscapeSequence :: // `u` LeadSurrogate `\u` TrailSurrogate # as 'leadSurrogate', 'trailSurrogate' // `u` LeadSurrogate # as 'leadSurrogateOnly' // `u` TrailSurrogate # as 'trailSurrogateOnly' // `u` NonSurrogate # as 'nonSurrogate' // `u` `{` CodePoint `}` # as 'codePoint' // // LeadSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xD800 to 0xDBFF # [dD][89aAbB][0-9a-fA-F]{2} // // TrailSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xDC00 to 0xDFFF # [dD][c-fC-F][0-9a-fA-F]{2} // // NonSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is not in the inclusive range 0xD800 to 0xDFFF # [0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2} // // CodePoint :: // HexDigits but only if MV of HexDigits ≤ 0x10FFFF # 0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4}) // const uidRe = /\\u(?:([dD][89aAbB][0-9a-fA-F]{2})\\u([dD][c-fC-F][0-9a-fA-F]{2})|([dD][89aAbB][0-9a-fA-F]{2})|([dD][c-fC-F][0-9a-fA-F]{2})|([0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2})|\{(0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4}))\})/; function decodeUnicodeGroupName(name) { return name.replace(new RegExp(uidRe, 'g'), function (_, leadSurrogate, trailSurrogate, leadSurrogateOnly, trailSurrogateOnly, nonSurrogate, codePoint) { if (leadSurrogate) { return String.fromCodePoint(parseInt(leadSurrogate, 16), parseInt(trailSurrogate, 16)); } if (leadSurrogateOnly) { return String.fromCodePoint(parseInt(leadSurrogateOnly, 16)); } if (trailSurrogateOnly) { // TODO: Per the spec: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence // > Each `\u` TrailSurrogate for which the choice of associated `u` LeadSurrogate is ambiguous shall be associated with the nearest possible `u` LeadSurrogate that would otherwise have no corresponding `\u` TrailSurrogate. return String.fromCodePoint(parseInt(trailSurrogateOnly, 16)); } if (nonSurrogate) { return String.fromCodePoint(parseInt(nonSurrogate, 16)); } if (codePoint) { return String.fromCodePoint(parseInt(codePoint, 16)); } return _; }); } /** * Extracts from `\k` pattern either a backreference * to a named capturing group (if it presents), or parses it * as a list of char: `\k`, `<`, `f`, etc. */ function NamedGroupRefOrChars(text, textLoc) { const referenceRaw = text.slice(3, -1); const reference = decodeUnicodeGroupName(referenceRaw); if (namedGroups.hasOwnProperty(reference)) { return Node({ type: 'Backreference', kind: 'name', number: namedGroups[reference], reference, referenceRaw, }, textLoc); } // Else `\k` should be parsed as a list of `Char`s. // This is really a 0.01% edge case, but we should handle it. let startOffset = null; let startLine = null; let endLine = null; let startColumn = null; if (textLoc) { startOffset = textLoc.startOffset; startLine = textLoc.startLine; endLine = textLoc.endLine; startColumn = textLoc.startColumn; } const charRe = /^[\w$<>]/; let loc; const chars = [ // Init to first \k, taking 2 symbols. Char( text.slice(1, 2), 'simple', startOffset ? { startLine, endLine, startColumn, startOffset, endOffset: startOffset += 2, endColumn: startColumn += 2, } : null ), ]; // For \k chars[0].escaped = true; // Other symbols. text = text.slice(2); while (text.length > 0) { let matched = null; // Unicode, \u003B or \u{003B} if ((matched = text.match(uReStart)) || (matched = text.match(ucpReStart))) { if (startOffset) { loc = { startLine, endLine, startColumn, startOffset, endOffset: (startOffset += matched[0].length), endColumn: (startColumn += matched[0].length), }; } chars.push(Char(matched[0], 'unicode', loc)); text = text.slice(matched[0].length); } // Simple char. else if ((matched = text.match(charRe))) { if (startOffset) { loc = { startLine, endLine, startColumn, startOffset, endOffset: ++startOffset, endColumn: ++startColumn, }; } chars.push(Char(matched[0], 'simple', loc)); text = text.slice(1); } } return chars; } /** * Creates an AST node with a location. */ function Node(node, loc) { if (yy.options.captureLocations) { node.loc = { source: parsingString.slice(loc.startOffset, loc.endOffset), start: { line: loc.startLine, column: loc.startColumn, offset: loc.startOffset, }, end: { line: loc.endLine, column: loc.endColumn, offset: loc.endOffset, }, }; } return node; } /** * Creates location node. */ function loc(start, end) { if (!yy.options.captureLocations) { return null; } return { startOffset: start.startOffset, endOffset: end.endOffset, startLine: start.startLine, endLine: end.endLine, startColumn: start.startColumn, endColumn: end.endColumn, }; } function unexpectedToken(token) { if (token.type === EOF) { unexpectedEndOfInput(); } tokenizer.throwUnexpectedToken( token.value, token.startLine, token.startColumn ); } function unexpectedEndOfInput() { parseError(`Unexpected end of input.`); } function parseError(message) { throw new SyntaxError(message); } module.exports = yyparse; regexp-tree-0.1.18/src/parser/index.js000066400000000000000000000013471361507317500175750ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const regexpTreeParser = require('./generated/regexp-tree'); /** * Original parse function. */ const 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; regexp-tree-0.1.18/src/parser/regexp.bnf000066400000000000000000001246111361507317500201110ustar00rootroot00000000000000/** * BNF grammar for Regular Expressions. * * Based on RegExp grammar from ECMAScript: * http://www.ecma-international.org/ecma-262/7.0/#sec-patterns * * Parser generated by Syntax tool (npm: syntax-cli) * * Dmitry Soshnikov * * MIT Style License. */ // -------------------------------------------- // 1. Lexical grammar. %lex CHAR [^*?+\[()\\|] ESC \\ U [0-9a-fA-F] U_LEAD_SURROGATE [dD][89abAB][0-9a-fA-F]{2} U_TRAIL_SURROGATE [dD][c-fC-F][0-9a-fA-F]{2} /** * Generated by scripts/generate-unicode-id-parts.js on node v10.16.0 with unicode 12.1 * based on http://www.unicode.org/reports/tr31/ and https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords * U_ID_START corresponds to the ID_Start property, and U_ID_CONTINUE corresponds to ID_Continue property */ U_ID_START ([\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4-\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5-\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a-\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\ude80-\ude9c\udea0-\uded0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf75\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00\ude10-\ude13\ude15-\ude17\ude19-\ude35\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee4\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd23\udf00-\udf1c\udf27\udf30-\udf45\udfe0-\udff6]|\ud804[\udc03-\udc37\udc83-\udcaf\udcd0-\udce8\udd03-\udd26\udd44\udd50-\udd72\udd76\udd83-\uddb2\uddc1-\uddc4\uddda\udddc\ude00-\ude11\ude13-\ude2b\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udede\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3d\udf50\udf5d-\udf61]|\ud805[\udc00-\udc34\udc47-\udc4a\udc5f\udc80-\udcaf\udcc4-\udcc5\udcc7\udd80-\uddae\uddd8-\udddb\ude00-\ude2f\ude44\ude80-\udeaa\udeb8\udf00-\udf1a]|\ud806[\udc00-\udc2b\udca0-\udcdf\udcff\udda0-\udda7\uddaa-\uddd0\udde1\udde3\ude00\ude0b-\ude32\ude3a\ude50\ude5c-\ude89\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc2e\udc40\udc72-\udc8f\udd00-\udd06\udd08-\udd09\udd0b-\udd30\udd46\udd60-\udd65\udd67-\udd68\udd6a-\udd89\udd98\udee0-\udef2]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udf00-\udf2f\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf50\udf93-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb]|\ud838[\udd00-\udd2c\udd37-\udd3d\udd4e\udec0-\udeeb]|\ud83a[\udc00-\udcc4\udd00-\udd43\udd4b]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d]) U_ID_CONTINUE ([\u0030-\u0039\u0041-\u005a\u005f\u0061-\u007a\u00aa\u00b5\u00b7\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u05d0-\u05ea\u05ef-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u07fd\u0800-\u082d\u0840-\u085b\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u08d3-\u08e1\u08e3-\u0963\u0966-\u096f\u0971-\u0983\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7-\u09c8\u09cb-\u09ce\u09d7\u09dc-\u09dd\u09df-\u09e3\u09e6-\u09f1\u09fc\u09fe\u0a01-\u0a03\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0af9-\u0aff\u0b01-\u0b03\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b5c-\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82-\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c00-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c58-\u0c5a\u0c60-\u0c63\u0c66-\u0c6f\u0c80-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1-\u0cf2\u0d00-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d54-\u0d57\u0d5f-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82-\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18-\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1369-\u1371\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772-\u1773\u1780-\u17d3\u17d7\u17dc-\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1878\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1ab0-\u1abd\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1cd0-\u1cd2\u1cd4-\u1cfa\u1d00-\u1df9\u1dfb-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua8fd-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\ua9e0-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabea\uabec-\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2f\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udca0-\udca9\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00-\ude03\ude05-\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude35\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd27\udd30-\udd39\udf00-\udf1c\udf27\udf30-\udf50\udfe0-\udff6]|\ud804[\udc00-\udc46\udc66-\udc6f\udc7f-\udcba\udcd0-\udce8\udcf0-\udcf9\udd00-\udd34\udd36-\udd3f\udd44-\udd46\udd50-\udd73\udd76\udd80-\uddc4\uddc9-\uddcc\uddd0-\uddda\udddc\ude00-\ude11\ude13-\ude37\ude3e\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udeea\udef0-\udef9\udf00-\udf03\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3b-\udf44\udf47-\udf48\udf4b-\udf4d\udf50\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc00-\udc4a\udc50-\udc59\udc5e-\udc5f\udc80-\udcc5\udcc7\udcd0-\udcd9\udd80-\uddb5\uddb8-\uddc0\uddd8-\udddd\ude00-\ude40\ude44\ude50-\ude59\ude80-\udeb8\udec0-\udec9\udf00-\udf1a\udf1d-\udf2b\udf30-\udf39]|\ud806[\udc00-\udc3a\udca0-\udce9\udcff\udda0-\udda7\uddaa-\uddd7\uddda-\udde1\udde3-\udde4\ude00-\ude3e\ude47\ude50-\ude99\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc36\udc38-\udc40\udc50-\udc59\udc72-\udc8f\udc92-\udca7\udca9-\udcb6\udd00-\udd06\udd08-\udd09\udd0b-\udd36\udd3a\udd3c-\udd3d\udd3f-\udd47\udd50-\udd59\udd60-\udd65\udd67-\udd68\udd6a-\udd8e\udd90-\udd91\udd93-\udd98\udda0-\udda9\udee0-\udef6]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\ude60-\ude69\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf50-\udf59\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf4f-\udf87\udf8f-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d-\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb\udfce-\udfff]|\ud836[\ude00-\ude36\ude3b-\ude6c\ude75\ude84\ude9b-\ude9f\udea1-\udeaf]|\ud838[\udc00-\udc06\udc08-\udc18\udc1b-\udc21\udc23-\udc24\udc26-\udc2a\udd00-\udd2c\udd30-\udd3d\udd40-\udd49\udd4e\udec0-\udef9]|\ud83a[\udc00-\udcc4\udcd0-\udcd6\udd00-\udd4b\udd50-\udd59]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef]) ESC_SEQ (\\'u'[0-9a-fA-F]{4}|\\'u{'[0-9a-fA-F]{1,}'}') ID_START ({U_ID_START}|[$_]|{ESC_SEQ}) ID_CONTINUE ({U_ID_CONTINUE}|[$_]|{ESC_SEQ}|[\u200c\u200d]) GROUP_NAME {ID_START}{ID_CONTINUE}* NAME \w+ /** * Lexer state for character class. * * Some meta symbols inside a character class are treated as simple * symbols, so we return different token types for them. * * E.g. `(` yields L_PAREN token by default, however in a character * class it's a simple symbol: * * '(' return 'L_PAREN' * '(' return 'CHAR' * * This allows making BNF grammar simpler with less productions, and states. * * is the same but with unicode flag. */ %s class %s u_class /** * State for "extended" regexp, enabled by `x` flag. * * In this state whitespace are ignored, and expressions can * use #-comments. * * is the same but with unicode flag. */ %s x %s xu /** * State for "unicode" regexp, enabled by `u` flag. * * In this state surrogate pairs are treated as codepoints and * \u{} syntax is understood. */ %s u %% '#'[^\n]+ /* skip comments */ \s+ /* skip whitespace */ '-' return 'DASH' '/' return 'CHAR' '#' return 'CHAR' '|' return 'CHAR' '.' return 'CHAR' '{' return 'CHAR' '{'\d+'}' return 'RANGE_EXACT' '{'\d+',}' return 'RANGE_OPEN' '{'\d+','\d+'}' return 'RANGE_CLOSED' {ESC}'k<'{GROUP_NAME}'>' { const groupName = yytext.slice(3, -1); validateUnicodeGroupName(groupName, this.getCurrentState()); return 'NAMED_GROUP_REF'; } {ESC}'b' return 'ESC_b' {ESC}'B' return 'ESC_B' {ESC}'c'[a-zA-Z] return 'CTRL_CH' {ESC}'0'\d{1,2} return 'OCT_CODE' {ESC}'0' return 'DEC_CODE' {ESC}\d{1,3} return 'DEC_CODE' {ESC}'u'{U_LEAD_SURROGATE}{ESC}'u'{U_TRAIL_SURROGATE} return 'U_CODE_SURROGATE' {ESC}'u{'{U}{1,}'}' return 'U_CODE' {ESC}'u'{U}{4} return 'U_CODE' {ESC}[pP]'{'{NAME}(?:\={NAME})?'}' return 'U_PROP_VALUE_EXP' {ESC}'x'{U}{2} return 'HEX_CODE' {ESC}[tnrdDsSwWvf] return 'META_CHAR' {ESC}'/' return 'ESC_CHAR' {ESC}[ #] return 'ESC_CHAR' {ESC}[\^\$\.\*\+\?\(\)\\\[\]\{\}\|\/] return 'ESC_CHAR' {ESC}{CHAR} { const s = this.getCurrentState(); if (s === 'u' || s === 'xu' || s === 'u_class') { throw new SyntaxError(`invalid Unicode escape ${yytext}`); } return 'ESC_CHAR'; } '(' return 'CHAR' ')' return 'CHAR' '(?=' return 'POS_LA_ASSERT' '(?!' return 'NEG_LA_ASSERT' '(?<=' return 'POS_LB_ASSERT' '(?' { yytext = yytext.slice(3, -1); validateUnicodeGroupName(yytext, this.getCurrentState()); return 'NAMED_CAPTURE_GROUP'; } '(' return 'L_PAREN' ')' return 'R_PAREN' [*?+[^$] return 'CHAR' {ESC}']' return 'ESC_CHAR' ']' { this.popState(); return 'R_BRACKET' } '^' return 'BOS' '$' return 'EOS' '*' return 'STAR' '?' return 'Q_MARK' '+' return 'PLUS' '|' return 'BAR' '.' return 'ANY' '/' return 'SLASH' {CHAR} return 'CHAR' '[^' { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'NEG_CLASS' } '[' { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'L_BRACKET' } /lex // -------------------------------------------- // 2. Syntactic grammar. %{ /** * Tracks capturing groups. */ let capturingGroupsCount = 0; /** * Tracks named groups. */ let namedGroups = {}; /** * Parsing string. */ let parsingString = ''; yyparse.onParseBegin = (string, lexer) => { parsingString = string; capturingGroupsCount = 0; namedGroups = {}; const lastSlash = string.lastIndexOf('/'); const flags = string.slice(lastSlash); if (flags.includes('x') && flags.includes('u')) { lexer.pushState('xu'); } else { if (flags.includes('x')) { lexer.pushState('x'); } if (flags.includes('u')) { lexer.pushState('u'); } } }; /** * On shifting `(` remember its number to used on reduce. */ yyparse.onShift = token => { if (token.type === 'L_PAREN' || token.type === 'NAMED_CAPTURE_GROUP') { token.value = new String(token.value); token.value.groupNumber = ++capturingGroupsCount; } return token; }; /** * Extracts ranges from the range string. */ function getRange(text) { const range = text.match(/\d+/g).map(Number); if (Number.isFinite(range[1]) && range[1] < range[0]) { throw new SyntaxError(`Numbers out of order in ${text} quantifier`); } return range; } /** * Checks class range */ function checkClassRange(from, to) { if (from.kind === 'control' || to.kind === 'control' || (!isNaN(from.codePoint) && !isNaN(to.codePoint) && from.codePoint > to.codePoint)) { throw new SyntaxError(`Range ${from.value}-${to.value} out of order in character class`); } } // ---------------------- Unicode property ------------------------------------------- const unicodeProperties = require('../unicode/parser-unicode-properties.js'); /** * Unicode property. */ function UnicodeProperty(matched, loc) { const negative = matched[1] === 'P'; const separatorIdx = matched.indexOf('='); let name = matched.slice(3, separatorIdx !== -1 ? separatorIdx : -1); let value; // General_Category allows using only value as a shorthand. const isShorthand = separatorIdx === -1 && unicodeProperties.isGeneralCategoryValue(name); // Binary propery name. const isBinaryProperty = separatorIdx === -1 && unicodeProperties.isBinaryPropertyName(name); if (isShorthand) { value = name; name = 'General_Category'; } else if (isBinaryProperty) { value = name; } else { if (!unicodeProperties.isValidName(name)) { throw new SyntaxError(`Invalid unicode property name: ${name}.`); } value = matched.slice(separatorIdx + 1, -1); if (!unicodeProperties.isValidValue(name, value)) { throw new SyntaxError(`Invalid ${name} unicode property value: ${value}.`); } } return Node({ type: 'UnicodeProperty', name, value, negative, shorthand: isShorthand, binary: isBinaryProperty, canonicalName: unicodeProperties.getCanonicalName(name) || name, canonicalValue: unicodeProperties.getCanonicalValue(value) || value, }, loc); } // ---------------------------------------------------------------------------------- /** * Creates a character node. */ function Char(value, kind, loc) { let symbol; let codePoint; switch (kind) { case 'decimal': { codePoint = Number(value.slice(1)); symbol = String.fromCodePoint(codePoint); break; } case 'oct': { codePoint = parseInt(value.slice(1), 8); symbol = String.fromCodePoint(codePoint); break; } case 'hex': case 'unicode': { if (value.lastIndexOf('\\u') > 0) { let [lead, trail] = value.split('\\u').slice(1); lead = parseInt(lead, 16); trail = parseInt(trail, 16); codePoint = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000; symbol = String.fromCodePoint(codePoint); } else { const hex = value.slice(2).replace('{', ''); codePoint = parseInt(hex, 16); if (codePoint > 0x10ffff) { throw new SyntaxError(`Bad character escape sequence: ${value}`); } symbol = String.fromCodePoint(codePoint); } break; } case 'meta': { switch (value) { case '\\t': symbol = '\t'; codePoint = symbol.codePointAt(0); break; case '\\n': symbol = '\n'; codePoint = symbol.codePointAt(0); break; case '\\r': symbol = '\r'; codePoint = symbol.codePointAt(0); break; case '\\v': symbol = '\v'; codePoint = symbol.codePointAt(0); break; case '\\f': symbol = '\f'; codePoint = symbol.codePointAt(0); break; case '\\b': symbol = '\b'; codePoint = symbol.codePointAt(0); case '\\0': symbol = '\0'; codePoint = 0; case '.': symbol = '.'; codePoint = NaN; break; default: codePoint = NaN; } break; } case 'simple': { symbol = value; codePoint = symbol.codePointAt(0); break; } } return Node({ type: 'Char', value, kind, symbol, codePoint, }, loc); } /** * Valid flags per current ECMAScript spec and * stage 3+ proposals. */ const validFlags = 'gimsuxy'; /** * Checks the flags are valid, and that * we don't duplicate flags. */ function checkFlags(flags) { const seen = new Set(); for (const flag of flags) { if (seen.has(flag) || !validFlags.includes(flag)) { throw new SyntaxError(`Invalid flags: ${flags}`); } seen.add(flag); } return flags.split('').sort().join(''); } /** * Parses patterns like \1, \2, etc. either as a backreference * to a group, or a deciaml char code. */ function GroupRefOrDecChar(text, textLoc) { const reference = Number(text.slice(1)); if (reference > 0 && reference <= capturingGroupsCount) { return Node({ type: 'Backreference', kind: 'number', number: reference, reference, }, textLoc); } return Char(text, 'decimal', textLoc); } /** * Unicode names. */ const uReStart = /^\\u[0-9a-fA-F]{4}/; // only matches start of string const ucpReStart = /^\\u\{[0-9a-fA-F]{1,}\}/; // only matches start of string const ucpReAnywhere = /\\u\{[0-9a-fA-F]{1,}\}/; // matches anywhere in string /** * Validates Unicode group name. */ function validateUnicodeGroupName(name, state) { const isUnicodeName = ucpReAnywhere.test(name); const isUnicodeState = (state === 'u' || state === 'xu' || state === 'u_class'); if (isUnicodeName && !isUnicodeState) { throw new SyntaxError(`invalid group Unicode name "${name}", use \`u\` flag.`); } return name; } // Matches the following production: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence // // RegExpUnicodeEscapeSequence :: // `u` LeadSurrogate `\u` TrailSurrogate # as 'leadSurrogate', 'trailSurrogate' // `u` LeadSurrogate # as 'leadSurrogateOnly' // `u` TrailSurrogate # as 'trailSurrogateOnly' // `u` NonSurrogate # as 'nonSurrogate' // `u` `{` CodePoint `}` # as 'codePoint' // // LeadSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xD800 to 0xDBFF # [dD][89aAbB][0-9a-fA-F]{2} // // TrailSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xDC00 to 0xDFFF # [dD][c-fC-F][0-9a-fA-F]{2} // // NonSurrogate :: // Hex4Digits but only if the SV of Hex4Digits is not in the inclusive range 0xD800 to 0xDFFF # [0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2} // // CodePoint :: // HexDigits but only if MV of HexDigits ≤ 0x10FFFF # 0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4}) // const uidRe = /\\u(?:([dD][89aAbB][0-9a-fA-F]{2})\\u([dD][c-fC-F][0-9a-fA-F]{2})|([dD][89aAbB][0-9a-fA-F]{2})|([dD][c-fC-F][0-9a-fA-F]{2})|([0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2})|\{(0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4}))\})/; function decodeUnicodeGroupName(name) { return name.replace(new RegExp(uidRe, 'g'), function (_, leadSurrogate, trailSurrogate, leadSurrogateOnly, trailSurrogateOnly, nonSurrogate, codePoint) { if (leadSurrogate) { return String.fromCodePoint(parseInt(leadSurrogate, 16), parseInt(trailSurrogate, 16)); } if (leadSurrogateOnly) { return String.fromCodePoint(parseInt(leadSurrogateOnly, 16)); } if (trailSurrogateOnly) { // TODO: Per the spec: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence // > Each `\u` TrailSurrogate for which the choice of associated `u` LeadSurrogate is ambiguous shall be associated with the nearest possible `u` LeadSurrogate that would otherwise have no corresponding `\u` TrailSurrogate. return String.fromCodePoint(parseInt(trailSurrogateOnly, 16)); } if (nonSurrogate) { return String.fromCodePoint(parseInt(nonSurrogate, 16)); } if (codePoint) { return String.fromCodePoint(parseInt(codePoint, 16)); } return _; }); } /** * Extracts from `\k` pattern either a backreference * to a named capturing group (if it presents), or parses it * as a list of char: `\k`, `<`, `f`, etc. */ function NamedGroupRefOrChars(text, textLoc) { const referenceRaw = text.slice(3, -1); const reference = decodeUnicodeGroupName(referenceRaw); if (namedGroups.hasOwnProperty(reference)) { return Node({ type: 'Backreference', kind: 'name', number: namedGroups[reference], reference, referenceRaw, }, textLoc); } // Else `\k` should be parsed as a list of `Char`s. // This is really a 0.01% edge case, but we should handle it. let startOffset = null; let startLine = null; let endLine = null; let startColumn = null; if (textLoc) { startOffset = textLoc.startOffset; startLine = textLoc.startLine; endLine = textLoc.endLine; startColumn = textLoc.startColumn; } const charRe = /^[\w$<>]/; let loc; const chars = [ // Init to first \k, taking 2 symbols. Char( text.slice(1, 2), 'simple', startOffset ? { startLine, endLine, startColumn, startOffset, endOffset: startOffset += 2, endColumn: startColumn += 2, } : null ), ]; // For \k chars[0].escaped = true; // Other symbols. text = text.slice(2); while (text.length > 0) { let matched = null; // Unicode, \u003B or \u{003B} if ((matched = text.match(uReStart)) || (matched = text.match(ucpReStart))) { if (startOffset) { loc = { startLine, endLine, startColumn, startOffset, endOffset: (startOffset += matched[0].length), endColumn: (startColumn += matched[0].length), }; } chars.push(Char(matched[0], 'unicode', loc)); text = text.slice(matched[0].length); } // Simple char. else if ((matched = text.match(charRe))) { if (startOffset) { loc = { startLine, endLine, startColumn, startOffset, endOffset: ++startOffset, endColumn: ++startColumn, }; } chars.push(Char(matched[0], 'simple', loc)); text = text.slice(1); } } return chars; } /** * Creates an AST node with a location. */ function Node(node, loc) { if (yy.options.captureLocations) { node.loc = { source: parsingString.slice(loc.startOffset, loc.endOffset), start: { line: loc.startLine, column: loc.startColumn, offset: loc.startOffset, }, end: { line: loc.endLine, column: loc.endColumn, offset: loc.endOffset, }, }; } return node; } /** * Creates location node. */ function loc(start, end) { if (!yy.options.captureLocations) { return null; } return { startOffset: start.startOffset, endOffset: end.endOffset, startLine: start.startLine, endLine: end.endLine, startColumn: start.startColumn, endColumn: end.endColumn, }; } %} %% RegExp : SLASH Pattern SLASH OptFlags { $$ = Node({ type: 'RegExp', body: $2, flags: checkFlags($4), }, loc(@1, @4 || @3)) } ; OptFlags : Flags | /* empty */ { $$ = '' } ; Flags : CHAR | Flags CHAR { $$ = $1 + $2 } ; Pattern : Disjunction ; Disjunction : Alternative | Disjunction BAR Alternative { // Location for empty disjunction: /|/ let _loc = null; if (@2) { _loc = loc(@1 || @2, @3 || @2); }; $$ = Node({ type: 'Disjunction', left: $1, right: $3, }, _loc) } ; Alternative : AlternativeEntries { if ($1.length === 0) { $$ = null; return; } if ($1.length === 1) { $$ = Node($1[0], @$); } else { $$ = Node({ type: 'Alternative', expressions: $1, }, @$) } } ; AlternativeEntries : /* empty */ { $$ = [] } | AlternativeEntries Term { $$ = $1.concat($2) } ; Term : Assertion { $$ = Node(Object.assign({type: 'Assertion'}, $1), @$) } | Atom OptQuantifier { $$ = $1; if ($2) { $$ = Node({ type: 'Repetition', expression: $1, quantifier: $2, }, @$) } } ; Assertion : BOS { $$ = { kind: '^' } } | EOS { $$ = { kind: '$' } } | ESC_b { $$ = { kind: '\\b' } } | ESC_B { $$ = { kind: '\\B' } } | POS_LA_ASSERT Disjunction R_PAREN { $$ = { kind: 'Lookahead', assertion: $2, } } | NEG_LA_ASSERT Disjunction R_PAREN { $$ = { kind: 'Lookahead', negative: true, assertion: $2, } } | POS_LB_ASSERT Disjunction R_PAREN { $$ = { kind: 'Lookbehind', assertion: $2, } } | NEG_LB_ASSERT Disjunction R_PAREN { $$ = { kind: 'Lookbehind', negative: true, assertion: $2, } } ; Atom : SourceCharacter | CharacterClass | Group ; SourceCharacter : CHAR { $$ = Char($1, 'simple', @$) } | ESC_CHAR { $$ = Char($1.slice(1), 'simple', @$); $$.escaped = true; } | U_CODE_SURROGATE { $$ = Char($1, 'unicode', @$); $$.isSurrogatePair = true; } | U_CODE { $$ = Char($1, 'unicode', @$) } | U_PROP_VALUE_EXP { $$ = UnicodeProperty($1, @$) } | CTRL_CH { $$ = Char($1, 'control', @$) } | HEX_CODE { $$ = Char($1, 'hex', @$) } | OCT_CODE { $$ = Char($1, 'oct', @$) } | DEC_CODE { $$ = GroupRefOrDecChar($1, @$) } | META_CHAR { $$ = Char($1, 'meta', @$) } | ANY { $$ = Char($1, 'meta', @$) } | NAMED_GROUP_REF { $$ = NamedGroupRefOrChars($1, @1) } ; OptQuantifier : Quantifier | /* empty */ ; Quantifier : QuantifierPrefix | QuantifierPrefix Q_MARK { $1.greedy = false; $$ = $1; } ; QuantifierPrefix : STAR { $$ = Node({ type: 'Quantifier', kind: $1, greedy: true, }, @$) } | PLUS { $$ = Node({ type: 'Quantifier', kind: $1, greedy: true, }, @$) } | Q_MARK { $$ = Node({ type: 'Quantifier', kind: $1, greedy: true, }, @$) } | RANGE_EXACT { const range = getRange($1); $$ = Node({ type: 'Quantifier', kind: 'Range', from: range[0], to: range[0], greedy: true, }, @$) } | RANGE_OPEN { $$ = Node({ type: 'Quantifier', kind: 'Range', from: getRange($1)[0], greedy: true, }, @$) } | RANGE_CLOSED { const range = getRange($1); $$ = Node({ type: 'Quantifier', kind: 'Range', from: range[0], to: range[1], greedy: true, }, @$) } ; Group : CapturingGroup | NonCapturingGroup ; CapturingGroup : NAMED_CAPTURE_GROUP Disjunction R_PAREN { const nameRaw = String($1); const name = decodeUnicodeGroupName(nameRaw); if (namedGroups.hasOwnProperty(name)) { throw new SyntaxError(`Duplicate of the named group "${name}".`); } namedGroups[name] = $1.groupNumber; $$ = Node({ type: 'Group', capturing: true, name, nameRaw, number: $1.groupNumber, expression: $2, }, @$); } | L_PAREN Disjunction R_PAREN { $$ = Node({ type: 'Group', capturing: true, number: $1.groupNumber, expression: $2, }, @$); } ; NonCapturingGroup : NON_CAPTURE_GROUP Disjunction R_PAREN { $$ = Node({ type: 'Group', capturing: false, expression: $2, }, @$) } ; CharacterClass : NEG_CLASS ClassRanges R_BRACKET { $$ = Node({ type: 'CharacterClass', negative: true, expressions: $2, }, @$) } | L_BRACKET ClassRanges R_BRACKET { $$ = Node({ type: 'CharacterClass', expressions: $2, }, @$) } ; ClassRanges : /* empty */ { $$ = [] } | NonemptyClassRanges ; NonemptyClassRanges : ClassAtom { $$ = [$1] } | ClassAtom NonemptyClassRangesNoDash { $$ = [$1].concat($2) } | ClassAtom DASH ClassAtom ClassRanges { checkClassRange($1, $3); $$ = [ Node({ type: 'ClassRange', from: $1, to: $3, }, loc(@1, @3)) ]; if ($4) { $$ = $$.concat($4); } } ; NonemptyClassRangesNoDash : ClassAtom | ClassAtomNoDash NonemptyClassRangesNoDash { $$ = [$1].concat($2) } | ClassAtomNoDash DASH ClassAtom ClassRanges { checkClassRange($1, $3); $$ = [ Node({ type: 'ClassRange', from: $1, to: $3, }, loc(@1, @3)), ]; if ($4) { $$ = $$.concat($4); } } ; ClassAtom : DASH { $$ = Char($1, 'simple', @$) } | ClassAtomNoDash ; ClassAtomNoDash : SourceCharacter | ESC_b { $$ = Char($1, 'meta', @$) } ; regexp-tree-0.1.18/src/parser/unicode/000077500000000000000000000000001361507317500175515ustar00rootroot00000000000000regexp-tree-0.1.18/src/parser/unicode/parser-unicode-properties.js000066400000000000000000000215721361507317500252300ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const NON_BINARY_PROP_NAMES_TO_ALIASES = { General_Category: 'gc', Script: 'sc', Script_Extensions: 'scx', }; const NON_BINARY_ALIASES_TO_PROP_NAMES = inverseMap( NON_BINARY_PROP_NAMES_TO_ALIASES ); const 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', }; const BINARY_ALIASES_TO_PROP_NAMES = inverseMap(BINARY_PROP_NAMES_TO_ALIASES); const 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', }; const GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES = inverseMap( GENERAL_CATEGORY_VALUE_TO_ALIASES ); const 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', }; const SCRIPT_VALUE_ALIASES_TO_VALUE = inverseMap(SCRIPT_VALUE_TO_ALIASES); function inverseMap(data) { const inverse = {}; for (let name in data) { if (!data.hasOwnProperty(name)) { continue; } const value = data[name]; if (Array.isArray(value)) { for (let 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, isValidName, isValidValue, isGeneralCategoryValue, isScriptCategoryValue, isBinaryPropertyName, getCanonicalName, getCanonicalValue, NON_BINARY_PROP_NAMES_TO_ALIASES, NON_BINARY_ALIASES_TO_PROP_NAMES, BINARY_PROP_NAMES_TO_ALIASES, BINARY_ALIASES_TO_PROP_NAMES, GENERAL_CATEGORY_VALUE_TO_ALIASES, GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES, SCRIPT_VALUE_TO_ALIASES, SCRIPT_VALUE_ALIASES_TO_VALUE, }; regexp-tree-0.1.18/src/regexp-tree.js000066400000000000000000000100031361507317500174060ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const compatTranspiler = require('./compat-transpiler'); const generator = require('./generator'); const optimizer = require('./optimizer'); const parser = require('./parser'); const transform = require('./transform'); const traverse = require('./traverse'); const fa = require('./interpreter/finite-automaton'); const {RegExpTree} = require('./compat-transpiler/runtime'); /** * An API object for RegExp processing (parsing/transform/generation). */ const regexpTree = { /** * Parser module exposed. */ parser, /** * Expose finite-automaton module. */ 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(regexp, options) { return parser.parse(`${regexp}`, options); }, /** * Traverses a RegExp AST. * * @param Object ast * @param Object | Array 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(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 handlers - a list of handlers. * * @return TransformResult - a transformation result. */ 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(ast) { return generator.generate(ast); }, /** * Creates a RegExp object from a regexp string. * * @param string regexp */ toRegExp(regexp) { const 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(regexp, whitelist, {blacklist} = {}) { return optimizer.optimize(regexp, {whitelist, blacklist}); }, /** * Translates a regular expression in new syntax or in new format * into equivalent expressions in old syntax. * * @param string regexp * * @return TransformResult object */ 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(re, string) { if (typeof re === 'string') { const compat = this.compatTranspile(re); const 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; regexp-tree-0.1.18/src/transform/000077500000000000000000000000001361507317500166425ustar00rootroot00000000000000regexp-tree-0.1.18/src/transform/README.md000066400000000000000000000031251361507317500201220ustar00rootroot00000000000000# regexp-tree: Transform module Transform module is a convenient wrapper on top of the _"parse-traverse-generate"_ tool chain. It accepts a regular expression in different formats (a string, an actual RegExp, or an AST), applies a set of tranformations, and returns a `TransformResult` object. Example: ```js const regexpTree = require('regexp-tree'); const re = regexpTree.transform('/[a-z]{1,}/', { /** * Handle "Quantifier" node type, * transforming `{1,}` quantifier to `+`. */ Quantifier(path) { const {node} = path; // {1,} -> + if ( node.type === 'Range' && node.from === 1 && !node.to ) { path.replace({ type: 'Quantifier', kind: '+', greedy: node.greedy, loc: node.loc ? {start: node.loc.start, end: node.loc.start + 1} : null, }); } }, }); console.log(re.toRegExp()); // /[a-z]+/ ``` ### API The module has the following interface: ```js type AST = Object; type Handler = Object; type RegExpIn = string | AST | RegExp; type Handlers = Array | Handler; transform(regexp: RegExpIn, handlers: Handlers): TransformResult; ``` ### TransformResult An object of `TransformResult` has the following methods: * `getAST` - returns the (transformed) AST component; * `getBodyString` - returns body of a regexp as a string (can be passed to `RegExp` constructor); * `getFlags` - returns flags of a regexp as a string (can be passed to `RegExp` constructor); * `toRegExp` - returns a regular expression as a RegExp instance; * `toString` - returns a regular expression as a string; regexp-tree-0.1.18/src/transform/__tests__/000077500000000000000000000000001361507317500206005ustar00rootroot00000000000000regexp-tree-0.1.18/src/transform/__tests__/transform-basic-test.js000066400000000000000000000044431361507317500252120ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('../../parser'); const {TransformResult, transform} = require('..'); const defaultRe = /a{1,}/i; const defaultReString = `${defaultRe}`; const defaultAst = parser.parse(defaultReString, { captureLocations: true, }); describe('transform-basic', () => { function test(re) { const result = transform(re, { Quantifier(path) { const {node} = path; if (node.kind === 'Range' && node.from == 1 && !node.to) { path.replace({ type: 'Quantifier', kind: '+', greedy: node.greedy, }); } } }); expect(result).toBeInstanceOf(TransformResult); expect(result.toString()).toBe('/a+/i'); expect(result.toRegExp()).toEqual(/a+/i); expect(result.getSource()).toBe('a+'); expect(result.getFlags()).toBe('i'); expect(result.getAST()).toEqual({ type: 'RegExp', body: { type: 'Repetition', expression: { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0), loc: { source: 'a', start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 2, offset: 2, }, }, }, quantifier: { type: 'Quantifier', kind: '+', greedy: true, }, loc: { source: 'a{1,}', // NOTE: original source might not be updated start: { line: 1, column: 1, offset: 1, }, end: { line: 1, column: 6, offset: 6, }, }, }, flags: 'i', loc: { source: '/a{1,}/i', start: { line: 1, column: 0, offset: 0, }, end: { line: 1, column: 8, offset: 8, }, } }); } it('from string', () => { test(defaultReString); }); it('from regexp', () => { test(defaultRe); }); it('from ast', () => { test(defaultAst); }); });regexp-tree-0.1.18/src/transform/__tests__/transform-utils-test.js000066400000000000000000000052661361507317500252750ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const parser = require('../../parser'); const transformUtils = require('../utils'); describe('transform-utils', () => { it('disjunctionToList', () => { const disjunction = parser.parse('/a|b|c|d/').body; const list = transformUtils.disjunctionToList(disjunction); expect(list).toEqual([ disjunction.left.left.left, // a disjunction.left.left.right, // b disjunction.left.right, // c disjunction.right, // d ]); }); it('handles empty parts', () => { const disjunction = parser.parse('/|/').body; const list = transformUtils.disjunctionToList(disjunction); expect(list).toEqual([ disjunction.left, // null disjunction.right // null ]); }); it('disjunctionToList', () => { const list = [ {type: 'Char', value: 'a', kind: 'simple', codePoint: 97, symbol: 'a'}, {type: 'Char', value: 'b', kind: 'simple', codePoint: 98, symbol: 'b'}, {type: 'Char', value: 'c', kind: 'simple', codePoint: 99, symbol: 'c'}, {type: 'Char', value: 'd', kind: 'simple', codePoint: 100, symbol: 'd'}, ]; const disjunction = transformUtils.listToDisjunction(list); const expected = parser.parse('/a|b|c|d/').body; expect(disjunction).toEqual(expected); }); it('increaseQuantifierByOne', () => { const quantifiers = [{ type: 'Quantifier', kind: '*', greedy: true }, { type: 'Quantifier', kind: '+', greedy: false }, { type: 'Quantifier', kind: '?', greedy: true }, { type: 'Quantifier', kind: 'Range', from: 2, to: 2, greedy: true }, { type: 'Quantifier', kind: 'Range', from: 2, greedy: false }, { type: 'Quantifier', kind: 'Range', from: 2, to: 3, greedy: true }]; const quantifiersIncreased = [{ type: 'Quantifier', kind: '+', greedy: true }, { type: 'Quantifier', kind: 'Range', from: 2, greedy: false }, { type: 'Quantifier', kind: 'Range', from: 1, to: 2, greedy: true }, { type: 'Quantifier', kind: 'Range', from: 3, to: 3, greedy: true }, { type: 'Quantifier', kind: 'Range', from: 3, greedy: false }, { type: 'Quantifier', kind: 'Range', from: 3, to: 4, greedy: true }]; quantifiers.forEach((quantifier, i) => { transformUtils.increaseQuantifierByOne(quantifier); expect(quantifier).toEqual(quantifiersIncreased[i]); }); }); });regexp-tree-0.1.18/src/transform/index.js000066400000000000000000000042631361507317500203140ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const generator = require('../generator'); const parser = require('../parser'); const traverse = require('../traverse'); /** * Transform result. */ class TransformResult { /** * Initializes a transform result for an AST. * * @param Object ast - an AST node * @param mixed extra - any extra data a transform may return */ constructor(ast, extra = null) { this._ast = ast; this._source = null; this._string = null; this._regexp = null; this._extra = extra; } getAST() { return this._ast; } setExtra(extra) { this._extra = extra; } getExtra() { return this._extra; } toRegExp() { if (!this._regexp) { this._regexp = new RegExp(this.getSource(), this._ast.flags); } return this._regexp; } getSource() { if (!this._source) { this._source = generator.generate(this._ast.body); } return this._source; } getFlags() { return this._ast.flags; } toString() { if (!this._string) { this._string = generator.generate(this._ast); } return this._string; } } module.exports = { /** * Expose `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: * * 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(regexp, handlers) { let 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); }, }; regexp-tree-0.1.18/src/transform/utils.js000066400000000000000000000031031361507317500203350ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; /** * Flattens a nested disjunction node to a list. * * /a|b|c|d/ * * {{{a, b}, c}, d} -> [a, b, c, d] */ function disjunctionToList(node) { if (node.type !== 'Disjunction') { throw new TypeError(`Expected "Disjunction" node, got "${node.type}"`); } const list = []; if (node.left && node.left.type === 'Disjunction') { list.push(...disjunctionToList(node.left), 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((left, right) => { return { type: 'Disjunction', left, 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, listToDisjunction, increaseQuantifierByOne, };regexp-tree-0.1.18/src/traverse/000077500000000000000000000000001361507317500164625ustar00rootroot00000000000000regexp-tree-0.1.18/src/traverse/README.md000066400000000000000000000055661361507317500177550ustar00rootroot00000000000000# regexp-tree: Traverse module Provides traversal API with _vistor_ pattern for regexp ASTs. See [the specification](https://github.com/DmitrySoshnikov/regexp-tree#ast-nodes-specification) for AST nodes format. Once a regular expression is parsed, it is possible to handle needed nodes by using the traversal API. Handlers receive an instance of `NodePath` class, which encapsulates `node` itself, and other convenient properties, and methods. Visiting a node follows this algorithm: - call `pre` handler. - recurse into node's children. - call `post` handler. For each node type of interest, you can provide either: - a function (`pre`). - an object with members `pre` and `post`. You can also provide a `*` handler which will be executed on every node. ```js const regexpTree = require('regexp-tree'); // Get AST. const ast = regexpTree.parse('/[a-z]{1,}/'); // Handle nodes. regexpTree.traverse(ast, { // Visit every node before any type-specific handlers. '*': function({node}) { ... }, // Handle "Quantifier" node type, // transforming `{1,}` quantifier to `+`. Quantifier({node}) { // {1,} -> + if ( node.kind === 'Range' && node.from === 1 && !node.to ) { node.kind = '+'; delete node.from; } }, // Handle "Char" node type, before and after. Char: { pre({node}) { ... }, post({node}) { ... } } }); // Generate the regexp. const re = regexpTree.generate(ast); console.log(re); // '/[a-z]+/' ``` ### NodePath class A instance of the `NodePath` class is a convenient _wrapper_ on top an AST `node` itself, its `parent` node, and `parentPath`, the `property` name of the node used in parent, and also an `index` of the node in case if the node is a part of a collection. It also provides a list of useful method for AST manipulation. Properties of the `NodePath`: * `node` - an actual AST node; * `parent` - an immediate parent AST node; * `parentPath` - a parent `NodePath`. Parent paths are chained: `parentPath.parentPath. ...` gives you an access to a needed parent level; * `property` - the property name of the node used in the parent node; * `index` - the index of the node, in case if the node is a part of a collection. Methods of the `NodePath`: * `remove()` - removes a node; * `replace(node: Object)` - replaces a node with the passed one; * `update(nodeProps: Object)` - updates a node inline; * `getPreviousSibling()` - returns previous sibling path; * `getNextSibling()` - returns next sibling path; * `getChild(n: Number = 0)` - return `n`th child path; * `getParent()` - return parent path, for consistency, returns parentPath; * `hasEqualSource(path)` - checks whether the path is equal in its source to the passed one * `jsonEncode({format, useLoc})` - returns JSON-encoded string for this path. The `format` can pass number for indentation, the `useLoc` controls whether to include `loc` info. regexp-tree-0.1.18/src/traverse/__tests__/000077500000000000000000000000001361507317500204205ustar00rootroot00000000000000regexp-tree-0.1.18/src/traverse/__tests__/node-path-test.js000066400000000000000000000365441361507317500236260ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const NodePath = require('../node-path'); const generator = require('../../generator'); const parser = require('../../parser'); const traverse = require('..'); describe('NodePath', () => { it('creates a node path', () => { const ast = parser.parse('/a/'); const node = ast.body; const regexpPath = NodePath.getForNode(ast); expect(regexpPath.node).toBe(ast); expect(regexpPath.parent).toBe(null); expect(regexpPath.parentPath).toBe(null); expect(regexpPath.property).toBe(null); expect(regexpPath.index).toBe(null); const charPath = NodePath.getForNode(node, regexpPath, 'body'); expect(charPath.node).toBe(node); expect(charPath.parent).toBe(ast); expect(charPath.parentPath).toBe(regexpPath); expect(charPath.property).toBe('body'); expect(charPath.index).toBe(null); }); it('removes a node', () => { const ast = parser.parse('/[ab]/'); const bCharPath = NodePath.getForNode( ast.body.expressions[1], NodePath.getForNode(ast.body), 'expressions', 1 ); bCharPath.remove(); expect(bCharPath.isRemoved()).toBe(true); expect(bCharPath.node).toBe(null); expect(ast).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0) }, // No 'b' char. ] }, flags: '', }); }); it('checks correct indices after removal', () => { const ast = parser.parse('/abcde/'); traverse.traverse(ast, [ // First handler, removes current 'b', and further 'c'. { Char(path) { const {node} = path; if (node.value === 'b') { const aPath = path.getPreviousSibling(); const cPath = path.getNextSibling(); const dPath = cPath.getNextSibling(); const ePath = dPath.getNextSibling(); // Remove 'b' itself. path.remove(); // Remove 'c' as well. cPath.remove(); // Check the index is rebuilt: // a, 0 expect(aPath.node.value).toBe('a'); expect(aPath.index).toBe(0); // No b (was 1 before removal) expect(path.isRemoved()).toBe(true); // No c (was 2 before removal) expect(cPath.isRemoved()).toBe(true); // d is now 1 expect(dPath.node.value).toBe('d'); expect(dPath.index).toBe(1); // e is now 2 expect(ePath.node.value).toBe('e'); expect(ePath.index).toBe(2); } }, }, // Second handler, backward-removes 'a' being on last 'e'. { // This handler is not called for 'b', and 'c' since // they were removed in the previous handler. Char(path) { const {node} = path; // Never can be 'b' or 'c', since they were removed. expect(path.value).not.toBe('b'); expect(path.value).not.toBe('c'); if (node.value === 'e') { // Initially index of `e` is 2. expect(path.index).toBe(2); // Being on 'd', we remove one of the previous siblings, 'a': const aPath = path.getPreviousSibling().getPreviousSibling(); expect(aPath.node.value).toBe('a'); const dPath = path.getPreviousSibling(); expect(dPath.node.value).toBe('d'); // Remove 'a' aPath.remove(); expect(aPath.isRemoved()).toBe(true); // Check the index is rebuilt: // d, 0 expect(dPath.index).toBe(0); // e, 1 (index of `e` is changed from 2 to 1) expect(path.index).toBe(1); expect(path.getNextSibling()).toBe(null); } } } ]); // '/abcde/' -> '/ae/', after 3 removals in 2 handlers. expect(generator.generate(ast)).toBe('/de/'); }); it('conditional remove', () => { const ast = parser.parse('/abcd/'); // '/abcd/' -> '/ad/' traverse.traverse(ast, { Char(path) { const {node} = path; if (node.value === 'b' || node.value === 'c') { path.remove(); } } }); expect(generator.generate(ast)).toBe('/ad/'); }); it('several backward/forward removes', () => { const ast = parser.parse('/abcdefghi/'); // '/abcdefghi/' -> '/bfi/' traverse.traverse(ast, { Char(path) { const {node} = path; // From 'd' remove previous 'a', 'c', 'd' (itself), and 'e'. // After this it is: bfghi if (node.value === 'd') { const cPath = path.getPreviousSibling(); const aPath = cPath.getPreviousSibling().getPreviousSibling(); const ePath = path.getNextSibling(); aPath.remove(); cPath.remove(); path.remove(); ePath.remove(); } // From 'f' remove two next 'g', and 'h' // After this it is: bfi if (node.value === 'f') { const gPath = path.getNextSibling(); const hPath = gPath.getNextSibling(); gPath.remove(); hPath.remove(); } } }); expect(generator.generate(ast)).toBe('/bfi/'); }); it('several backward/forward removes/inserts', () => { const ast = parser.parse('/abcdefghi/'); // '/abcdefghi/' -> '/bxyfzi/' traverse.traverse(ast, { Char(path) { const {node} = path; // From 'd' remove previous 'a', 'c', inserts 'x' before 'd', // removes 'd' (itself), insert 'y' after 'e', remove 'e'. // After this it is: bxyfghi if (node.value === 'd') { const cPath = path.getPreviousSibling(); const aPath = cPath.getPreviousSibling().getPreviousSibling(); const ePath = path.getNextSibling(); // Remove 'a', and 'c'. aPath.remove(); cPath.remove(); // Insert 'x' before 'd'. After we removed 'a', and 'c' // insert index is 1. const xNode = { type: 'Char', value: 'x', symbol: 'x', kind: 'simple', codePoint: 'x'.codePointAt(0) }; const parentPath = path.parentPath; parentPath.insertChildAt(xNode, 1); // Remove 'd'. path.remove(); // Insert 'y' after 'e'. const yNode = { type: 'Char', value: 'y', symbol: 'y', kind: 'simple', codePoint: 'y'.codePointAt(0) }; parentPath.insertChildAt(yNode, 3); // Remove 'e'. ePath.remove(); } // From 'f' remove two next 'g', and 'h', insert 'z' after 'f'. // After this it is: bfi if (node.value === 'f') { const gPath = path.getNextSibling(); const hPath = gPath.getNextSibling(); // Remove 'g'. gPath.remove(); // Insert 'z'. const zNode = { type: 'Char', value: 'z', symbol: 'z', kind: 'simple', codePoint: 'z'.codePointAt(0) }; path.parentPath.insertChildAt(zNode, 4); // Remove 'h'. hPath.remove(); } } }); expect(generator.generate(ast)).toBe('/bxyfzi/'); }); it('replaces a node', () => { const ast = parser.parse('/[ab]/'); const bCharPath = new NodePath( ast.body.expressions[1], new NodePath(ast.body), 'expressions', 1 ); const cNode = { type: 'Char', value: 'c', symbol: 'c', kind: 'simple', codePoint: 'c'.codePointAt(0) }; bCharPath.replace(cNode); expect(bCharPath.node).toEqual(cNode); expect(ast).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', expressions: [ { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0) }, // 'b' replaced with 'c' cNode ] }, flags: '', }); }); it('replaces with already used node with another parent', () => { const ast = parser.parse('/a(b)/'); const bodyPath = new NodePath(ast.body); const aCharPath = bodyPath.getChild(0); const groupPath = bodyPath.getChild(1); const bCharPath = groupPath.getChild(); const aNode = aCharPath.node; const bNode = bCharPath.node; bodyPath.node.mark = 'body'; groupPath.node.mark = 'group'; // swap a and b groupPath.setChild(aNode); bodyPath.setChild(bNode, 0); // 'a' and 'b' should be swapped now expect(generator.generate(ast)).toBe('/b(a)/'); // body and group still ok expect(bodyPath.node.mark).toBe('body'); expect(groupPath.node.mark).toBe('group'); expect(bodyPath.getChild(1).node.mark).toBe('group'); // first is now Char 'b' in body expect(bodyPath.getChild(0).node.type).toBe('Char'); expect(bodyPath.getChild(0).node.value).toBe('b'); // parentPath should point to the same node as parent expect(bodyPath.getChild(0).parentPath.node) .toBe(bodyPath.getChild(0).parent); // now the parent of 'b' is the body expect(bodyPath.getChild(0).parent.mark).toBe('body'); expect(bodyPath.getChild(0).parent.type).toBe('Alternative'); // Group has children property 'expressions' expect(bodyPath.getChild(0).property).toBe('expressions'); // and an index of 0 expect(bodyPath.getChild(0).index).toBe(0); // second is Char 'a' in the Group expect(bodyPath.getChild(1).getChild().node.type).toBe('Char'); expect(bodyPath.getChild(1).getChild().node.value).toBe('a'); // parentPath should point to the same node as parent expect(bodyPath.getChild(1).getChild().parentPath.node) .toBe(bodyPath.getChild(1).getChild().parent); // now the parent of 'a' is the Group expect(bodyPath.getChild(1).getChild().parent.mark).toBe('group'); expect(bodyPath.getChild(1).getChild().parent.type).toBe('Group'); // Group has child property 'expression' expect(bodyPath.getChild(1).getChild().property).toBe('expression'); // and no index! expect(bodyPath.getChild(1).getChild().index).toBe(null); // the locally stored NodePaths should have new parents expect(bCharPath.parentPath).toBe(bodyPath); expect(aCharPath.parentPath).toBe(groupPath); // paths from the registry should be the same const bCharPath2 = NodePath.getForNode(bNode); expect(bCharPath2).toBe(bCharPath); const aCharPath2 = NodePath.getForNode(aNode); expect(aCharPath2).toBe(aCharPath); }); it('sets a child node to two new nested nodes', () => { const ast = parser.parse('/ab/'); const bodyPath = new NodePath(ast.body); const bCharPath = bodyPath.getChild(1); const cNode = { type: 'Char', value: 'c', symbol: 'c', kind: 'simple', codePoint: 'c'.codePointAt(0) }; const groupNode = { type: 'Group', capturing: true, expression: null }; const alterNode = { type: 'Alternative', expressions: [] }; bCharPath.replace(groupNode); expect(bCharPath.node).toEqual(groupNode); const groupPath = bodyPath.getChild(1); groupPath.setChild(cNode); const cPath = NodePath.getForNode(cNode); expect(cPath.parentPath).toBe(groupPath); const cPath2 = groupPath.getChild(); expect(cPath2).toBe(cPath); expect(cPath2.parentPath).toBe(groupPath); const alterPath = groupPath.setChild(alterNode); alterPath.appendChild(cNode); const dNode = { type: 'Char', value: 'd', symbol: 'd', kind: 'simple', codePoint: 'd'.codePointAt(0) }; alterPath.appendChild(dNode); const dPath = NodePath.getForNode(dNode); expect(cPath.parentPath).toBe(alterPath); expect(dPath.parentPath).toBe(alterPath); expect(alterPath.parentPath).toBe(groupPath); expect(generator.generate(ast)).toEqual('/a(cd)/'); }); it('getPreviousSibling', () => { const ast = parser.parse('/ab/'); const aCharPath = NodePath.getForNode( ast.body.expressions[0], NodePath.getForNode(ast.body), 'expressions', 0 ); const bCharPath = NodePath.getForNode( ast.body.expressions[1], NodePath.getForNode(ast.body), 'expressions', 1 ); expect(bCharPath.getPreviousSibling()).toBe(aCharPath); }); it('getNextSibling', () => { const ast = parser.parse('/ab/'); const aCharPath = NodePath.getForNode( ast.body.expressions[0], NodePath.getForNode(ast.body), 'expressions', 0 ); const bCharPath = NodePath.getForNode( ast.body.expressions[1], NodePath.getForNode(ast.body), 'expressions', 1 ); expect(aCharPath.getNextSibling()).toBe(bCharPath); }); it('getParent/getChild', () => { const ast = parser.parse('/a(bc)d/'); const bodyPath = NodePath.getForNode(ast.body); const groupPath = bodyPath.getChild(1); expect(groupPath.node.type).toBe('Group'); expect(groupPath.getParent()).toBe(bodyPath); const alterPath = groupPath.getChild(); expect(alterPath.node.type).toBe('Alternative'); expect(alterPath.getParent()).toBe(groupPath); const bCharPath = alterPath.getChild(0); const cCharPath = alterPath.getChild(1); expect(bCharPath.getParent()).toBe(alterPath); expect(cCharPath.getParent()).toBe(alterPath); expect(bCharPath.getParent().getParent()).toBe(groupPath); expect(cCharPath.getParent().getParent()).toBe(groupPath); expect(groupPath.getChild()).toBe(alterPath); expect(groupPath.getChild(0)).toBe(alterPath); expect(groupPath.getChild(1)).toBe(null); expect(groupPath.getChild().getChild(0)).toBe(bCharPath); expect(groupPath.getChild().getChild(1)).toBe(cCharPath); expect(groupPath.getChild()).toBe(alterPath); expect(groupPath.getChild(0)).toBe(alterPath); expect(groupPath.getChild(1)).toBe(null); }); it('hasEqualSource', () => { const ast = parser.parse('/aba/', { captureLocations: true, }); const parentPath = NodePath.getForNode(ast.body); const a1Path = NodePath.getForNode( ast.body.expressions[0], parentPath, 'expressions', 0 ); const bPath = NodePath.getForNode( ast.body.expressions[1], parentPath, 'expressions', 1 ); const a2Path = NodePath.getForNode( ast.body.expressions[2], parentPath, 'expressions', 2 ); expect(a1Path.hasEqualSource(a2Path)).toBe(true); expect(bPath.hasEqualSource(a1Path)).toBe(false); expect(bPath.hasEqualSource(a2Path)).toBe(false); }); it('jsonEncode', () => { const node = { type: 'Char', value: 'a', symbol: 'a', kind: 'simple', codePoint: 'a'.codePointAt(0) }; const path = NodePath.getForNode( Object.assign({}, node, { loc: { source: 'a', start: 1, end: 2, } }) ); expect(path.jsonEncode()) .toBe(JSON.stringify(node, null, (prop, value) => { if (prop === 'loc') { return undefined; } return value; })); }); }); regexp-tree-0.1.18/src/traverse/__tests__/traverse-basic-test.js000066400000000000000000000175301361507317500246530ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const traverse = require('..'); const parser = require('../../parser'); const defaultAst = parser.parse('/[a-z]+a/i'); describe('traverse-basic', () => { it('visits each node as a NodePath', () => { const visited = []; traverse.traverse(defaultAst, { RegExp({node, parent}) { visited.push(node.type); expect(node.type).toBe('RegExp'); expect(parent).toBe(null); }, Alternative({node, parent}) { visited.push(node.type); expect(node.type).toBe('Alternative'); expect(parent.type).toBe('RegExp'); expect(node.expressions.length).toBe(2); expect(node.expressions[0].type).toBe('Repetition'); expect(node.expressions[1].type).toBe('Char'); }, Repetition({node, parent}) { visited.push(node.type); expect(node.type).toBe('Repetition'); expect(parent.type).toBe('Alternative'); expect(node.expression.type).toBe('CharacterClass'); expect(node.quantifier.type).toBe('Quantifier'); expect(node.quantifier.kind).toBe('+'); }, CharacterClass({node, parent}) { visited.push(node.type); expect(node.type).toBe('CharacterClass'); expect(parent.type).toBe('Repetition'); expect(node.expressions.length).toBe(1); expect(node.expressions[0].type).toBe('ClassRange'); }, ClassRange({node, parent, property, index}) { visited.push(node.type); expect(node.type).toBe('ClassRange'); expect(parent.type).toBe('CharacterClass'); expect(node.from.type).toBe('Char'); expect(node.to.type).toBe('Char'); expect(property).toBe('expressions'); expect(index).toBe(0); }, Quantifier({node}) { visited.push(node.type); expect(node.type).toBe('Quantifier'); expect(node.kind).toBe('+'); }, Char({node, parent}) { visited.push(node.type); expect(node.type).toBe('Char'); // Char appears only in these two parent nodes: expect( parent.type === 'ClassRange' || parent.type === 'Alternative' ).toBe(true); }, }); // Visit order. expect(visited).toEqual([ 'RegExp', 'Alternative', 'Repetition', 'CharacterClass', 'ClassRange', 'Char', 'Char', 'Quantifier', 'Char', ]); }); it('visits each node as raw', () => { const visited = []; // This traversal receives raw nodes in handlers. const options = {asNodes: true}; traverse.traverse(defaultAst, { RegExp(node, parent) { visited.push(node.type); expect(node.type).toBe('RegExp'); expect(parent).toBe(null); }, Alternative(node, parent) { visited.push(node.type); expect(node.type).toBe('Alternative'); expect(parent.type).toBe('RegExp'); expect(node.expressions.length).toBe(2); expect(node.expressions[0].type).toBe('Repetition'); expect(node.expressions[1].type).toBe('Char'); }, Repetition(node, parent) { visited.push(node.type); expect(node.type).toBe('Repetition'); expect(parent.type).toBe('Alternative'); expect(node.expression.type).toBe('CharacterClass'); expect(node.quantifier.type).toBe('Quantifier'); expect(node.quantifier.kind).toBe('+'); }, CharacterClass(node, parent) { visited.push(node.type); expect(node.type).toBe('CharacterClass'); expect(parent.type).toBe('Repetition'); expect(node.expressions.length).toBe(1); expect(node.expressions[0].type).toBe('ClassRange'); }, ClassRange(node, parent, property, index) { visited.push(node.type); expect(node.type).toBe('ClassRange'); expect(parent.type).toBe('CharacterClass'); expect(node.from.type).toBe('Char'); expect(node.to.type).toBe('Char'); expect(property).toBe('expressions'); expect(index).toBe(0); }, Quantifier(node) { visited.push(node.type); expect(node.type).toBe('Quantifier'); expect(node.kind).toBe('+'); }, Char(node, parent) { visited.push(node.type); expect(node.type).toBe('Char'); // Char appears only in these two parent nodes: expect( parent.type === 'ClassRange' || parent.type === 'Alternative' ).toBe(true); }, }, options); // Visit order. expect(visited).toEqual([ 'RegExp', 'Alternative', 'Repetition', 'CharacterClass', 'ClassRange', 'Char', 'Char', 'Quantifier', 'Char', ]); }); it('modifies a direct node', () => { const ast = parser.parse('/a{1,}/'); traverse.traverse(ast, { Quantifier({node}) { if (node.kind === 'Range' && node.from == 1 && !node.to) { node.kind = '+'; delete node.from; } }, }); expect(ast.body.quantifier).toEqual({ type: 'Quantifier', kind: '+', greedy: true, }); }); it('replaces a node using parent', () => { const ast = parser.parse('/a{1,}?/'); traverse.traverse(ast, { Quantifier({node, parent, property}) { if (node.kind === 'Range' && node.from == 1 && !node.to) { parent[property] = { type: 'Quantifier', kind: '+', greedy: node.greedy, }; } }, }); expect(ast.body.quantifier).toEqual({ type: 'Quantifier', kind: '+', greedy: false, }); }); it('multiple handlers', () => { const ast = parser.parse('/a/'); expect(ast.body.value).toBe('a'); // Two handlers. const handlers = [ { Char({node}) { node.value = 'b'; }, }, { Char({node}) { node.value += 'c'; }, } ]; traverse.traverse(ast, handlers); expect(ast.body.value).toBe('bc'); }); it('shouldRun hook', () => { const handler = { shouldRun(ast) { return ast.flags.includes('s'); }, RegExp({node}) { node.flags = node.flags.replace('s', ''); }, Char(path) { const {node} = path; if (node.kind === 'meta' && node.value === '.') { path.replace({ type: 'CharacterClass', negative: true, }); } }, }; // Should run. let ast = parser.parse('/./s'); traverse.traverse(ast, handler); expect(ast).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', negative: true, }, flags: '', }); // Should not run. ast = parser.parse('/./'); traverse.traverse(ast, handler); expect(ast).toEqual({ type: 'RegExp', body: { type: 'Char', value: '.', symbol: '.', kind: 'meta', codePoint: NaN }, flags: '', }); // Should run (not `shouldRun` hook present). ast = parser.parse('/./'); traverse.traverse(ast, { Char({node, parent, property}) { if (node.kind === 'meta' && node.value === '.') { parent[property] = { type: 'CharacterClass', negative: true, }; } }, }); expect(ast).toEqual({ type: 'RegExp', body: { type: 'CharacterClass', negative: true, }, flags: '', }); }); it('catch-all * handler', () => { const ast = parser.parse('/a+/'); const visited = []; traverse.traverse(ast, { '*': ({node}) => { visited.push(node.type); }, }); expect(visited).toEqual([ 'RegExp', 'Repetition', 'Char', 'Quantifier', ]); }); });regexp-tree-0.1.18/src/traverse/index.js000066400000000000000000000176441361507317500201430ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const 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, options = {}) { const pre = options.pre; const post = options.post; const skipProperty = options.skipProperty; function visit(node, parent, prop, idx) { if (!node || typeof node.type !== 'string') { return; } let 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 (let prop in node) if (node.hasOwnProperty(prop)) { if (skipProperty ? skipProperty(prop, node) : prop[0] === '$') { continue; } const 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)) { let 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 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(ast, handlers, options = {asNodes: false}) { if (!Array.isArray(handlers)) { handlers = [handlers]; } // Filter out handlers by result of `shouldRun`, if the method is present. handlers = handlers.filter(handler => { if (typeof handler.shouldRun !== 'function') { return true; } return handler.shouldRun(ast); }); NodePath.initRegistry(); // Allow handlers to initializer themselves. handlers.forEach(handler => { if (typeof handler.init === 'function') { handler.init(ast); } }); function getPathFor(node, parent, prop, index) { const parentPath = NodePath.getForNode(parent); const nodePath = NodePath.getForNode( node, parentPath, prop, index ); return nodePath; } // Handle actual nodes. astTraverse(ast, { /** * Handler on node enter. */ pre(node, parent, prop, index) { let nodePath; if (!options.asNodes) { nodePath = getPathFor(node, parent, prop, index); } for (const handler of handlers) { // "Catch-all" `*` handler. if (typeof handler['*'] === 'function') { if (nodePath) { // A path/node can be removed by some previous handler. if (!nodePath.isRemoved()) { const handlerResult = handler['*'](nodePath); // Explicitly stop traversal. if (handlerResult === false) { return false; } } } else { handler['*'](node, parent, prop, index); } } // Per-node handler. let handlerFuncPre; 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()) { const handlerResult = handlerFuncPre.call(handler, nodePath); // Explicitly stop traversal. if (handlerResult === false) { return false; } } } else { handlerFuncPre.call(handler, node, parent, prop, index); } } } // Loop over handlers }, // pre func /** * Handler on node exit. */ post(node, parent, prop, index) { if (!node) { return; } let nodePath; if (!options.asNodes) { nodePath = getPathFor(node, parent, prop, index); } for (const handler of handlers) { // Per-node handler. let handlerFuncPost; 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()) { const handlerResult = handlerFuncPost.call(handler, nodePath); // Explicitly stop traversal. if (handlerResult === false) { return false; } } } else { handlerFuncPost.call(handler, node, parent, prop, index); } } } // Loop over handlers }, // post func /** * Skip locations by default. */ skipProperty(prop) { return prop === 'loc'; }, }); }, }; regexp-tree-0.1.18/src/traverse/node-path.js000066400000000000000000000216161361507317500207050ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; const DEFAULT_COLLECTION_PROP = 'expressions'; const 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. */ class NodePath { /** * 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. */ constructor( node, parentPath = null, property = null, index = null ) { this.node = node; this.parentPath = parentPath; this.parent = parentPath ? parentPath.node : null; this.property = property; this.index = index; } _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 */ setChild(node, index = null, property = null) { let childPath; 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 */ appendChild(node, property = null) { if (!property) { property = DEFAULT_COLLECTION_PROP; } this._enforceProp(property); const 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 */ insertChildAt(node, index, property = 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. */ 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). */ _rebuildIndex(parent, property) { const parentPath = NodePath.getForNode(parent); for (let i = 0; i < parent[property].length; i++) { const path = NodePath.getForNode( parent[property][i], parentPath, property, i ); path.index = i; } } /** * Whether the path was removed. */ isRemoved() { return this.node === null; } /** * Replaces a node with the passed one. */ 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. */ update(nodeProps) { Object.assign(this.node, nodeProps); } /** * Returns parent. */ getParent() { return this.parentPath; } /** * Returns nth child. */ getChild(n = 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 */ hasEqualSource(path) { return ( JSON.stringify(this.node, jsonSkipLoc) === JSON.stringify(path.node, jsonSkipLoc) ); } /** * JSON-encodes a node skipping location. */ jsonEncode({format, useLoc} = {}) { return JSON.stringify( this.node, useLoc ? null : jsonSkipLoc, format ); } /** * Returns previous sibling. */ 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. */ 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. */ static getForNode(node, parentPath = null, prop = null, index = -1) { if (!node) { return null; } if (!NodePath.registry.has(node)) { NodePath.registry.set( node, new NodePath(node, parentPath, prop, index == -1 ? null : index) ); } let 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. */ static initRegistry() { if (!NodePath.registry) { NodePath.registry = new Map(); } NodePath.registry.clear(); } /** * Updates index of a currently traversing collection. */ static updateTraversingIndex(dx) { return ( NodePath.traversingIndexStack[ NodePath.traversingIndexStack.length - 1 ] += dx ); } /** * Returns current traversing index. */ static getTraversingIndex() { return NodePath.traversingIndexStack[ NodePath.traversingIndexStack.length - 1 ]; } } 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; regexp-tree-0.1.18/src/utils/000077500000000000000000000000001361507317500157675ustar00rootroot00000000000000regexp-tree-0.1.18/src/utils/__tests__/000077500000000000000000000000001361507317500177255ustar00rootroot00000000000000regexp-tree-0.1.18/src/utils/__tests__/utils-clone-test.js000066400000000000000000000022041361507317500234740ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ const clone = require('../clone'); describe('utils-clone', () => { it('clones null', () => { expect(clone(null)).toBe(null); }); it('clones scalars', () => { expect(clone(true)).toBe(true); expect(clone(42.25)).toBe(42.25); expect(clone('foobar')).toBe('foobar'); }); it('clones arrays', () => { const arr = [ 1, [false], ['string', {}], 'foo' ]; const arrCopy = clone(arr); expect(arrCopy).toEqual(arr); expect(arrCopy).not.toBe(arr); expect(arrCopy[1]).not.toBe(arr[1]); expect(arrCopy[2]).not.toBe(arr[2]); expect(arrCopy[2][1]).not.toBe(arr[2][1]); }); it('clones objects', () => { const obj = { prop: { arr: [{}, 1, true, ''], prop: {} }, arr: [] }; const objCopy = clone(obj); expect(objCopy).toEqual(obj); expect(objCopy).not.toBe(obj); expect(objCopy.prop).not.toBe(obj.prop); expect(objCopy.arr).not.toBe(obj.arr); expect(objCopy.prop.arr[0]).not.toBe(obj.prop.arr[0]); }); });regexp-tree-0.1.18/src/utils/clone.js000066400000000000000000000010031361507317500174170ustar00rootroot00000000000000/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ '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; } let res; if (Array.isArray(obj)) { res = []; } else { res = {}; } for (let i in obj) { res[i] = clone(obj[i]); } return res; };