Skip to content

Commit d7f0d66

Browse files
committed
refactor to be more consistent with acorn impl
1 parent 06d7dff commit d7f0d66

File tree

2 files changed

+69
-51
lines changed

2 files changed

+69
-51
lines changed

src/group-specifiers.ts

+68-49
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ export interface GroupSpecifiers {
1616
* Called when visiting the Alternative.
1717
* For ES2025, manage nesting with new Alternative scopes.
1818
*/
19-
enterAlternative: () => void
20-
/**
21-
* Called when leaving the Alternative.
22-
*/
23-
leaveAlternative: () => void
19+
enterAlternative: (index: number) => void
2420
/**
2521
* Called when leaving the Disjunction.
2622
*/
@@ -40,7 +36,7 @@ export interface GroupSpecifiers {
4036
}
4137

4238
export class GroupSpecifiersAsES2018 implements GroupSpecifiers {
43-
private groupName = new Set<string>()
39+
private readonly groupName = new Set<string>()
4440

4541
public clear(): void {
4642
this.groupName.clear()
@@ -72,77 +68,100 @@ export class GroupSpecifiersAsES2018 implements GroupSpecifiers {
7268
// Prior to ES2025, it does not manage alternative scopes.
7369
}
7470

75-
// eslint-disable-next-line class-methods-use-this
76-
public leaveAlternative(): void {
77-
// Prior to ES2025, it does not manage alternative scopes.
78-
}
79-
8071
// eslint-disable-next-line class-methods-use-this
8172
public leaveDisjunction(): void {
8273
// Prior to ES2025, it does not manage disjunction scopes.
8374
}
8475
}
8576

86-
export class GroupSpecifiersAsES2025 implements GroupSpecifiers {
87-
private groupNamesAddedInDisjunction = new Set<string>()
88-
private groupNamesAddedInUpperDisjunctionStack: Set<string>[] = []
89-
private groupNamesInScope = new Set<string>()
90-
private groupNamesInUpperScopeStack: Set<string>[] = []
77+
/**
78+
* Track disjunction structure to determine whether a duplicate
79+
* capture group name is allowed because it is in a separate branch.
80+
*/
81+
class BranchID {
82+
public readonly parent: BranchID | null
83+
private readonly base: BranchID
84+
public constructor(parent: BranchID | null, base: BranchID | null) {
85+
// Parent disjunction branch
86+
this.parent = parent
87+
// Identifies this set of sibling branches
88+
this.base = base ?? this
89+
}
90+
91+
/**
92+
* A branch is separate from another branch if they or any of
93+
* their parents are siblings in a given disjunction
94+
*/
95+
public separatedFrom(other: BranchID): boolean {
96+
if (this.base === other.base && this !== other) {
97+
return true
98+
}
99+
if (other.parent && this.separatedFrom(other.parent)) {
100+
return true
101+
}
102+
return this.parent?.separatedFrom(other) ?? false
103+
}
104+
105+
public child() {
106+
return new BranchID(this, null)
107+
}
108+
109+
public sibling() {
110+
return new BranchID(this.parent, this.base)
111+
}
112+
}
91113

92-
private groupNamesInPattern = new Set<string>()
114+
export class GroupSpecifiersAsES2025 implements GroupSpecifiers {
115+
private branchID = new BranchID(null, null)
116+
private readonly groupNames = new Map<string, BranchID[]>()
93117

94118
public clear(): void {
95-
this.groupNamesAddedInDisjunction.clear()
96-
this.groupNamesAddedInUpperDisjunctionStack.length = 0
97-
this.groupNamesInScope.clear()
98-
this.groupNamesInUpperScopeStack.length = 0
99-
this.groupNamesInPattern.clear()
119+
this.branchID = new BranchID(null, null)
120+
this.groupNames.clear()
100121
}
101122

102123
public isEmpty(): boolean {
103-
return !this.groupNamesInPattern.size
124+
return !this.groupNames.size
104125
}
105126

106127
public enterDisjunction(): void {
107-
this.groupNamesAddedInUpperDisjunctionStack.push(
108-
this.groupNamesAddedInDisjunction,
109-
)
110-
// Clear groupNamesAddedInDisjunction to store the groupName added in this Disjunction.
111-
this.groupNamesAddedInDisjunction = new Set()
128+
this.branchID = this.branchID.child()
112129
}
113130

114-
public enterAlternative(): void {
115-
this.groupNamesInUpperScopeStack.push(this.groupNamesInScope)
116-
this.groupNamesInScope = new Set(this.groupNamesInScope)
117-
}
118-
119-
public leaveAlternative(): void {
120-
this.groupNamesInScope = this.groupNamesInUpperScopeStack.pop()!
131+
public enterAlternative(index: number): void {
132+
if (index === 0) {
133+
return
134+
}
135+
this.branchID = this.branchID.sibling()
121136
}
122137

123138
public leaveDisjunction(): void {
124-
const groupNamesAddedInDisjunction = this.groupNamesAddedInDisjunction
125-
this.groupNamesAddedInDisjunction =
126-
this.groupNamesAddedInUpperDisjunctionStack.pop()!
127-
for (const groupName of groupNamesAddedInDisjunction) {
128-
// Adds the groupName added in Disjunction to groupNamesInScope.
129-
this.groupNamesInScope.add(groupName)
130-
// Adds the groupName added in Disjunction to the upper Disjunction.
131-
this.groupNamesAddedInDisjunction.add(groupName)
132-
}
139+
this.branchID = this.branchID.parent!
133140
}
134141

135142
public hasInPattern(name: string): boolean {
136-
return this.groupNamesInPattern.has(name)
143+
return this.groupNames.has(name)
137144
}
138145

139146
public hasInScope(name: string): boolean {
140-
return this.groupNamesInScope.has(name)
147+
const branches = this.groupNames.get(name)
148+
if (!branches) {
149+
return false
150+
}
151+
for (const branch of branches) {
152+
if (!branch.separatedFrom(this.branchID)) {
153+
return true
154+
}
155+
}
156+
return false
141157
}
142158

143159
public addToScope(name: string): void {
144-
this.groupNamesInScope.add(name)
145-
this.groupNamesAddedInDisjunction.add(name)
146-
this.groupNamesInPattern.add(name)
160+
const branches = this.groupNames.get(name)
161+
if (branches) {
162+
branches.push(this.branchID)
163+
return
164+
}
165+
this.groupNames.set(name, [this.branchID])
147166
}
148167
}

src/validator.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1415,13 +1415,12 @@ export class RegExpValidator {
14151415
private consumeAlternative(i: number): void {
14161416
const start = this.index
14171417

1418-
this._groupSpecifiers.enterAlternative()
1418+
this._groupSpecifiers.enterAlternative(i)
14191419
this.onAlternativeEnter(start, i)
14201420
while (this.currentCodePoint !== -1 && this.consumeTerm()) {
14211421
// do nothing.
14221422
}
14231423
this.onAlternativeLeave(start, this.index, i)
1424-
this._groupSpecifiers.leaveAlternative()
14251424
}
14261425

14271426
/**

0 commit comments

Comments
 (0)