securityos/node_modules/regexp-tree/README.md

2125 lines
44 KiB
Markdown
Raw Permalink Normal View History

2024-09-06 15:32:35 +00:00
# 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)
- [Parsing options](#parsing-options)
- [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/<your-github-account>/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,
});
```
### Parsing options
The parser supports several options which can be set globally via the `setOptions` method on the parser, or by passing them with each `parse` method invocation.
Example:
```js
const regexpTree = require('regexp-tree');
const parsed = regexpTree.parse(/a|b/, {
allowGroupNameDuplicates: true,
});
```
The following options are supported:
- `captureLocations: boolean` -- whether to capture AST node [locations](#capturing-locations) (`false` by default)
- `allowGroupNameDuplicates: boolean` -- whether to skip duplicates check of the [named capturing groups](#named-capturing-group)
Set `allowGroupNameDuplicates` would make the following expression possible:
```regexp
/
# YYY-MM-DD date format:
(?<year> \d{4}) -
(?<month> \d{2}) -
(?<day> \d{2})
|
# DD.MM.YYY date format
(?<day> \d{2}) .
(?<month> \d{2}) .
(?<year> \d{4})
/x
```
### 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
/(?<value>a)\k<value>\1/
```
Becomes:
```js
/(a)\1\1/
```
To use the API from Node:
```js
const regexpTree = require('regexp-tree');
// Using new syntax.
const originalRe = '/(?<all>.)\\k<all>/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 '/(?<all>.)\k<all>/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
Some of the _non-standard_ feature are also supported by _regexp-tree_.
> NOTE: _"non-standard"_ means specifically ECMAScript standard, since in other regexp egnines, e.g. PCRE, Python, etc. these features are standard.
One of such features is the `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.
(?<year>\d{4})- # year part of a date
(?<month>\d{2})- # month part of a date
(?<day>\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.
(?<year>\\d{4})- # year part of a date
(?<month>\\d{2})- # month part of a date
(?<day>\\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
A capturing group can be given a name using the `(?<name>...)` syntax, for any identifier `name`.
For example, a regular expressions for a date:
```js
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
```
For the group:
```js
(?<foo>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<name>` pattern, and also using a numbered reference.
Matches `www`:
```js
(?<foo>w)\k<foo>\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`:
```
(?<!a)b
```
A node:
```js
{
type: 'Alternative',
expressions: [
{
type: 'Assertion',
kind: 'Lookbehind',
negative: true,
assertion: {
type: 'Char',
value: 'a',
symbol: 'a',
kind: 'simple',
codePoint: 97
}
},
{
type: 'Char',
value: 'b',
symbol: 'b',
kind: 'simple',
codePoint: 98
},
]
}
```