11import assert from "assert"
2- import { parseRegExpLiteral , RegExpParser } from "../src/index"
2+ import { parseRegExpLiteral , RegExpParser , RegExpValidator } from "../src/index"
33import type { RegExpSyntaxError } from "../src/regexp-syntax-error"
44import { cloneWithoutCircular } from "../scripts/clone-without-circular"
55import { fixturesData } from "./fixtures/parser/literal"
6+ import {
7+ ASTERISK ,
8+ isLineTerminator ,
9+ LEFT_SQUARE_BRACKET ,
10+ REVERSE_SOLIDUS ,
11+ RIGHT_SQUARE_BRACKET ,
12+ SOLIDUS ,
13+ } from "../src/unicode"
614
715function generateAST ( source : string , options : RegExpParser . Options ) : object {
816 return cloneWithoutCircular ( parseRegExpLiteral ( source , options ) )
@@ -56,6 +64,45 @@ describe("parseRegExpLiteral function:", () => {
5664 }
5765 assert . fail ( "Should fail, but succeeded." )
5866 } )
67+
68+ const validator = new RegExpValidator ( options )
69+ const extracted = extractPatternAndFlags ( source , validator )
70+ if ( extracted ) {
71+ it ( `${ source } should throw syntax error with RegExpValidator#validatePattern.` , ( ) => {
72+ const expected = result . error
73+ try {
74+ validator . validatePattern (
75+ extracted . pattern ,
76+ undefined ,
77+ undefined ,
78+ {
79+ unicode : extracted . flags . includes ( "u" ) ,
80+ unicodeSets :
81+ extracted . flags . includes ( "v" ) ,
82+ } ,
83+ )
84+ } catch ( err ) {
85+ const error = err as RegExpSyntaxError
86+ const expectedMessage =
87+ expected . message . replace (
88+ / \/ ( [ a - z ] + ?) : / u,
89+ ( _ , flagsInLiteral : string ) =>
90+ `/${ flagsInLiteral . replace (
91+ / [ ^ u v ] / gu,
92+ "" ,
93+ ) } :`,
94+ )
95+ const expectedIndex = expected . index - 1
96+ assert . strictEqual (
97+ error . message ,
98+ expectedMessage ,
99+ )
100+ assert . strictEqual ( error . index , expectedIndex )
101+ return
102+ }
103+ assert . fail ( "Should fail, but succeeded." )
104+ } )
105+ }
59106 }
60107 }
61108 } )
@@ -79,3 +126,61 @@ describe("RegExpParser:", () => {
79126 } )
80127 } )
81128} )
129+
130+ function extractPatternAndFlags (
131+ source : string ,
132+ validator : RegExpValidator ,
133+ ) : { pattern : string ; flags : string } | null {
134+ let inClass = false
135+ let escaped = false
136+
137+ const chars = [ ...source ]
138+
139+ if ( chars [ 0 ] !== "/" ) {
140+ return null
141+ }
142+ chars . shift ( )
143+
144+ const pattern : string [ ] = [ ]
145+
146+ let first = true
147+ // https://tc39.es/ecma262/2022/multipage/ecmascript-language-lexical-grammar.html#prod-RegularExpressionBody
148+ for ( ; ; ) {
149+ const char = chars . shift ( )
150+ if ( ! char ) {
151+ return null
152+ }
153+ const cp = char . charCodeAt ( 0 ) !
154+ if ( isLineTerminator ( cp ) ) {
155+ return null
156+ }
157+ if ( escaped ) {
158+ escaped = false
159+ } else if ( cp === REVERSE_SOLIDUS ) {
160+ escaped = true
161+ } else if ( cp === LEFT_SQUARE_BRACKET ) {
162+ inClass = true
163+ } else if ( cp === RIGHT_SQUARE_BRACKET ) {
164+ inClass = false
165+ } else if ( cp === ASTERISK && first ) {
166+ return null
167+ } else if ( cp === SOLIDUS && ! inClass ) {
168+ break
169+ }
170+ pattern . push ( char )
171+ first = false
172+ }
173+
174+ const flags = chars . join ( "" )
175+ if ( pattern . length === 0 ) {
176+ return null
177+ }
178+
179+ try {
180+ validator . validateFlags ( flags )
181+ } catch {
182+ return null
183+ }
184+
185+ return { pattern : pattern . join ( "" ) , flags }
186+ }
0 commit comments