regexp-to-ast
regexp-to-ast parses the text form of an older JavaScript regular-expression literal, including its slash delimiters and flags, into a compact AST. The tree represents alternatives, assertions, groups, backreferences, character sets, code-unit values, and quantifier bounds, with numeric source offsets on every node. It also ships a base visitor for walking the result. It analyzes syntax only; it does not execute, optimize, validate safety, or regenerate a RegExp.
Its tiny AST and visitor remain understandable, but the accepted grammar stopped before several now-common JavaScript RegExp features. Keep it for compatible legacy consumers; choose @eslint-community/regexpp or regjsparser for new analysis tools.
Use it if
- You maintain a tool that already consumes this package's small, stable AST shape
- Your input is limited to the pre-lookbehind JavaScript RegExp subset documented by version 0.5.0
- You need ES5-style browser code with no runtime dependencies and can load the actual distributed UMD file
- You want a simple subclassable visitor and numeric begin/end offsets without a larger ESTree-compatible parser
- You need current ECMAScript regex syntax: the flag parser accepts only g, i, m, u, and y, excluding dotAll s, indices d, and Unicode sets v
- You need named groups, named backreferences, lookbehind, Unicode property escapes, or code-point escapes: the README explicitly lists Unicode flag escapes and non-BMP handling as unfinished, and the published AST has no node types for the other modern constructs
- You need useful diagnostics for editor tooling: the README lists descriptive messages and error positions as TODO items, and source contains generic errors such as Unexpected end of input and Internal Error
- You want an actively evolving parser: npm version 0.5.0 dates to December 2019 and the latest default-branch commits shown by GitHub are dependency and example updates from February 2020
- You intend to use the README's browser script tag unchanged: it points to lib/parser.js, but the published package contains lib/regexp-to-ast.js instead
Setup reality
npm install regexp-to-ast has no runtime dependencies, native build, peer dependency, credential, or configuration step. In CommonJS, require the package and construct RegExpParser. The parser does not accept a bare source such as a|b; pattern expects literal text beginning with / and ending with / plus flags. A RegExp instance is not accepted directly, so call toString() first. The same parser instance can be reused sequentially because each pattern call resets its mutable index, input, and capture counter, but do not share one instance across overlapping asynchronous work. Version 0.5.0 recognizes only g, i, m, u, and y flags, throws on duplicates, and rejects trailing input. Its TypeScript declaration describes the shipped AST well, but VERSION is incorrectly declared as number even though runtime exports the string "0.5.0". Characters and set members are numeric UTF-16 code units; the README warns that characters outside the Basic Multilingual Plane need unfinished support. Quantifier infinity is represented by JavaScript Infinity, which becomes null if you JSON.stringify the AST. The base visitor calls your type-specific method and then always traverses children, including quantifiers attached to atoms. Override visitChildren if you need pruning. Browser use relies on the UMD build at lib/regexp-to-ast.js and creates window.regexpToAst; the README's lib/parser.js path does not exist in the tarball. There is no parser option object, recovery mode, custom grammar, AST generator, or safety analysis for catastrophic backtracking.
Patterns
Parse regular-expression literal textparse-literal
const { RegExpParser } = require('regexp-to-ast');
const parser = new RegExpParser();
const ast = parser.pattern('/^(cat|dog)s?$/i');
console.log(ast.type); // PatternInput must include opening and closing slashes plus any flags. A bare source string such as ^cat$ is rejected immediately.
Parse an existing RegExp objectparse-regexp-object
const expression = /a|b/gi;
const ast = parser.pattern(expression.toString());pattern accepts only text. RegExp.prototype.toString supplies escaped slash delimiters and flags in the format the parser expects.
Reuse one parser sequentiallyreuse-parser
const inputs = ['/one/', '/(two)+/g', '/three|four/i'];
const trees = inputs.map((input) => parser.pattern(input));Each call resets mutable parser state. Sequential reuse is documented; do not interleave calls by wrapping or extending the parser with asynchronous callbacks.
Inspect parsed flagsread-flags
const { flags } = parser.pattern('/name/gimuy');
console.log({
global: flags.global,
ignoreCase: flags.ignoreCase,
multiLine: flags.multiLine,
unicode: flags.unicode,
sticky: flags.sticky,
});These are the only accepted flags. Inputs containing s, d, or v are rejected as redundant trailing input.
Read top-level alternativesinspect-alternatives
const ast = parser.pattern('/red|green|blue/');
const alternatives = ast.value.value;
console.log(alternatives.length); // 3
console.log(alternatives.map((alt) => alt.type));Pattern.value is a Disjunction, and Disjunction.value is an array of Alternative nodes even when there is only one branch.
Convert character nodes back to textdecode-characters
const ast = parser.pattern('/abc/');
const terms = ast.value.value[0].value;
const text = terms
.filter((node) => node.type === 'Character')
.map((node) => String.fromCharCode(node.value))
.join('');
console.log(text); // abcCharacter.value stores a UTF-16 code unit, not a string or full Unicode code point. Non-BMP characters are a documented limitation.
Expand character set entriesinspect-character-set
const ast = parser.pattern('/[^a-c0-2]/');
const set = ast.value.value[0].value[0];
for (const item of set.value) {
if (typeof item === 'number') console.log('code unit', item);
else console.log('range', item.from, item.to);
}
console.log('negated', set.complement);Set values mix numeric code units and { from, to } ranges. Expanded escapes such as \d can add many numeric entries.
Inspect repetition bounds and greedinessread-quantifier
const ast = parser.pattern('/a{2,}?/');
const atom = ast.value.value[0].value[0];
console.log(atom.quantifier);
// { type: 'Quantifier', atLeast: 2, atMost: Infinity, greedy: false, loc: ... }Unbounded maxima use Infinity. JSON.stringify turns Infinity into null, so encode it explicitly before persisting an AST.
Collect numbered capture groupsfind-capturing-groups
const { BaseRegExpVisitor } = require('regexp-to-ast');
class CaptureVisitor extends BaseRegExpVisitor {
constructor() {
super();
this.groups = [];
}
visitGroup(node) {
if (node.capturing) this.groups.push(node.idx);
}
}
const visitor = new CaptureVisitor();
visitor.visit(parser.pattern('/(a)(?:b)(c)/'));
console.log(visitor.groups); // [1, 2]Only numbered captures exist in this AST. The base visitor traverses children automatically after visitGroup returns.
Count every AST node typecount-node-types
class CountingVisitor extends BaseRegExpVisitor {
constructor() {
super();
this.counts = Object.create(null);
}
visit(node) {
this.counts[node.type] = (this.counts[node.type] || 0) + 1;
super.visit(node);
}
}
const visitor = new CountingVisitor();
visitor.visit(parser.pattern('/a+(b|c)/'));
console.log(visitor.counts);Call super.visit(node) or traversal stops at the current node. Quantifiers are child nodes and receive their own visits.
Map a node back to input textslice-source-location
const input = '/foo(bar)+/g';
const ast = parser.pattern(input);
const group = ast.value.value[0].value[3];
console.log(input.slice(group.loc.begin, group.loc.end)); // (bar)+loc uses zero-based begin and exclusive end offsets into the complete literal text, including delimiters and flags. An atom's range includes its attached quantifier.
Load the actual browser bundleload-in-browser
<script src="https://unpkg.com/regexp-to-ast@0.5.0/lib/regexp-to-ast.js"></script>
<script>
const parser = new regexpToAst.RegExpParser();
console.log(parser.pattern('/hello/i'));
</script>The README shows lib/parser.js, but that file is absent from 0.5.0. The distributed UMD file is lib/regexp-to-ast.js and exposes regexpToAst globally.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @eslint-community/regexpp | npm | Use a maintained ECMAScript parser when current flags, named groups, lookbehind, Unicode properties, and lint tooling matter |
| regjsparser | npm | Use the parser from the Babel and regjs stack when broad modern JavaScript RegExp syntax coverage is the priority |
| regexp-tree | npm | Use a parser plus traversal, transformation, optimization, and generation toolkit rather than an AST reader alone |
| regexpp | npm | Use the older standalone ECMAScript parser only when an existing dependency requires its established API; prefer the community fork for new work |