11import {
2+ addRange ,
23 append ,
34 arrayIsEqualTo ,
45 binarySearch ,
@@ -12,7 +13,6 @@ import {
1213 Debug ,
1314 DiagnosticMessage ,
1415 Diagnostics ,
15- flatMap ,
1616 forEach ,
1717 getNameOfScriptTarget ,
1818 getSpellingSuggestion ,
@@ -32,7 +32,8 @@ import {
3232 positionIsSynthesized ,
3333 PunctuationOrKeywordSyntaxKind ,
3434 RegularExpressionAnyString ,
35- RegularExpressionDisjunctionScope ,
35+ RegularExpressionDisjunction ,
36+ RegularExpressionDisjunctionsScope ,
3637 RegularExpressionFlags ,
3738 RegularExpressionPattern ,
3839 RegularExpressionPatternContent ,
@@ -2664,12 +2665,9 @@ export function createScanner(
26642665 var groupNameReferences : ( TextRange & { name : string ; } ) [ ] | undefined ;
26652666 /** All numeric backreferences within the regex. */
26662667 var decimalEscapes : ( TextRange & { value : number ; } ) [ ] | undefined ;
2667- /** A stack of scopes for disjunction, including capturing groups, non-capturing groups, lookaheads and lookbehinds. */
2668- var disjunctionsScopeStack : ( RegularExpressionDisjunctionScope | undefined ) [ ] = [ ] ;
2669- var topDisjunctionsScope : RegularExpressionDisjunctionScope | undefined ;
2670- /** A stack of scopes for named capturing groups. @see {scanGroupName} */
2671- var namedCapturingGroupsScopeStack : ( Set < string > | undefined ) [ ] = [ ] ;
2672- var topNamedCapturingGroupsScope : Set < string > | undefined ;
2668+ /** A stack of scopes for disjunctions, including capturing groups, non-capturing groups, lookaheads and lookbehinds. */
2669+ var disjunctionsScopesStack : RegularExpressionDisjunctionsScope [ ] = [ ] ;
2670+ var topDisjunctionsScope ! : RegularExpressionDisjunctionsScope ;
26732671 /* eslint-enable no-var */
26742672
26752673 regExpCapturingGroups = [ ] ;
@@ -2691,21 +2689,17 @@ export function createScanner(
26912689 // Disjunction ::= Alternative ('|' Alternative)*
26922690 function scanDisjunction ( isInGroup : boolean ) : RegularExpressionPatternUnion {
26932691 const patternUnion = new Set ( ) as RegularExpressionPatternUnion ;
2694- disjunctionsScopeStack . push ( topDisjunctionsScope ) ;
2695- topDisjunctionsScope = undefined ;
2696- namedCapturingGroupsScopeStack . push ( topNamedCapturingGroupsScope ) ;
2697- topNamedCapturingGroupsScope = undefined ;
2692+ ( topDisjunctionsScope = [ ] as unknown as RegularExpressionDisjunctionsScope ) . currentAlternativeIndex = 0 ;
26982693 while ( true ) {
26992694 patternUnion . add ( scanAlternative ( isInGroup ) ) ;
27002695 if ( charCodeChecked ( pos ) !== CharacterCodes . bar ) {
27012696 if ( patternUnion . size > 1 ) {
27022697 markAllInnerPatternUnionsAsPossiblyUndefined ( patternUnion ) ;
27032698 }
2704- topDisjunctionsScope = disjunctionsScopeStack . pop ( ) ;
2705- topNamedCapturingGroupsScope = namedCapturingGroupsScopeStack . pop ( ) ;
27062699 return patternUnion ;
27072700 }
27082701 pos ++ ;
2702+ topDisjunctionsScope . currentAlternativeIndex = topDisjunctionsScope . length ;
27092703 }
27102704 }
27112705
@@ -2827,25 +2821,34 @@ export function createScanner(
28272821 groupNumber = ++ numberOfCapturingGroups ;
28282822 isPreviousTermQuantifiable = true ;
28292823 }
2830- const patternUnion = scanDisjunction ( /*isInGroup*/ true ) ;
2824+
2825+ const disjunction : RegularExpressionDisjunction = { groupNumber, groupName } ;
2826+ topDisjunctionsScope . push ( disjunction ) ;
2827+ disjunctionsScopesStack . push ( topDisjunctionsScope ) ;
2828+ const patternUnion = disjunction . patternUnion = scanDisjunction ( /*isInGroup*/ true ) ;
2829+ if ( isNegativeAssertion ) {
2830+ // Mark all capturing groups in the negative lookahead/lookbehind just closed as possibly undefined
2831+ markAllInnerPatternUnionsAsPossiblyUndefined ( patternUnion ) ;
2832+ // Also flag them such that they won't be matched by backreferences
2833+ for ( const disjunction of topDisjunctionsScope ) {
2834+ disjunction . isInNegativeAssertion = true ;
2835+ }
2836+ }
2837+ const currentTopDisjunctionsScope = topDisjunctionsScope ;
2838+ topDisjunctionsScope = disjunctionsScopesStack . pop ( ) ! ;
2839+ addRange ( topDisjunctionsScope , currentTopDisjunctionsScope ) ;
2840+
28312841 isCaseInsensitive = prevIsCaseInsensitive ;
28322842 if ( isPreviousTermQuantifiable ) {
28332843 // not an assertion
28342844 pattern . push ( patternUnion ) ;
28352845 if ( groupNumber ) {
28362846 regExpCapturingGroups [ groupNumber ] = patternUnion ;
2837- ( ( topDisjunctionsScope ??= { } ) . groups ??= [ ] ) [ groupNumber ] = patternUnion ;
28382847 if ( groupName ) {
28392848 ( regExpCapturingGroupSpecifiers ??= createMultiMap ( ) ) . add ( groupName , patternUnion ) ;
2840- ( topDisjunctionsScope . groupSpecifiers ??= createMultiMap ( ) ) . add ( groupName , patternUnion ) ;
28412849 }
28422850 }
28432851 }
2844- else if ( isNegativeAssertion ) {
2845- // Invalidate all capturing groups in the negative lookahead/lookbehind just closed
2846- // such that they won't be matched by backreferences
2847- topDisjunctionsScope = undefined ;
2848- }
28492852 scanExpectedChar ( CharacterCodes . closeParen ) ;
28502853 break ;
28512854 case CharacterCodes . openBrace :
@@ -3029,16 +3032,24 @@ export function createScanner(
30293032 return currFlags ;
30303033 }
30313034
3032- function getBackreferencePatternUnion ( selector : ( disjunctionScope : RegularExpressionDisjunctionScope | undefined ) => RegularExpressionPatternUnion | RegularExpressionPatternUnion [ ] | undefined ) : RegularExpressionPatternContent {
3033- disjunctionsScopeStack . push ( topDisjunctionsScope ) ;
3034- const capturingGroups = flatMap ( disjunctionsScopeStack , selector ) ;
3035- disjunctionsScopeStack . pop ( ) ;
3036- if ( ! capturingGroups . length ) return "" ;
3037- const patternUnion = new Set ( capturingGroups as RegularExpressionPattern ) as RegularExpressionPatternUnion ;
3038- if ( some ( capturingGroups , patternUnion => patternUnion . isPossiblyUndefined ! ) ) {
3039- patternUnion . add ( "" ) ;
3035+ function getBackreferencePatternUnion ( predicate : ( disjunction : RegularExpressionDisjunction ) => boolean ) : RegularExpressionPatternContent {
3036+ disjunctionsScopesStack . push ( topDisjunctionsScope ) ;
3037+ let patternUnion : RegularExpressionPatternUnion | undefined ;
3038+ for ( const disjunctionsScope of disjunctionsScopesStack ) {
3039+ for ( let i = disjunctionsScope . currentAlternativeIndex ; i < disjunctionsScope . length ; i ++ ) {
3040+ const disjunction = disjunctionsScope [ i ] ;
3041+ if ( disjunction . patternUnion && ! disjunction . isInNegativeAssertion && predicate ( disjunction ) ) {
3042+ for ( const pattern of disjunction . patternUnion ) {
3043+ ( patternUnion ??= new Set ( ) as RegularExpressionPatternUnion ) . add ( pattern ) ;
3044+ }
3045+ if ( disjunction . patternUnion . isPossiblyUndefined ) {
3046+ ( patternUnion ??= new Set ( ) as RegularExpressionPatternUnion ) . add ( "" ) ;
3047+ }
3048+ }
3049+ }
30403050 }
3041- return patternUnion ;
3051+ disjunctionsScopesStack . pop ( ) ;
3052+ return patternUnion || "" ;
30423053 }
30433054
30443055 // AtomEscape ::=
@@ -3055,7 +3066,7 @@ export function createScanner(
30553066 pos ++ ;
30563067 const groupName = scanGroupName ( /*isReference*/ true ) ;
30573068 scanExpectedChar ( CharacterCodes . greaterThan ) ;
3058- return groupName ? getBackreferencePatternUnion ( disjunctionsScope => disjunctionsScope ?. groupSpecifiers ?. get ( groupName ) ) : "" ;
3069+ return groupName ? getBackreferencePatternUnion ( disjunction => disjunction . groupName === groupName ) : "" ;
30593070 }
30603071 error ( Diagnostics . k_must_be_followed_by_a_capturing_group_name_enclosed_in_angle_brackets , pos - 2 , 2 ) ;
30613072 return getCharacterEquivalents ( String . fromCharCode ( ch ) ) ;
@@ -3077,7 +3088,7 @@ export function createScanner(
30773088 scanDigits ( ) ;
30783089 const groupNumber = + tokenValue ;
30793090 decimalEscapes = append ( decimalEscapes , { pos : start , end : pos , value : groupNumber } ) ;
3080- return getBackreferencePatternUnion ( disjunctionsScope => disjunctionsScope ?. groups ?. [ groupNumber ] ) ;
3091+ return getBackreferencePatternUnion ( disjunction => disjunction . groupNumber === groupNumber ) ;
30813092 }
30823093 }
30833094
@@ -3149,19 +3160,26 @@ export function createScanner(
31493160 scanIdentifier ( codePointChecked ( pos ) , languageVersion ) ;
31503161 if ( pos === tokenStart ) {
31513162 error ( Diagnostics . Expected_a_capturing_group_name ) ;
3163+ return ;
31523164 }
3153- else if ( isReference ) {
3165+ if ( isReference ) {
31543166 groupNameReferences = append ( groupNameReferences , { pos : tokenStart , end : pos , name : tokenValue } ) ;
3155- return tokenValue ;
3156- }
3157- else if ( topNamedCapturingGroupsScope ?. has ( tokenValue ) || namedCapturingGroupsScopeStack . some ( group => group ?. has ( tokenValue ) ) ) {
3158- error ( Diagnostics . Named_capturing_groups_with_the_same_name_must_be_mutually_exclusive_to_each_other , tokenStart , pos - tokenStart ) ;
31593167 }
31603168 else {
3161- topNamedCapturingGroupsScope ??= new Set ( ) ;
3162- topNamedCapturingGroupsScope . add ( tokenValue ) ;
3163- return tokenValue ;
3169+ disjunctionsScopesStack . push ( topDisjunctionsScope ) ;
3170+ if (
3171+ some ( disjunctionsScopesStack , disjunctionsScope => {
3172+ for ( let i = disjunctionsScope . currentAlternativeIndex ; i < disjunctionsScope . length ; i ++ ) {
3173+ if ( disjunctionsScope [ i ] . groupName === tokenValue ) return true ;
3174+ }
3175+ return false ;
3176+ } )
3177+ ) {
3178+ error ( Diagnostics . Named_capturing_groups_with_the_same_name_must_be_mutually_exclusive_to_each_other , tokenStart , pos - tokenStart ) ;
3179+ }
3180+ disjunctionsScopesStack . pop ( ) ;
31643181 }
3182+ return tokenValue ;
31653183 }
31663184
31673185 function isClassContentExit ( ch : number ) {
0 commit comments