@@ -3,6 +3,7 @@ package org.evomaster.core.parser
33import org.antlr.v4.runtime.*
44import org.antlr.v4.runtime.misc.ParseCancellationException
55import org.evomaster.core.search.gene.regex.RegexGene
6+ import org.evomaster.core.utils.ParsedFlagExpression
67import org.evomaster.core.utils.RegexFlags
78import org.evomaster.core.utils.RegexWithExternalFlags
89
@@ -33,7 +34,9 @@ object RegexHandler {
3334 return cacheJVM[key]!! .copy() as RegexGene
3435 }
3536
36- val stream = CharStreams .fromString(regex)
37+ val preprocessedRegex = preprocessCommentsForJavaRegex(regex, externalRegexFlags)
38+
39+ val stream = CharStreams .fromString(preprocessedRegex)
3740 val lexer = RegexJavaLexer (stream)
3841 val tokenStream = prepareLexer(lexer)
3942 val parser = RegexJavaParser (tokenStream)
@@ -48,6 +51,107 @@ object RegexHandler {
4851 return gene
4952 }
5053
54+ /* *
55+ * This function handles comments and whitespace for Java regex, striping them when the "x" flag is on.
56+ */
57+ private fun preprocessCommentsForJavaRegex (regex : String , externalRegexFlags : RegexFlags ): String {
58+ val result = StringBuilder (regex.length)
59+ val scopeStack = ArrayDeque <RegexFlags >() // stack of flags per level
60+ var currentFlags = externalRegexFlags
61+ var i = 0
62+
63+ while (i < regex.length) {
64+ val c = regex[i]
65+ when {
66+ // backslash escape
67+ c == ' \\ ' && i + 1 < regex.length -> {
68+ when {
69+ regex[i+ 1 ] == ' Q' -> {
70+ // \Q...\E quote block, copy everything
71+ result.append(' \\ ' ); result.append(' Q' )
72+ i + = 2
73+ while (i < regex.length) {
74+ if (regex[i] == ' \\ ' && i+ 1 < regex.length && regex[i+ 1 ] == ' E' ) {
75+ result.append(' \\ ' ); result.append(' E' )
76+ i + = 2 ; break
77+ }
78+ result.append(regex[i++ ])
79+ }
80+ }
81+ else -> {
82+ // regular escape, copy both
83+ result.append(c); result.append(regex[i+ 1 ])
84+ i + = 2
85+ }
86+ }
87+ }
88+
89+ // opening paren: check for flag group or scope
90+ c == ' (' && i+ 1 < regex.length && regex[i+ 1 ] == ' ?' -> {
91+ // scan forward to find the flag content
92+ val flagStart = i + 2
93+ var j = flagStart
94+ // lookahead to end of group/scope/other, set j to that position
95+ while (j < regex.length && regex[j] != ' :' && regex[j] != ' )' && regex[j] != ' (' ) j++
96+
97+ // check if regex[i..j] forms valid flag scope/group
98+ if (j < regex.length && (regex[j] == ' :' || regex[j] == ' )' ) && j > i+ 2
99+ && regex.substring(i+ 2 , j).all{ it in RegexFlags .validFlagCharacters || it == ' -' }) {
100+ // valid flag group/scope
101+ if (regex[j] == ' :' ) {
102+ // flag group (?flags:...): parse flags and push scope
103+ val flagToken = regex.substring(i, j+ 1 ) // e.g. "(?iu:"
104+ val newFlags = currentFlags.merge(ParsedFlagExpression .fromFlagToken(flagToken))
105+ scopeStack.addLast(currentFlags)
106+ currentFlags = newFlags
107+ result.append(regex.substring(i, j+ 1 ))
108+ i = j + 1
109+ } else {
110+ // flag scope (?flags), update currentFlags
111+ val flagToken = regex.substring(i, j+ 1 ) // e.g. "(?iu)"
112+ currentFlags = currentFlags.merge(ParsedFlagExpression .fromFlagToken(flagToken))
113+ result.append(regex.substring(i, j+ 1 ))
114+ i = j + 1
115+ }
116+ } else {
117+ // not a flag group/scope: push current flags unchanged
118+ scopeStack.addLast(currentFlags)
119+ result.append(c); i++
120+ }
121+ }
122+
123+ c == ' (' -> {
124+ scopeStack.addLast(currentFlags)
125+ result.append(c); i++
126+ }
127+
128+ c == ' )' -> {
129+ currentFlags = scopeStack.removeLastOrNull() ? : externalRegexFlags
130+ result.append(c); i++
131+ }
132+
133+ // comment
134+ c == ' #' && currentFlags.comments -> {
135+ i++
136+ while (i < regex.length && ! currentFlags.isLineTerminator(regex[i])) i++
137+ // consume line terminator
138+ if (i < regex.length) {
139+ // \r\n is a 2-character line terminator
140+ if (regex[i] == ' \r ' && i+ 1 < regex.length && regex[i+ 1 ] == ' \n ' ) i + = 2
141+ else i++
142+ }
143+ }
144+
145+ // whitespace, skip when comments flag is on
146+ c.isWhitespace() && currentFlags.comments -> i++
147+
148+ // else copy
149+ else -> { result.append(c); i++ }
150+ }
151+ }
152+ return result.toString()
153+ }
154+
51155 /* *
52156 * Given a ECMA262 regex string, generate RegexGene for it.
53157 * Based on RegexEcma262.g4 file.
0 commit comments