@@ -213,6 +213,13 @@ function isUnicodePropertyValueCharacter(cp: number): boolean {
213213 return isUnicodePropertyNameCharacter ( cp ) || isDecimalDigit ( cp )
214214}
215215
216+ export type RegExpValidatorSourceContext = {
217+ readonly source : string
218+ readonly start : number
219+ readonly end : number
220+ readonly kind : "flags" | "literal" | "pattern"
221+ }
222+
216223export namespace RegExpValidator {
217224 /**
218225 * The options for RegExpValidator construction.
@@ -628,6 +635,8 @@ export class RegExpValidator {
628635
629636 private _backreferenceNames = new Set < string > ( )
630637
638+ private _srcCtx : RegExpValidatorSourceContext | null = null
639+
631640 /**
632641 * Initialize this validator.
633642 * @param options The options of validator.
@@ -647,6 +656,7 @@ export class RegExpValidator {
647656 start = 0 ,
648657 end : number = source . length ,
649658 ) : void {
659+ this . _srcCtx = { source, start, end, kind : "literal" }
650660 this . _unicodeSetsMode = this . _unicodeMode = this . _nFlag = false
651661 this . reset ( source , start , end )
652662
@@ -655,8 +665,8 @@ export class RegExpValidator {
655665 const flagStart = this . index
656666 const unicode = source . includes ( "u" , flagStart )
657667 const unicodeSets = source . includes ( "v" , flagStart )
658- this . validateFlags ( source , flagStart , end )
659- this . validatePattern ( source , start + 1 , flagStart - 1 , {
668+ this . validateFlagsInternal ( source , flagStart , end )
669+ this . validatePatternInternal ( source , start + 1 , flagStart - 1 , {
660670 unicode,
661671 unicodeSets,
662672 } )
@@ -680,68 +690,8 @@ export class RegExpValidator {
680690 start = 0 ,
681691 end : number = source . length ,
682692 ) : void {
683- const existingFlags = new Set < number > ( )
684- let global = false
685- let ignoreCase = false
686- let multiline = false
687- let sticky = false
688- let unicode = false
689- let dotAll = false
690- let hasIndices = false
691- let unicodeSets = false
692- for ( let i = start ; i < end ; ++ i ) {
693- const flag = source . charCodeAt ( i )
694-
695- if ( existingFlags . has ( flag ) ) {
696- this . raise ( `Duplicated flag '${ source [ i ] } '` )
697- }
698- existingFlags . add ( flag )
699-
700- if ( flag === LATIN_SMALL_LETTER_G ) {
701- global = true
702- } else if ( flag === LATIN_SMALL_LETTER_I ) {
703- ignoreCase = true
704- } else if ( flag === LATIN_SMALL_LETTER_M ) {
705- multiline = true
706- } else if (
707- flag === LATIN_SMALL_LETTER_U &&
708- this . ecmaVersion >= 2015
709- ) {
710- unicode = true
711- } else if (
712- flag === LATIN_SMALL_LETTER_Y &&
713- this . ecmaVersion >= 2015
714- ) {
715- sticky = true
716- } else if (
717- flag === LATIN_SMALL_LETTER_S &&
718- this . ecmaVersion >= 2018
719- ) {
720- dotAll = true
721- } else if (
722- flag === LATIN_SMALL_LETTER_D &&
723- this . ecmaVersion >= 2022
724- ) {
725- hasIndices = true
726- } else if (
727- flag === LATIN_SMALL_LETTER_V &&
728- this . ecmaVersion >= 2024
729- ) {
730- unicodeSets = true
731- } else {
732- this . raise ( `Invalid flag '${ source [ i ] } '` )
733- }
734- }
735- this . onRegExpFlags ( start , end , {
736- global,
737- ignoreCase,
738- multiline,
739- unicode,
740- sticky,
741- dotAll,
742- hasIndices,
743- unicodeSets,
744- } )
693+ this . _srcCtx = { source, start, end, kind : "flags" }
694+ this . validateFlagsInternal ( source , start , end )
745695 }
746696
747697 /**
@@ -786,7 +736,23 @@ export class RegExpValidator {
786736 }
787737 | undefined = undefined ,
788738 ) : void {
789- const mode = this . _parseFlagsOptionToMode ( uFlagOrFlags , source , end )
739+ this . _srcCtx = { source, start, end, kind : "pattern" }
740+ this . validatePatternInternal ( source , start , end , uFlagOrFlags )
741+ }
742+
743+ private validatePatternInternal (
744+ source : string ,
745+ start = 0 ,
746+ end : number = source . length ,
747+ uFlagOrFlags :
748+ | boolean // The unicode flag (backward compatibility).
749+ | {
750+ unicode ?: boolean
751+ unicodeSets ?: boolean
752+ }
753+ | undefined = undefined ,
754+ ) : void {
755+ const mode = this . _parseFlagsOptionToMode ( uFlagOrFlags , end )
790756
791757 this . _unicodeMode = mode . unicodeMode
792758 this . _nFlag = mode . nFlag
@@ -805,6 +771,75 @@ export class RegExpValidator {
805771 }
806772 }
807773
774+ private validateFlagsInternal (
775+ source : string ,
776+ start : number ,
777+ end : number ,
778+ ) : void {
779+ const existingFlags = new Set < number > ( )
780+ let global = false
781+ let ignoreCase = false
782+ let multiline = false
783+ let sticky = false
784+ let unicode = false
785+ let dotAll = false
786+ let hasIndices = false
787+ let unicodeSets = false
788+ for ( let i = start ; i < end ; ++ i ) {
789+ const flag = source . charCodeAt ( i )
790+
791+ if ( existingFlags . has ( flag ) ) {
792+ this . raise ( `Duplicated flag '${ source [ i ] } '` , { index : start } )
793+ }
794+ existingFlags . add ( flag )
795+
796+ if ( flag === LATIN_SMALL_LETTER_G ) {
797+ global = true
798+ } else if ( flag === LATIN_SMALL_LETTER_I ) {
799+ ignoreCase = true
800+ } else if ( flag === LATIN_SMALL_LETTER_M ) {
801+ multiline = true
802+ } else if (
803+ flag === LATIN_SMALL_LETTER_U &&
804+ this . ecmaVersion >= 2015
805+ ) {
806+ unicode = true
807+ } else if (
808+ flag === LATIN_SMALL_LETTER_Y &&
809+ this . ecmaVersion >= 2015
810+ ) {
811+ sticky = true
812+ } else if (
813+ flag === LATIN_SMALL_LETTER_S &&
814+ this . ecmaVersion >= 2018
815+ ) {
816+ dotAll = true
817+ } else if (
818+ flag === LATIN_SMALL_LETTER_D &&
819+ this . ecmaVersion >= 2022
820+ ) {
821+ hasIndices = true
822+ } else if (
823+ flag === LATIN_SMALL_LETTER_V &&
824+ this . ecmaVersion >= 2024
825+ ) {
826+ unicodeSets = true
827+ } else {
828+ this . raise ( `Invalid flag '${ source [ i ] } '` , { index : start } )
829+ }
830+ }
831+ this . onRegExpFlags ( start , end , {
832+ global,
833+ ignoreCase,
834+ multiline,
835+ unicode,
836+ sticky,
837+ dotAll,
838+ hasIndices,
839+ unicodeSets,
840+ } )
841+ }
842+
808843 private _parseFlagsOptionToMode (
809844 uFlagOrFlags :
810845 | boolean // The unicode flag (backward compatibility).
@@ -813,7 +848,6 @@ export class RegExpValidator {
813848 unicodeSets ?: boolean
814849 }
815850 | undefined ,
816- source : string ,
817851 sourceEnd : number ,
818852 ) : {
819853 unicodeMode : boolean
@@ -837,12 +871,11 @@ export class RegExpValidator {
837871 if ( unicode && unicodeSets ) {
838872 // 1. If v is true and u is true, then
839873 // a. Let parseResult be a List containing one SyntaxError object.
840- throw new RegExpSyntaxError (
841- source ,
842- { unicode, unicodeSets } ,
843- sourceEnd + 1 /* `/` */ ,
844- "Invalid regular expression flags" ,
845- )
874+ this . raise ( "Invalid regular expression flags" , {
875+ index : sourceEnd + 1 /* `/` */ ,
876+ unicode,
877+ unicodeSets,
878+ } )
846879 }
847880
848881 const unicodeMode = unicode || unicodeSets
@@ -856,7 +889,6 @@ export class RegExpValidator {
856889
857890 return { unicodeMode, nFlag, unicodeSetsMode }
858891 }
859-
860892 // #region Delegate for Options
861893
862894 private get strict ( ) {
@@ -1164,10 +1196,6 @@ export class RegExpValidator {
11641196
11651197 // #region Delegate for Reader
11661198
1167- private get source ( ) : string {
1168- return this . _reader . source
1169- }
1170-
11711199 private get index ( ) : number {
11721200 return this . _reader . index
11731201 }
@@ -1214,14 +1242,19 @@ export class RegExpValidator {
12141242
12151243 // #endregion
12161244
1217- private raise ( message : string ) : never {
1245+ private raise (
1246+ message : string ,
1247+ context ?: { index ?: number ; unicode ?: boolean ; unicodeSets ?: boolean } ,
1248+ ) : never {
12181249 throw new RegExpSyntaxError (
1219- this . source ,
1250+ this . _srcCtx ! ,
12201251 {
1221- unicode : this . _unicodeMode && ! this . _unicodeSetsMode ,
1222- unicodeSets : this . _unicodeSetsMode ,
1252+ unicode :
1253+ context ?. unicode ??
1254+ ( this . _unicodeMode && ! this . _unicodeSetsMode ) ,
1255+ unicodeSets : context ?. unicodeSets ?? this . _unicodeSetsMode ,
12231256 } ,
1224- this . index ,
1257+ context ?. index ?? this . index ,
12251258 message ,
12261259 )
12271260 }
0 commit comments