repattern Specification 1.0.0-beta.0
Specification of an embedded DSL for building JavaScript RegExp from declarative schemas.
Languages: Russian
Contribute: GitHub teplostanski/repattern-spec (new issue, open issues)
Versions: 1.0.0-beta.0 (this page), current
Abstract
The repattern specification defines an embedded DSL for building JavaScript regular expressions from declarative Schema objects that are converted into RegExp instances.
Schemas provide a human-readable way to describe patterns, as an alternative to writing regular expressions manually.
Note
To use this specification effectively, you should be familiar with regular expression terminology and how regular expressions work, since schemas express the same concepts in a declarative form.
Table of Contents
1. Terms
1.1. Schema
A schema is an array (sequencer) of objects (atoms) that describes a regular expression in a declarative form. Each element of the schema—an atom—represents one component of the pattern.
Simple example:
const schema = [
{ lineStart: true },
{ zeroOrMore: [{ charIn: 'a-z' }] },
{ lineEnd: true },
];
// Result: /^[a-z]*$/
Example: validating an email address
Regular expression:
/^(?:[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-])+@[a-zA-Z0-9](?:(?:[a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?
(?:\.[a-zA-Z0-9](?:(?:[a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?)+$/
This regular expression validates email addresses according to RFC 5322.
The schema that describes this regular expression:
const schema = [
{ lineStart: true }, // ^
{
repeat: [
// (?: ... )+
{ charIn: "a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-" }, // [a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]
],
},
{ exactly: '@' }, // @
{ charIn: 'a-zA-Z0-9' }, // [a-zA-Z0-9]
{
maybe: [
// (?: ... )?
{
repeat: [
// (?: ... ){0,61}
{ charIn: 'a-zA-Z0-9-' }, // [a-zA-Z0-9-]
{ params: { times: [0, 61] } },
],
},
{ charIn: 'a-zA-Z0-9' }, // [a-zA-Z0-9]
],
},
{
repeat: [
// (?: ... )+
{ exactly: '.' }, // .
{ charIn: 'a-zA-Z0-9' }, // [a-zA-Z0-9]
{
maybe: [
// (?: ... )?
{
repeat: [
// (?: ... ){0,61}
{ charIn: 'a-zA-Z0-9-' }, // [a-zA-Z0-9-]
{ params: { times: [0, 61] } },
],
},
{ charIn: 'a-zA-Z0-9' }, // [a-zA-Z0-9]
],
},
],
},
{ lineEnd: true }, // $
];
1.2. Atom
An atom is the smallest unit of a schema. It describes one component of the pattern (an anchor, character class, quantified sequence, alternation, etc.).
An atom is an object with exactly one key that defines its type. The value of that key may only be:
- a string;
- a number or an array of two numbers (only for the
timesparameter; see Params objectparams); - a boolean;
- an array of atoms (a sequencer), or—for
anyOf—an array of anonymous sequencers.
An atom’s value cannot be another atom or an arbitrary object.
Any object that appears in a schema is treated as an atom,
except the special params object, which is not an atom
and does not describe a pattern component of its own (see Params object params).
- lineStart
- lineEnd
- exactly
- anyChar
- tab
- lineFeed
- carriageReturn
- referenceTo
- charIn
- charNotIn
- unicodeProps
- digit
- word
- whitespace
- boundary
1.3. Sequencer
A sequencer is an array of atoms that combines several subpatterns into a sequence. The order of atoms in a sequencer matches their order in the resulting regular expression.
Kinds of sequencers
A schema defines three kinds of sequencers, depending on context and what they may contain:
-
Root sequencer — the top-level array of the schema. Contains only atoms.
-
Atom-sequencer — an atom whose value is a sequencer (see Atom-sequencer).
-
Anonymous sequencer — an unnamed sequencer that appears only inside the
anyOfatom-sequencer. It describes alternative branches and may contain only atoms.
1.4. Atom-sequencer
An atom-sequencer is an atom whose value is a sequencer (an array of atoms) or, for anyOf, an array of sequencers.
It describes a sequence of subpatterns: nested atoms are processed in array order and concatenate into a single group.
An atom-sequencer always wraps its subpatterns in a group equivalent to one of the following:
- non-capturing
(?: … ) - capturing
( … ) - named
(?<name> … )
By default, an atom-sequencer creates a non-capturing group (?: … ) (except grouped).
You can change the group type with the group parameter in the params object.
1.5. Params object params
The params object is metadata used to configure an atom-sequencer.
It is not a schema atom, does not take part in the subpattern sequence, and does not change the order of nested atoms.
The params object must appear at the end of the atom-sequencer array and is treated as metadata for the parent atom-sequencer.
Parameters
times
Specifies an exact repetition count or a range.
Applies only to the repeat quantifier.
times?: number | [number] | [number, number];
| Value | Equivalent | Description |
|---|---|---|
n | {n} | exactly n repetitions |
[min] | {min,} | from min to infinity |
[min, max] | {min,max} | repetition range |
If
timesis omitted, the+quantifier is used (one or more repetitions).
lazy
Enables a lazy (non-greedy) quantifier.
true makes the quantifier lazy (*?, {n,}?); false keeps the default greedy behavior. Applies to all quantifiers: repeat, zeroOrMore, maybe.
lazy?: boolean;
group
Defines the type of group:
false: non-capturing group(?: … )true: capturing group( … )"<name>": named group(?<name> … )
group?: boolean | string;
optionally
When true, makes the alternation group optional.
Applies only to anyOf alternation.
optionally?: boolean;
Usage context
The params object may appear only inside atom-sequencers.
Parameters by atom-sequencer:
| Parameter | Atom-sequencer |
|---|---|
times | repeat |
lazy | repeat, zeroOrMore, maybe |
group | repeat, zeroOrMore, maybe, grouped, anyOf |
optionally | anyOf |
2. Types
This section describes the TypeScript types used with schemas.
2.1. Schema
A schema is a root sequencer that contains only atoms.
type Schema = Atom[];
2.2. Atom
An atom is the smallest unit of a schema. In TypeScript, it is represented as a union of all atom types.
type Atom = BooleanAtom | StringAtom | ReferenceToAtom | AtomSequencer;
type BooleanAtom =
| { lineStart: boolean }
| { lineEnd: boolean }
| { digit: boolean }
| { word: boolean }
| { whitespace: boolean }
| { boundary: boolean }
| { anyChar: boolean }
| { tab: boolean }
| { lineFeed: boolean }
| { carriageReturn: boolean };
type StringAtom =
| { exactly: string }
| { charIn: string }
| { charNotIn: string }
| { unicodeProps: string };
type ReferenceToAtom = { referenceTo: number | string };
2.3. AtomSequencer
These atom-sequencers accept a sequencer—or, for anyOf, an array of sequencers—along with the corresponding parameter types.
type AtomSequencer =
| { repeat: RepeatSequence }
| { zeroOrMore: ZeroOrMoreSequence }
| { maybe: MaybeSequence }
| { grouped: GroupedSequence }
| { anyOf: AnyOfSequence };
// params is the last array element when present
type RepeatSequence =
| Atom[]
| [...Atom[], { params: RepeatParams }];
type ZeroOrMoreSequence =
| Atom[]
| [...Atom[], { params: ZeroOrMoreParams }];
type MaybeSequence =
| Atom[]
| [...Atom[], { params: MaybeParams }];
type GroupedSequence =
| Atom[]
| [...Atom[], { params: GroupedParams }];
type AnyOfSequence =
| Atom[][]
| [...Atom[][], { params: AnyOfParams }];
2.4. Params
Parameter types for atom-sequencers. Each atom-sequencer has its own set of allowed parameters.
type Params =
| RepeatParams
| ZeroOrMoreParams
| MaybeParams
| GroupedParams
| AnyOfParams;
type RepeatParams = {
times?: number | [number] | [number, number];
lazy?: boolean;
group?: boolean | string;
};
type ZeroOrMoreParams = {
lazy?: boolean;
group?: boolean | string;
};
type MaybeParams = {
lazy?: boolean;
group?: boolean | string;
};
type GroupedParams = {
group?: boolean | string;
};
type AnyOfParams = {
group?: boolean | string;
optionally?: boolean;
};
3. Atom-sequencers
3.1. Quantifiers
3.1.1 repeat
- Type: AtomSequencer
- Equivalent:
{n},{min,max},{min,},+
repeat — Creates a grouped subpattern with a quantifier.
Groups subpatterns and repeats them according to times (or defaults to +).
Supported parameters
The params object may contain (see Params object params):
times— when omitted, defaults to+(one or more times);lazy— defaultfalse;group— defaultfalse(non-capturing group(?: … )).
Examples
const schema = [
{ repeat: [{ charIn: 'a-z' }, { params: { times: [0, 3], lazy: true } }] },
];
// Result: /(?:[a-z]){0,3}?/
const schema = [
{ repeat: [{ exactly: 'foo' }, { params: { group: 'word' } }] },
];
// Result: /(?<word>foo)+/
3.1.2. zeroOrMore
- Type: AtomSequencer
- Equivalent:
*
zeroOrMore — Applies the * quantifier to a grouped subpattern.
Matches the subpattern zero or more times.
Supported parameters
The params object may contain (see Params object params):
Examples
const schema = [{ zeroOrMore: [{ exactly: 'foo' }] }];
// Result: /(?:foo)*/
const schema = [
{
zeroOrMore: [
{ charIn: 'a-z' },
{ params: { group: 'letters', lazy: true } },
],
},
];
// Result: /(?<letters>[a-z]*?)/
3.1.3. maybe
- Type: AtomSequencer
- Equivalent:
?
maybe — Applies the ? quantifier to a grouped subpattern (zero or one match).
Makes the subpattern optional.
Supported parameters
The params object may contain (see Params object params):
Examples
const schema = [{ maybe: [{ exactly: 'foo' }] }];
// Result: /(?:foo)?/
const schema = [
{ maybe: [{ charIn: 'A-Z' }, { params: { group: 'opt', lazy: true } }] },
];
// Result: /(?<opt>[A-Z])??/
Note
For deeply nested anyOf alternation, prefer the optionally parameter over wrapping in maybe to avoid unnecessary nesting.
3.2. Group
3.2.1. grouped
- Type: AtomSequencer
- Equivalent:
(...),(?:...),(?<name>...)
grouped — Creates a group.
Concatenates several subpatterns into a single group.
Supported parameters
The params object may contain (see Params object params):
group— defaulttrue(capturing group( … )).
Examples
const schema = [{ grouped: [{ exactly: 'foo' }, { charIn: 'A-Z' }] }];
// Result: /(foo[A-Z])/
const schema = [
{ grouped: [{ exactly: 'bar' }, { params: { group: false } }] },
];
// Result: /(?:bar)/
const schema = [
{ grouped: [{ exactly: 'buzz' }, { params: { group: 'word' } }] },
];
// Result: /(?<word>buzz)/
3.3. Alternation
3.3.1. anyOf
- Type: AtomSequencer
- Equivalent:
|
anyOf — Creates alternation using the | operator.
Defines alternative branches, at least one of which must match.
Structure
The value of the anyOf key is an array of anonymous sequencers (alternation branches):
{
anyOf: [
[ /* branch 1 */ ],
[ /* branch 2 */ ],
...
]
}
Supported parameters
The params object may contain (see Params object params):
group— defaultfalse(non-capturing group(?: … ));optionally— defaultfalse.
Note
To make an alternation optional, use the optionally parameter instead of wrapping anyOf in maybe to avoid unnecessary nesting.
Examples
const schema = [
{
anyOf: [
[{ charIn: '01' }, { digit: true }], // branch 1
[{ exactly: '2' }, { charIn: '0-3' }], // branch 2
{ params: { group: 'hours' } },
],
},
{ exactly: ':' },
{
grouped: [
{ charIn: '0-5' },
{ digit: true },
{ params: { group: 'minutes' } },
],
},
];
// Result: /(?<hours>[01]\d|2[0-3]):(?<minutes>[0-5]\d)/
const str = '23:59 25:99 1:2';
const re = /(?<hours>[01]\d|2[0-3]):(?<minutes>[0-5]\d)/;
const result = str.match(re);
console.log(result);
/* Output:
[
'23:59',
'23',
'59',
index: 0,
input: '23:59 25:99 1:2',
groups: [Object: null prototype] { hours: '23', minutes: '59' }
]
*/
4. Atoms
4.1. Anchors
4.1.1. lineStart
- Type: Atom
- Equivalent:
^
lineStart — Anchor matching the start of a line (^).
true emits the anchor; false omits the atom from the resulting pattern.
{
lineStart: true;
} // ^
4.1.2. lineEnd
- Type: Atom
- Equivalent:
$
lineEnd — Anchor matching the end of a line ($).
true emits the anchor; false omits the atom from the resulting pattern.
{
lineEnd: true;
} // $
4.2. Literals and special characters
4.2.1. exactly
- Type: Atom
exactly — Matches a literal string.
Accepts a string that is escaped and inserted into the regular expression as-is.
{
exactly: 'foo';
} // foo
{
exactly: '.';
} // \.
{
exactly: '(';
} // \(
4.2.2. anyChar
- Type: Atom
- Equivalent:
.
anyChar — Matches any character except line terminators.
{
anyChar: true;
} // .
4.2.3. tab
- Type: Atom
- Equivalent:
\t
tab — Matches a tab character (\t).
{
tab: true;
} // \t
4.2.4. lineFeed
- Type: Atom
- Equivalent:
\n
lineFeed — Matches a line feed character (\n).
{
lineFeed: true;
} // \n
4.2.5. carriageReturn
- Type: Atom
- Equivalent:
\r
carriageReturn — Matches a carriage return character (\r).
{
carriageReturn: true;
} // \r
4.3. Character sets
4.3.1. charIn
- Type: Atom
- Equivalent:
[ ... ]
charIn — Matches any character in the specified set.
Accepts a string that describes the character set using the same syntax as regular expression character classes (ranges, escapes, etc.).
{
charIn: 'a-z';
} // [a-z]
{
charIn: 'a-zA-Z0-9';
} // [a-zA-Z0-9]
{
charIn: 'abc';
} // [abc]
4.3.2. charNotIn
- Type: Atom
- Equivalent:
[^ ...]
charNotIn — Matches any character not in the specified set (negated character class).
Accepts a string that describes the character set using the same syntax as regular expression character classes.
{
charNotIn: 'a-z';
} // [^a-z]
{
charNotIn: '0-9';
} // [^0-9]
4.4. Group references
4.4.1. referenceTo
- Type: Atom
- Equivalent:
\k<name>,\N
referenceTo — Inserts a backreference to a captured group.
Accepts:
- a number — numeric backreference by capture group index (
\N); - a string — named backreference (
\k<name>).
{
referenceTo: 1;
} // \1
{
referenceTo: 'name';
} // \k<name>
4.5. Unicode properties
4.5.1. unicodeProps
- Type: Atom
- Equivalent:
\p{...}
unicodeProps — Matches characters by Unicode property.
Accepts any Unicode property expression supported in \p{...} (for example, Letter, Number, Script=Latin, etc.).
{
unicodeProps: 'Letter';
} // \p{Letter}
{
unicodeProps: 'Script=Latin';
} // \p{Script=Latin}
4.6. Character classes
Character classes are atoms that take a boolean value.
Unlike anchors, where false omits the atom, for character classes false selects the negated class (for example, \D instead of \d, \W instead of \w).
4.6.1. digit
- Type: Atom
- Equivalent:
\d,\D
digit — Matches decimal digits (\d) or non-digits (\D).
true matches a decimal digit (0–9); false matches a non-digit.
{
digit: true;
} // \d
{
digit: false;
} // \D
4.6.2. word
- Type: Atom
- Equivalent:
\w,\W
word — Matches word characters (\w) or non-word characters (\W).
true matches Latin letters (A–Z, a–z), decimal digits (0–9), and underscore (_); false matches a non-word character.
{
word: true;
} // \w
{
word: false;
} // \W
4.6.3. whitespace
- Type: Atom
- Equivalent:
\s,\S
whitespace — Matches whitespace (\s) or non-whitespace (\S).
true matches whitespace; false matches a non-whitespace character.
{
whitespace: true;
} // \s
{
whitespace: false;
} // \S
4.6.4. boundary
- Type: Atom
- Equivalent:
\b,\B
boundary — Word boundary assertion.
true matches a word boundary; false matches a position that is not a word boundary (\B).
{
boundary: true;
} // \b
{
boundary: false;
} // \B