@@ -283,15 +283,97 @@ class RegularExpressionParserSuite extends AnyFunSuite {
283283 RegexChar ('$' ))))
284284 }
285285
286- test(" replacement: numeric braced backref rejected (Java spec)" ) {
287- val brace = " $" + " {"
288- val cases = Seq (s " [ ${brace}2}] " , s " ${brace}1} " , s " ${brace}12} " ,
289- s " a ${brace}3}b " , s " ${brace}0} " )
290- for (rep <- cases) {
286+ test(" issue-14742-subbug1: \\ N in replacement is the literal character N, not a backref" ) {
287+ val repl = new RegexParser (" \\ 1" ).parseReplacement(numCaptureGroups = 1 )
288+ assert(repl.parts.toList === List (RegexChar ('\\ ' ), RegexChar ('1' )))
289+ }
290+
291+ test(" issue-14742-subbug1: \\ a in replacement is the literal character a" ) {
292+ val repl = new RegexParser (" \\ a" ).parseReplacement(numCaptureGroups = 0 )
293+ assert(repl.parts.toList === List (RegexChar ('\\ ' ), RegexChar ('a' )))
294+ }
295+
296+ test(" issue-14742-subbug2: trailing \\ in replacement throws" ) {
297+ val ex = intercept[RegexUnsupportedException ] {
298+ new RegexParser (" \\ " ).parseReplacement(numCaptureGroups = 0 )
299+ }
300+ assert(ex.getMessage.contains(" character to be escaped is missing" ))
301+ }
302+
303+ test(" issue-14742-subbug3: bare $X for non-digit X throws" ) {
304+ val ex = intercept[RegexUnsupportedException ] {
305+ new RegexParser (" $x" ).parseReplacement(numCaptureGroups = 0 )
306+ }
307+ assert(ex.getMessage.contains(" Illegal group reference" ))
308+ }
309+
310+ test(" issue-14742-subbug3: trailing bare $ throws" ) {
311+ val ex = intercept[RegexUnsupportedException ] {
312+ new RegexParser (" $" ).parseReplacement(numCaptureGroups = 0 )
313+ }
314+ assert(ex.getMessage.contains(" Illegal group reference" ))
315+ }
316+
317+ test(" issue-14742-subbug4: dollar-brace-digit-brace throws" ) {
318+ val ex = intercept[RegexUnsupportedException ] {
319+ new RegexParser (" $" + " {1}" ).parseReplacement(numCaptureGroups = 1 )
320+ }
321+ assert(ex.getMessage.contains(" Illegal group reference" ))
322+ assert(ex.getMessage.contains(" digit" ))
323+ }
324+
325+ test(" issue-14742-subbug5: dollar-brace-name-brace for named group is not supported on GPU" ) {
326+ val ex = intercept[RegexUnsupportedException ] {
327+ new RegexParser (" $" + " {name}" ).parseReplacement(numCaptureGroups = 1 )
328+ }
329+ assert(ex.getMessage.contains(" named-group reference" ))
330+ }
331+
332+ test(" issue-14742-subbug5: dollar-brace-name with missing closing brace throws" ) {
333+ val ex = intercept[RegexUnsupportedException ] {
334+ new RegexParser (" $" + " {name" ).parseReplacement(numCaptureGroups = 0 )
335+ }
336+ assert(ex.getMessage.contains(" Illegal group reference" ))
337+ }
338+
339+ test(" issue-14742: dollar-brace with empty body throws" ) {
340+ val ex = intercept[RegexUnsupportedException ] {
341+ new RegexParser (" $" + " {}" ).parseReplacement(numCaptureGroups = 0 )
342+ }
343+ assert(ex.getMessage.contains(" Illegal group reference" ))
344+ }
345+
346+ test(" issue-14742: numbered backref $0 still works" ) {
347+ val repl = new RegexParser (" $0" ).parseReplacement(numCaptureGroups = 0 )
348+ assert(repl.parts.toList === List (RegexBackref (0 )))
349+ }
350+
351+ test(" issue-14742: numbered backref $1 still works" ) {
352+ val repl = new RegexParser (" $1" ).parseReplacement(numCaptureGroups = 1 )
353+ assert(repl.parts.toList === List (RegexBackref (1 )))
354+ }
355+
356+ test(" issue-14742: numbered backref $12 still consumes max digits" ) {
357+ val repl = new RegexParser (" $12" ).parseReplacement(numCaptureGroups = 12 )
358+ assert(repl.parts.toList === List (RegexBackref (12 )))
359+ }
360+
361+ test(" issue-14742: escaped metachar \\ $ in replacement keeps the \\ pair" ) {
362+ val repl = new RegexParser (" \\ $" ).parseReplacement(numCaptureGroups = 0 )
363+ assert(repl.parts.toList === List (RegexChar ('\\ ' ), RegexChar ('$' )))
364+ }
365+
366+ test(" issue-14742: escaped backslash \\\\ in replacement keeps the \\ pair" ) {
367+ val repl = new RegexParser (" \\\\ " ).parseReplacement(numCaptureGroups = 0 )
368+ assert(repl.parts.toList === List (RegexChar ('\\ ' ), RegexChar ('\\ ' )))
369+ }
370+
371+ test(" issue-14742: non-ASCII Unicode digit after `$` triggers GPU fallback" ) {
372+ for (rep <- Seq (" $٢" , " $१" , " $۱" )) {
291373 val e = intercept[RegexUnsupportedException ] {
292- new RegexParser (rep).parseReplacement(4 )
374+ new RegexParser (rep).parseReplacement(numCaptureGroups = 4 )
293375 }
294- assert(e.getMessage.contains( " backref in replacement string is not supported " ),
376+ assert(e.getMessage.startsWith( " Illegal group reference " ),
295377 s " unexpected message for replacement ' $rep': ${e.getMessage}" )
296378 }
297379 }
0 commit comments