Skip to content

Commit 347b151

Browse files
authored
feat: support for modifier (#83)
1 parent 25af7a7 commit 347b151

26 files changed

+19366
-723
lines changed

scripts/update-fixtures.ts

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ for (const filename of Object.keys(Visitor.fixturesData)) {
5353
onExpressionCharacterClassEnter: enter,
5454
onFlagsEnter: enter,
5555
onGroupEnter: enter,
56+
onModifierFlagsEnter: enter,
57+
onModifiersEnter: enter,
5658
onPatternEnter: enter,
5759
onQuantifierEnter: enter,
5860
onRegExpLiteralEnter: enter,
@@ -71,6 +73,8 @@ for (const filename of Object.keys(Visitor.fixturesData)) {
7173
onExpressionCharacterClassLeave: leave,
7274
onFlagsLeave: leave,
7375
onGroupLeave: leave,
76+
onModifierFlagsLeave: leave,
77+
onModifiersLeave: leave,
7478
onPatternLeave: leave,
7579
onQuantifierLeave: leave,
7680
onRegExpLiteralLeave: leave,

src/ast.ts

+33
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type BranchNode =
1717
| ExpressionCharacterClass
1818
| Group
1919
| LookaroundAssertion
20+
| Modifiers
2021
| Pattern
2122
| Quantifier
2223
| RegExpLiteral
@@ -31,6 +32,7 @@ export type LeafNode =
3132
| Character
3233
| CharacterSet
3334
| Flags
35+
| ModifierFlags
3436

3537
/**
3638
* The type which includes all atom nodes.
@@ -122,6 +124,7 @@ export interface Alternative extends NodeBase {
122124
export interface Group extends NodeBase {
123125
type: "Group"
124126
parent: Alternative | Quantifier
127+
modifiers: Modifiers | null
125128
alternatives: Alternative[]
126129
}
127130

@@ -446,6 +449,36 @@ export interface UnambiguousBackreference extends BaseBackreference {
446449
resolved: CapturingGroup
447450
}
448451

452+
/**
453+
* The modifiers.
454+
*/
455+
export interface Modifiers extends NodeBase {
456+
type: "Modifiers"
457+
parent: Group
458+
/**
459+
* The add modifier flags.
460+
*/
461+
add: ModifierFlags
462+
/**
463+
* The remove modifier flags.
464+
*
465+
* `null` means no remove modifier flags. e.g. `(?ims:x)`
466+
* The reason for `null` is that there is no position where the remove modifier flags appears. Must be behind the minus mark.
467+
*/
468+
remove: ModifierFlags | null
469+
}
470+
471+
/**
472+
* The modifier flags.
473+
*/
474+
export interface ModifierFlags extends NodeBase {
475+
type: "ModifierFlags"
476+
parent: Modifiers
477+
dotAll: boolean
478+
ignoreCase: boolean
479+
multiline: boolean
480+
}
481+
449482
/**
450483
* The flags.
451484
*/

src/parser.ts

+86-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
UnicodeSetsCharacterClass,
1919
ExpressionCharacterClass,
2020
StringAlternative,
21+
Modifiers,
2122
} from "./ast"
2223
import type { EcmaVersion } from "./ecma-versions"
2324
import { latestEcmaVersion } from "./ecma-versions"
@@ -31,6 +32,7 @@ type AppendableNode =
3132
| ClassStringDisjunction
3233
| Group
3334
| LookaroundAssertion
35+
| Modifiers
3436
| Pattern
3537
| StringAlternative
3638

@@ -205,14 +207,17 @@ class RegExpParserState {
205207
throw new Error("UnknownError")
206208
}
207209

208-
this._node = {
210+
const group: Group = {
209211
type: "Group",
210212
parent,
211213
start,
212214
end: start,
213215
raw: "",
216+
modifiers: null,
214217
alternatives: [],
215218
}
219+
220+
this._node = group
216221
parent.elements.push(this._node)
217222
}
218223

@@ -227,6 +232,85 @@ class RegExpParserState {
227232
this._node = node.parent
228233
}
229234

235+
public onModifiersEnter(start: number): void {
236+
const parent = this._node
237+
if (parent.type !== "Group") {
238+
throw new Error("UnknownError")
239+
}
240+
241+
this._node = {
242+
type: "Modifiers",
243+
parent,
244+
start,
245+
end: start,
246+
raw: "",
247+
add: null as never, // Set in onAddModifiers.
248+
remove: null,
249+
}
250+
parent.modifiers = this._node
251+
}
252+
253+
public onModifiersLeave(start: number, end: number): void {
254+
const node = this._node
255+
if (node.type !== "Modifiers" || node.parent.type !== "Group") {
256+
throw new Error("UnknownError")
257+
}
258+
259+
node.end = end
260+
node.raw = this.source.slice(start, end)
261+
this._node = node.parent
262+
}
263+
264+
public onAddModifiers(
265+
start: number,
266+
end: number,
267+
{
268+
ignoreCase,
269+
multiline,
270+
dotAll,
271+
}: { ignoreCase: boolean; multiline: boolean; dotAll: boolean },
272+
): void {
273+
const parent = this._node
274+
if (parent.type !== "Modifiers") {
275+
throw new Error("UnknownError")
276+
}
277+
parent.add = {
278+
type: "ModifierFlags",
279+
parent,
280+
start,
281+
end,
282+
raw: this.source.slice(start, end),
283+
ignoreCase,
284+
multiline,
285+
dotAll,
286+
}
287+
}
288+
289+
public onRemoveModifiers(
290+
start: number,
291+
end: number,
292+
{
293+
ignoreCase,
294+
multiline,
295+
dotAll,
296+
}: { ignoreCase: boolean; multiline: boolean; dotAll: boolean },
297+
): void {
298+
const parent = this._node
299+
if (parent.type !== "Modifiers") {
300+
throw new Error("UnknownError")
301+
}
302+
parent.remove = {
303+
type: "ModifierFlags",
304+
parent,
305+
start,
306+
end,
307+
raw: this.source.slice(start, end),
308+
ignoreCase,
309+
multiline,
310+
dotAll,
311+
}
312+
}
313+
230314
public onCapturingGroupEnter(start: number, name: string | null): void {
231315
const parent = this._node
232316
if (parent.type !== "Alternative") {
@@ -765,7 +849,7 @@ export namespace RegExpParser {
765849
* - `2022` added `d` flag.
766850
* - `2023` added more valid Unicode Property Escapes.
767851
* - `2024` added `v` flag.
768-
* - `2025` added duplicate named capturing groups.
852+
* - `2025` added duplicate named capturing groups, modifiers.
769853
*/
770854
ecmaVersion?: EcmaVersion
771855
}

0 commit comments

Comments
 (0)